// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "HAL/ThreadSingleton.h" #include "UObject/UObjectGlobals.h" class UDynamicClass; struct FCompilerNativizationOptions; class ITargetPlatform; /** * List of asset registry tags used by blueprints. These are here so they can be used by both the asset registry and blueprint code * These need to be kept in sync with UBlueprint::GetAssetRegistryTags, and any changes there will require resaving content */ struct COREUOBJECT_API FBlueprintTags { /** Full path in export form ClassType'/PackagePath/PackageName.ClassName' of generated blueprint class */ static const FName GeneratedClassPath; /** Full path in export form ClassType'/PackagePath/PackageName.ClassName' of the immediate parent, may be a blueprint or native class */ static const FName ParentClassPath; /** Full path in export form Class'/Script/ModuleName.ClassName' of the first found parent native class */ static const FName NativeParentClassPath; /** Integer representing bitfield EClassFlags */ static const FName ClassFlags; /** String representing enum EBlueprintType */ static const FName BlueprintType; /** String with user-entered description of blueprint */ static const FName BlueprintDescription; /** String with user-entered display name for the blueprint class (used in editor along with the description to identify the Blueprint type) */ static const FName BlueprintDisplayName; /** String set to True/False, set if this is a data only blueprint */ static const FName IsDataOnly; /** List of implemented interfaces, must be converted to FBPInterfaceDescription */ static const FName ImplementedInterfaces; /** Very large string used to store find in blueprint data for the editor */ static const FName FindInBlueprintsData; /** (Deprecated) Legacy tag that was initially used to store find in blueprint data for the editor */ static const FName UnversionedFindInBlueprintsData; /** Number of replicated properties */ static const FName NumReplicatedProperties; /** Number of native components */ static const FName NumNativeComponents; /** Number of blueprint components */ static const FName NumBlueprintComponents; /** The subpath of a blueprint contained within the asset. Used to determine whether, and where a blueprint exists in a package. */ static const FName BlueprintPathWithinPackage; }; struct FBlueprintWarningDeclaration { FBlueprintWarningDeclaration(FName InWarningIdentifier, FText InWarningDescription) : WarningIdentifier(InWarningIdentifier), WarningDescription(InWarningDescription) { } FName WarningIdentifier; FText WarningDescription; }; typedef void (*FFlushReinstancingQueueFPtr)(); typedef void (*FClassReparentingFPtr)(const TMap&); /** * This set of functions contains blueprint related UObject functionality. */ struct FBlueprintSupport { /** * Defined in BlueprintSupport.cpp * Duplicates all fields of a struct in depth-first order. It makes sure that everything contained * in a class is duplicated before the struct itself, as well as all function parameters before the * function itself. * * @param StructToDuplicate Instance of the struct that is about to be duplicated * @param Writer duplicate writer instance to write the duplicated data to */ static void DuplicateAllFields(class UStruct* StructToDuplicate, class FDuplicateDataWriter& Writer); /** * A series of query functions that we can use to easily gate-off/disable * aspects of the deferred loading (mostly for testing purposes). */ static bool UseDeferredDependencyLoading(); static bool IsDeferredExportCreationDisabled(); static bool IsDeferredCDOInitializationDisabled(); /** Checks for any old instances and reinstances them: */ static void FlushReinstancingQueue(); COREUOBJECT_API static void SetFlushReinstancingQueueFPtr(FFlushReinstancingQueueFPtr Ptr); COREUOBJECT_API static void ReparentHierarchies(const TMap& OldClassToNewClass); COREUOBJECT_API static void SetClassReparentingFPtr(FClassReparentingFPtr Ptr); /** Tells if the specified object is one of the many flavors of FLinkerPlaceholderBase that we have. */ COREUOBJECT_API static bool IsDeferredDependencyPlaceholder(UObject* LoadedObj); /** Registers any object properties in this struct with the deferred dependency system */ COREUOBJECT_API static void RegisterDeferredDependenciesInStruct(const UStruct* Struct, void* StructData); /** Not a particularly fast function. Mostly intended for validation in debug builds. */ static bool IsInBlueprintPackage(UObject* LoadedObj); COREUOBJECT_API static void RegisterBlueprintWarning(const FBlueprintWarningDeclaration& Warning); COREUOBJECT_API static const TArray& GetBlueprintWarnings(); COREUOBJECT_API static void UpdateWarningBehavior(const TArray& WarningIdentifiersToTreatAsError, const TArray& WarningIdentifiersToSuppress); COREUOBJECT_API static bool ShouldTreatWarningAsError(FName WarningIdentifier); COREUOBJECT_API static bool ShouldSuppressWarning(FName WarningIdentifier); COREUOBJECT_API static bool IsClassPlaceholder(UClass* Class); #if WITH_EDITOR /** Function that walks the object graph, ensuring that there are no references to TRASH or REINST classes: */ COREUOBJECT_API static void ValidateNoRefsToOutOfDateClasses(); /** Function that walks the object graph, ensuring that there are no references to SKEL classes: */ COREUOBJECT_API static void ValidateNoExternalRefsToSkeletons(); #endif }; /** * When dealing with user defined structs we don't always have a UObject container * this registers raw addresses for tracking. This is somewhat less safe, make * sure to not register addresses that may change */ struct COREUOBJECT_API FScopedPlaceholderRawContainerTracker { public: FScopedPlaceholderRawContainerTracker(void* InData); ~FScopedPlaceholderRawContainerTracker(); private: void* Data; }; #if WITH_EDITOR /** * This is a helper struct that allows us to gather all previously unloaded class dependencies of a UClass * The first time we create a new UClass object in FLinkerLoad::CreateExport(), we register it as a dependency * master. Any subsequent UClasses that are created for the first time during the preload of that class are * added to the list as potential cyclic referencers. We then step over the list at the end of the load, and * recompile any classes that may depend on each other a second time to ensure that that functions and properties * are properly resolved */ struct COREUOBJECT_API FScopedClassDependencyGather { public: FScopedClassDependencyGather(UClass* ClassToGather, FUObjectSerializeContext* InLoadContext); ~FScopedClassDependencyGather(); /** * Post load, some systems would like an easy list of dependencies. This will * retrieve that latest BatchClassDependencies (filled with dependencies from * the last loaded class). * * @return The most recent array of tracked dependencies. */ static TArray const& GetCachedDependencies(); private: /** Whether or not this dependency gather is the dependency master, and thus should process all dependencies in the destructor */ bool bMasterClass; /** The current class that is gathering potential dependencies in this scope */ static UClass* BatchMasterClass; /** List of dependencies (i.e. UClasses that have been newly instantiated) in the scope of this dependency gather */ static TArray BatchClassDependencies; /** Current load context */ FUObjectSerializeContext* LoadContext; FScopedClassDependencyGather(); }; enum class EReplacementResult { /** Don't replace the provided package at all */ DontReplace, /** Generate a stub file, but don't replace the package */ GenerateStub, /** Completely replace the file with generated code */ ReplaceCompletely }; /** * Interface needed by CoreUObject to the BlueprintNativeCodeGen logic. Used by cooker to convert assets * to native code. */ struct IBlueprintNativeCodeGenCore { /** Returns the current IBlueprintNativeCodeGenCore, may return nullptr */ COREUOBJECT_API static const IBlueprintNativeCodeGenCore* Get(); /** * Registers the IBlueprintNativeCodeGenCore, just used to point us at an implementation. * By default, there is no IBlueprintNativeCodeGenCore, and thus no blueprints are * replaced at cook. */ COREUOBJECT_API static void Register(const IBlueprintNativeCodeGenCore* Coordinator); /** * Determines whether the provided package needs to be replaced (in part or completely) * * @param Package The package in question * @return Whether the package should be converted */ virtual EReplacementResult IsTargetedForReplacement(const UPackage* Package, const FCompilerNativizationOptions& NativizationOptions) const = 0; /** * Determines whether the provided object needs to be replaced (in part or completely). * Some objects in a package may require conversion and some may not. If any object * in a package wants to be converted then it is implied that all other objects will * be converted with it (no support for partial package conversion, beyond stubs) * * @param Object The package in question * @return Whether the object should be converted */ virtual EReplacementResult IsTargetedForReplacement(const UObject* Object, const FCompilerNativizationOptions& NativizationOptions) const = 0; /** * Function used to change the type of a class from, say, UBlueprintGeneratedClass to * UDynamicClass. Cooking (and conversion in general) must be order independent so * The scope of this kind of type swap is limited. * * @param Object whose class will be replaced * @return A replacement class ptr, null if none */ virtual UClass* FindReplacedClassForObject(const UObject* Object, const FCompilerNativizationOptions& NativizationOptions) const = 0; /** * Function used to change the path of subobject from a nativized class. * * @param Object Imported Object. * @param OutName Referenced to name, that will be saved in import table. * @return An Outer object that should be saved in import table. */ virtual UObject* FindReplacedNameAndOuter(UObject* Object, FName& OutName, const FCompilerNativizationOptions& NativizationOptions) const = 0; /* * Return nativization options for given platform. */ virtual const FCompilerNativizationOptions& GetNativizationOptionsForPlatform(const ITargetPlatform* Platform) const = 0; }; #endif // WITH_EDITOR /** * A base struct for storing FObjectInitializers that were not run on * Blueprint objects post-construction (presumably because the object's super/archetype * had not been fully serialized yet). * * This was designed to hold onto FObjectInitializers until a later point, when * they can properly be ran (after the archetype has been serialized). */ struct FDeferredInitializationTrackerBase { public: virtual ~FDeferredInitializationTrackerBase() {} /** * Makes a copy of the specified initializer and stores it (mapped under its * dependency), so that it can instead be executed later via ResolveArchetypeInstances(). * * @param InitDependecy The object (usually the initializer's archetype) that this initializer is dependent on. The key * you'll pass to ResolveArchetypeInstances() later to run this initializer. * @param DeferringInitializer The initializer you want to defer. * * @raturn A copy of the specified initializer. This should always succeed (only returns null if InitDependecy is null). */ FObjectInitializer* Add(const UObject* InitDependecy, const FObjectInitializer& DeferringInitializer); /** * Runs all deferred initializers that were dependent on the specified archetype * (unless they're dependent on another), and runs Preload() on any objects that * had their Preload() skipped as a result. * * @param ArchetypeKey The initializer dependency that has been fully loaded/serialized. */ void ResolveArchetypeInstances(UObject* ArchetypeKey); /** * Checks to see if the specified object has had its initialization deferred (meaning a super/archetype * hasn't had FObjectInitializer::PostConstructInit() ran on it yet). */ bool IsInitializationDeferred(const UObject* Object) const; /** * Determines if the specified object needs to have its Preload() call deferred * (this is meant to be called from Preload() itself). If so, this will record the object * and serialize it in later, once it's initializer dependency has been resolved * (from ResolveArchetypeInstances). * * This should be the case for any object that's had its initialization deferred (need * to initialize before you serialize), and any dependencies (sub-objects, etc.) waiting * on that object's initialization. * * @return True if Object's load/serialization should be skipped (for now). */ virtual bool DeferPreload(UObject* Object); protected: /** * Used to keep DeferPreload() from re-adding objects while we're in the midst of resolving. */ bool IsResolving(UObject* ArchetypeInstance) const; /** * Runs the deferred initializer for the specified archetype object if its not dependent * on other archetypes (like a sub-object that first requires the super's CDO to be constructed, * and then for its archetype to be serialized). * * If the initializer needs to be further deferred, this should re-register under its new dependency. * * @param ResolvingObject The dependency that has been fully loaded/serialized, that the object's initializer was logged under. * @param ArchetypeInstance The object which has had its initializer deferred. * * @return True if the initializer was ran, other wise false. */ virtual bool ResolveDeferredInitialization(UObject* ResolvingObject, UObject* ArchetypeInstance); /** * Runs through all objects that had their Preload() skipped due to an initializer * being deferred. Runs serialization on each object. */ void PreloadDeferredDependents(UObject* ArchetypeInstance); protected: #if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS friend struct FDeferredObjInitializationHelper; #endif /** Tracks objects that have had their initialization deferred as a result of their archetype not being fully serialized (maps archetype => list-o-instances) */ TMultiMap ArchetypeInstanceMap; /** A map that lets us look up FObjectInitializers by their UObject */ TMap DeferredInitializers; /** Used to keep ResolveArchetypeInstances() from re-adding sub-objects via DeferPreload() */ TArray ResolvingObjects; /** Track default objects that had their Preload() skipped, because a archetype dependency should initialize first */ TMultiMap DeferredPreloads; }; /** * Specialized FDeferredInitializationTracker for tracking deferred Blueprint CDOs specifically. * (every object in DeferredInitializers should be a BP CDO). */ struct FDeferredCdoInitializationTracker: FDeferredInitializationTrackerBase , TThreadSingleton { public: //~ FDeferredInitializationTrackerBase interface virtual bool DeferPreload(UObject* Object) override; }; /** * Specialized FDeferredInitializationTracker for tracking deferred Blueprint sub-objects specifically. * (every object in DeferredInitializers should be a default sub-object or component template). */ struct FDeferredSubObjInitializationTracker: FDeferredInitializationTrackerBase , TThreadSingleton { protected: //~ FDeferredInitializationTrackerBase interface virtual bool ResolveDeferredInitialization(UObject* ResolvingObject, UObject* ArchetypeInstance) override; }; /** * Access points for making FDeferredInitializationTracker calls. Takes care of * routing calls to the right tracker (CDOs vs sub-objects), and wraps the TThreadSingleton access for each. */ struct FDeferredObjInitializationHelper { /** * Determines if the specified initializer needs to be deferred (does it have a * archetype dependency that needs to be serialized first?). If so, the FObjectInitializer will * be copied and stored with the appropriate tracker (CDO vs. sub-object, etc.). * * Designed to be called from the FObjectInitializer itself (before it runs initialization). * * @return A pointer to the initializer copy (if one was made), null if no deferral was needed. */ static FObjectInitializer* DeferObjectInitializerIfNeeded(const FObjectInitializer& DeferringInitializer); /** * Determines if the specified object should have its Preload() skipped. If so, this * should cache the sub-object so it can be loaded later, when its dependency is resolved. * * Designed to be called from the Preload() itself (before it runs serializes the object). * * More info: Because of delta serialization, we require that a parent's CDO be * fully serialized before its children's CDOs are created. However, * due to cyclic parent/child dependencies, we have some cases where * the linker breaks that expected behavior. In those cases, we * defer the child's initialization (i.e. defer copying of parent * property values, etc.), and wait until we can guarantee that the * parent CDO has been fully loaded. * * In a normal scenario, the order of property initialization is: * Creation (zeroed) -> Initialization (copied super's values) -> Serialization (overridden values loaded) * When the initialization has been deferred we have to make sure to * defer serialization here as well (don't worry, it will be invoked * again from FinalizeBlueprint()->ResolveDeferredExports()) * * also, if this is an inherited sub-object on a CDO, and that CDO has had * its initialization deferred (for reasons explained above), then * we shouldn't serialize in data for this quite yet... not until * its owner has had a chaTnce to initialize itself (because, as part * of CDO initialization, inherited sub-objects get filled in with * values inherited from the super) * * @return True if the object's Preload() should be skipped. */ static bool DeferObjectPreload(UObject* Object); /** * Loops through all object initializers and preloads that were skipped due * to this archetype object not being ready yet. * * Should be called once the object has been fully serialized in. */ static void ResolveDeferredInitsFromArchetype(UObject* Archetype); }; struct FBlueprintDependencyType { uint8 bSerializationBeforeSerializationDependency : 1; uint8 bCreateBeforeSerializationDependency : 1; uint8 bSerializationBeforeCreateDependency : 1; uint8 bCreateBeforeCreateDependency : 1; FBlueprintDependencyType() : bSerializationBeforeSerializationDependency(0), bCreateBeforeSerializationDependency(0), bSerializationBeforeCreateDependency(0), bCreateBeforeCreateDependency(0) {} FBlueprintDependencyType(bool bInSerializationBeforeSerializationDependency, bool bInCreateBeforeSerializationDependency, bool bInSerializationBeforeCreateDependency, bool bInCreateBeforeCreateDependency) : bSerializationBeforeSerializationDependency(bInSerializationBeforeSerializationDependency), bCreateBeforeSerializationDependency(bInCreateBeforeSerializationDependency), bSerializationBeforeCreateDependency(bInSerializationBeforeCreateDependency), bCreateBeforeCreateDependency(bInCreateBeforeCreateDependency) {} }; struct COREUOBJECT_API FCompactBlueprintDependencyData { int16 ObjectRefIndex; FBlueprintDependencyType StructDependency; FBlueprintDependencyType CDODependency; FCompactBlueprintDependencyData() : ObjectRefIndex(-1) {} FCompactBlueprintDependencyData(int16 InObjectRefIndex, FBlueprintDependencyType InStructDependency, FBlueprintDependencyType InCDODependency = FBlueprintDependencyType()) : ObjectRefIndex(InObjectRefIndex), StructDependency(InStructDependency), CDODependency(InCDODependency) {} }; struct COREUOBJECT_API FBlueprintDependencyObjectRef { FName PackageName; FName ObjectName; FName ClassPackageName; FName ClassName; FName OuterName; FBlueprintDependencyObjectRef() {} FORCENOINLINE FBlueprintDependencyObjectRef(const TCHAR* InPackageFolder, const TCHAR* InShortPackageName, const TCHAR* InObjectName, const TCHAR* InClassPackageName, const TCHAR* InClassName, const TCHAR* InOuterName); }; struct COREUOBJECT_API FBlueprintDependencyData { FBlueprintDependencyObjectRef ObjectRef; // 0 - dependency type for dynamic class or UDS // 1 - dependency type for CD0 FBlueprintDependencyType DependencyTypes[2]; int16 ObjectRefIndex; // NativizationWithoutEDLBT FBlueprintDependencyData(const FBlueprintDependencyObjectRef& InObjectRef, const FCompactBlueprintDependencyData& InCompactDependencyData) : ObjectRef(InObjectRef), ObjectRefIndex(InCompactDependencyData.ObjectRefIndex) { DependencyTypes[0] = InCompactDependencyData.StructDependency; DependencyTypes[1] = InCompactDependencyData.CDODependency; } bool operator==(const FBlueprintDependencyData& Other) const { return Other.ObjectRefIndex == ObjectRefIndex; } static bool ContainsDependencyData(TArray& Assets, int16 ObjectRefIndex); static void AppendUniquely(TArray& Destination, const TArray& AdditionalData); }; /** * Stores info about dependencies of native classes converted from BPs */ struct COREUOBJECT_API FConvertedBlueprintsDependencies { typedef void (*GetDependenciesNamesFunc)(TArray&); private: TMap PackageNameToGetter; public: static FConvertedBlueprintsDependencies& Get(); void RegisterConvertedClass(FName PackageName, GetDependenciesNamesFunc GetAssets); /** Get all assets paths necessary for the class with the given class name and all converted classes that dependencies. */ void GetAssets(FName PackageName, TArray& OutDependencies) const; static void FillUsedAssetsInDynamicClass(UDynamicClass* DynamicClass, GetDependenciesNamesFunc GetUsedAssets); static UObject* LoadObjectForStructConstructor(UScriptStruct* ScriptStruct, const TCHAR* ObjectPath); };