// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= UObjectBase.h: Base class for UObject, defines low level functionality =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "Stats/Stats.h" #include "UObject/ObjectMacros.h" #include "UObject/UObjectGlobals.h" #include "HAL/LowLevelMemTracker.h" DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("STAT_UObjectsStatGroupTester"), STAT_UObjectsStatGroupTester, STATGROUP_UObjects, COREUOBJECT_API); /** * Low level implementation of UObject, should not be used directly in game code */ class COREUOBJECT_API UObjectBase { friend class UObjectBaseUtility; friend struct Z_Construct_UClass_UObject_Statics; friend class FUObjectArray; // for access to InternalIndex without revealing it to anyone else friend class FUObjectAllocator; // for access to destructor without revealing it to anyone else friend COREUOBJECT_API void UObjectForceRegistration(UObjectBase* Object, bool bCheckForModuleRelease); friend COREUOBJECT_API void InitializePrivateStaticClass( class UClass* TClass_Super_StaticClass, class UClass* TClass_PrivateStaticClass, class UClass* TClass_WithinClass_StaticClass, const TCHAR* PackageName, const TCHAR* Name); protected: UObjectBase(): NamePrivate(NoInit) // screwy, but the name was already set and we don't want to set it again { } /** * Constructor used for bootstrapping * @param InFlags RF_Flags to assign */ UObjectBase(EObjectFlags InFlags); public: /** * Constructor used by StaticAllocateObject * @param InClass non NULL, this gives the class of the new object, if known at this time * @param InFlags RF_Flags to assign * @param InInternalFlags EInternalObjectFlags to assign * @param InOuter outer for this object * @param InName name of the new object */ UObjectBase(UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject* InOuter, FName InName); /** * Final destructor, removes the object from the object array, and indirectly, from any annotations **/ virtual ~UObjectBase(); /** * Emit GC tokens for UObjectBase, this might be UObject::StaticClass or Default__Class **/ static void EmitBaseReferences(UClass* RootClass); protected: /** * Just change the FName and Outer and rehash into name hash tables. For use by higher level rename functions. * * @param NewName new name for this object * @param NewOuter new outer for this object, if NULL, outer will be unchanged */ void LowLevelRename(FName NewName, UObject* NewOuter = NULL); /** Force any base classes to be registered first */ virtual void RegisterDependencies() {} /** Enqueue the registration for this object. */ void Register(const TCHAR* PackageName, const TCHAR* Name); /** * Convert a boot-strap registered class into a real one, add to uobject array, etc * * @param UClassStaticClass Now that it is known, fill in UClass::StaticClass() as the class */ virtual void DeferredRegister(UClass* UClassStaticClass, const TCHAR* PackageName, const TCHAR* Name); private: /** * Add a newly created object to the name hash tables and the object array * * @param Name name to assign to this uobject * @param InSetInternalFlags Internal object flags to be set on the object once it's been added to the array */ void AddObject(FName Name, EInternalObjectFlags InSetInternalFlags); public: /** * Checks to see if the object appears to be valid * @return true if this appears to be a valid object */ bool IsValidLowLevel() const; /** * Faster version of IsValidLowLevel. * Checks to see if the object appears to be valid by checking pointers and their alignment. * Name and InternalIndex checks are less accurate than IsValidLowLevel. * @param bRecursive true if the Class pointer should be checked with IsValidLowLevelFast * @return true if this appears to be a valid object */ bool IsValidLowLevelFast(bool bRecursive = true) const; /** * Returns the unique ID of the object...these are reused so it is only unique while the object is alive. * Useful as a tag. **/ FORCEINLINE uint32 GetUniqueID() const { return (uint32)InternalIndex; } /** Returns the UClass that defines the fields of this object */ FORCEINLINE UClass* GetClass() const { return ClassPrivate; } /** Returns the UObject this object resides in */ FORCEINLINE UObject* GetOuter() const { return OuterPrivate; } /** Returns the logical name of this object */ FORCEINLINE FName GetFName() const { return NamePrivate; } /** Removes the class prefix from the given string */ static FString RemoveClassPrefix(const TCHAR* ClassName); /** Returns the external UPackage associated with this object, if any */ UPackage* GetExternalPackage() const; /** Associate an external package directly to this object. */ void SetExternalPackage(UPackage* InPackage); /** Returns the external UPackage for this object, if any, NOT THREAD SAFE, used by internal gc reference collecting. */ UPackage* GetExternalPackageInternal() const; protected: /** * Set the object flags directly * **/ FORCEINLINE void SetFlagsTo(EObjectFlags NewFlags) { checkfSlow((NewFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as 0x%x but is trying to set flags to RF_AllFlags"), *GetFName().ToString(), (int)ObjectFlags); ObjectFlags = NewFlags; } public: /** * Retrieve the object flags directly * * @return Flags for this object **/ FORCEINLINE EObjectFlags GetFlags() const { checkfSlow((ObjectFlags & ~RF_AllFlags) == 0, TEXT("%s flagged as RF_AllFlags"), *GetFName().ToString()); return ObjectFlags; } /** * Atomically adds the specified flags. * Do not use unless you know what you are doing. * Designed to be used only by parallel GC and UObject loading thread. */ FORCENOINLINE void AtomicallySetFlags(EObjectFlags FlagsToAdd) { int32 OldFlags = 0; int32 NewFlags = 0; do { OldFlags = ObjectFlags; NewFlags = OldFlags | FlagsToAdd; } while (FPlatformAtomics::InterlockedCompareExchange((int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags); } /** * Atomically clears the specified flags. * Do not use unless you know what you are doing. * Designed to be used only by parallel GC and UObject loading thread. */ FORCENOINLINE void AtomicallyClearFlags(EObjectFlags FlagsToClear) { int32 OldFlags = 0; int32 NewFlags = 0; do { OldFlags = ObjectFlags; NewFlags = OldFlags & ~FlagsToClear; } while (FPlatformAtomics::InterlockedCompareExchange((int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags); } private: /** Flags used to track and report various object states. This needs to be 8 byte aligned on 32-bit platforms to reduce memory waste */ EObjectFlags ObjectFlags; /** Index into GObjectArray...very private. */ int32 InternalIndex; /** Class the object belongs to. */ UClass* ClassPrivate; /** Name of this object */ FName NamePrivate; /** Object this object resides in. */ UObject* OuterPrivate; friend class FBlueprintCompileReinstancer; friend class FContextObjectManager; /** This is used by the reinstancer to re-class and re-archetype the current instances of a class before recompiling */ void SetClass(UClass* NewClass); #if HACK_HEADER_GENERATOR // Required by UHT makefiles for internal data serialization. friend struct FObjectBaseArchiveProxy; #endif // HACK_HEADER_GENERATOR }; /** * Checks to see if the UObject subsystem is fully bootstrapped and ready to go. * If true, then all objects are registered and auto registration of natives is over, forever. * * @return true if the UObject subsystem is initialized. */ COREUOBJECT_API bool UObjectInitialized(); /** * Force a pending registrant to register now instead of in the natural order */ COREUOBJECT_API void UObjectForceRegistration(UObjectBase* Object, bool bCheckForModuleRelease = true); /** * Base class for deferred native class registration */ struct FFieldCompiledInInfo { FFieldCompiledInInfo(SIZE_T InClassSize, uint32 InCrc) : Size(InClassSize), Crc(InCrc), OldClass(nullptr), bHasChanged(false) { } /** Registers the native class (constructs a UClass object) */ virtual UClass* Register() const = 0; /** Return the package the class belongs in */ virtual const TCHAR* ClassPackage() const = 0; /** Size of the class */ SIZE_T Size; /** CRC of the generated code for this class */ uint32 Crc; /** Old UClass object */ UClass* OldClass; /** True if this class has changed after hot-reload (or new class) */ bool bHasChanged; }; /** * Adds a class to deferred registration queue. */ COREUOBJECT_API void UClassCompiledInDefer(FFieldCompiledInInfo* Class, const TCHAR* Name, SIZE_T ClassSize, uint32 Crc); /** * Specialized version of the deferred class registration structure. */ template struct TClassCompiledInDefer: public FFieldCompiledInInfo { TClassCompiledInDefer(const TCHAR* InName, SIZE_T InClassSize, uint32 InCrc) : FFieldCompiledInInfo(InClassSize, InCrc) { UClassCompiledInDefer(this, InName, InClassSize, InCrc); } virtual UClass* Register() const override { LLM_SCOPE(ELLMTag::UObject); return TClass::StaticClass(); } virtual const TCHAR* ClassPackage() const override { return TClass::StaticPackage(); } }; /** * Stashes the singleton function that builds a compiled in class. Later, this is executed. */ COREUOBJECT_API void UObjectCompiledInDefer(class UClass* (*InRegister)(), class UClass* (*InStaticClass)(), const TCHAR* Name, const TCHAR* PackageName, bool bDynamic, const TCHAR* DynamicPathName, void (*InInitSearchableValues)(TMap&)); struct FCompiledInDefer { FCompiledInDefer(class UClass* (*InRegister)(), class UClass* (*InStaticClass)(), const TCHAR* PackageName, const TCHAR* Name, bool bDynamic, const TCHAR* DynamicPackageName = nullptr, const TCHAR* DynamicPathName = nullptr, void (*InInitSearchableValues)(TMap&) = nullptr) { if (bDynamic) { GetConvertedDynamicPackageNameToTypeName().Add(FName(DynamicPackageName), FName(Name)); } UObjectCompiledInDefer(InRegister, InStaticClass, Name, PackageName, bDynamic, DynamicPathName, InInitSearchableValues); } }; /** * Stashes the singleton function that builds a compiled in struct (StaticStruct). Later, this is executed. */ COREUOBJECT_API void UObjectCompiledInDeferStruct(class UScriptStruct* (*InRegister)(), const TCHAR* PackageName, const TCHAR* ObjectName, bool bDynamic, const TCHAR* DynamicPathName); struct FCompiledInDeferStruct { FCompiledInDeferStruct(class UScriptStruct* (*InRegister)(), const TCHAR* PackageName, const TCHAR* Name, bool bDynamic, const TCHAR* DynamicPackageName, const TCHAR* DynamicPathName) { if (bDynamic) { GetConvertedDynamicPackageNameToTypeName().Add(FName(DynamicPackageName), FName(Name)); } UObjectCompiledInDeferStruct(InRegister, PackageName, Name, bDynamic, DynamicPathName); } }; /** * Either call the passed in singleton, or if this is hot reload, find the existing struct */ COREUOBJECT_API class UScriptStruct* GetStaticStruct(class UScriptStruct* (*InRegister)(), UObject* StructOuter, const TCHAR* StructName, SIZE_T Size, uint32 Crc); /** * Stashes the singleton function that builds a compiled in enum. Later, this is executed. */ COREUOBJECT_API void UObjectCompiledInDeferEnum(class UEnum* (*InRegister)(), const TCHAR* PackageName, const TCHAR* ObjectName, bool bDynamic, const TCHAR* DynamicPathName); struct FCompiledInDeferEnum { FCompiledInDeferEnum(class UEnum* (*InRegister)(), const TCHAR* PackageName, const TCHAR* Name, bool bDynamic, const TCHAR* DynamicPackageName, const TCHAR* DynamicPathName) { if (bDynamic) { GetConvertedDynamicPackageNameToTypeName().Add(FName(DynamicPackageName), FName(Name)); } UObjectCompiledInDeferEnum(InRegister, PackageName, Name, bDynamic, DynamicPathName); } }; /** * Either call the passed in singleton, or if this is hot reload, find the existing enum */ COREUOBJECT_API class UEnum* GetStaticEnum(class UEnum* (*InRegister)(), UObject* EnumOuter, const TCHAR* EnumName); /** Called during HotReload to hook up an existing structure */ COREUOBJECT_API class UScriptStruct* FindExistingStructIfHotReloadOrDynamic(UObject* Outer, const TCHAR* StructName, SIZE_T Size, uint32 Crc, bool bIsDynamic); /** Called during HotReload to hook up an existing enum */ COREUOBJECT_API class UEnum* FindExistingEnumIfHotReloadOrDynamic(UObject* Outer, const TCHAR* EnumName, SIZE_T Size, uint32 Crc, bool bIsDynamic); /** Must be called after a module has been loaded that contains UObject classes */ COREUOBJECT_API void ProcessNewlyLoadedUObjects(FName Package = NAME_None, bool bCanProcessNewlyLoadedObjects = true); #if WITH_HOT_RELOAD /** Map of duplicated CDOs for reinstancing during hot-reload purposes. */ COREUOBJECT_API TMap& GetDuplicatedCDOMap(); #endif // WITH_HOT_RELOAD /** * Final phase of UObject initialization. all auto register objects are added to the main data structures. */ void UObjectBaseInit(); /** * Final phase of UObject shutdown */ void UObjectBaseShutdown();