· 6 years ago · Jul 25, 2019, 05:24 PM
1// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
2
3#include "Blueprint/BlueprintSupport.h"
4#include "Misc/ScopeLock.h"
5#include "Misc/CoreMisc.h"
6#include "Misc/ConfigCacheIni.h"
7#include "UObject/UObjectHash.h"
8#include "UObject/Object.h"
9#include "UObject/GarbageCollection.h"
10#include "UObject/Class.h"
11#include "UObject/Package.h"
12#include "Templates/Casts.h"
13#include "UObject/UnrealType.h"
14#include "Serialization/DuplicatedDataWriter.h"
15#include "Misc/PackageName.h"
16#include "UObject/ObjectResource.h"
17#include "UObject/GCObject.h"
18#include "UObject/LinkerPlaceholderClass.h"
19#include "UObject/LinkerPlaceholderExportObject.h"
20#include "UObject/LinkerPlaceholderFunction.h"
21#include "UObject/ReferenceChainSearch.h"
22#include "UObject/StructScriptLoader.h"
23#include "UObject/UObjectThreadContext.h"
24
25#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
26#include "UObject/UObjectIterator.h"
27#endif
28
29DEFINE_LOG_CATEGORY_STATIC(LogBlueprintSupport, Log, All);
30
31// Flag to enable the BlueprintCompilationManager:
32COREUOBJECT_API bool GBlueprintUseCompilationManager = true;
33
34const FName FBlueprintTags::GeneratedClassPath(TEXT("GeneratedClass"));
35const FName FBlueprintTags::ParentClassPath(TEXT("ParentClass"));
36const FName FBlueprintTags::NativeParentClassPath(TEXT("NativeParentClass"));
37const FName FBlueprintTags::ClassFlags(TEXT("ClassFlags"));
38const FName FBlueprintTags::BlueprintType(TEXT("BlueprintType"));
39const FName FBlueprintTags::BlueprintDescription(TEXT("BlueprintDescription"));
40const FName FBlueprintTags::IsDataOnly(TEXT("IsDataOnly"));
41const FName FBlueprintTags::ImplementedInterfaces(TEXT("ImplementedInterfaces"));
42const FName FBlueprintTags::FindInBlueprintsData(TEXT("FiBData"));
43const FName FBlueprintTags::NumReplicatedProperties(TEXT("NumReplicatedProperties"));
44const FName FBlueprintTags::NumNativeComponents(TEXT("NativeComponents"));
45const FName FBlueprintTags::NumBlueprintComponents(TEXT("BlueprintComponents"));
46const FName FBlueprintTags::BlueprintPathWithinPackage(TEXT("BlueprintPath"));
47
48/**
49 * Defined in BlueprintSupport.cpp
50 * Duplicates all fields of a class in depth-first order. It makes sure that everything contained
51 * in a class is duplicated before the class itself, as well as all function parameters before the
52 * function itself.
53 *
54 * @param StructToDuplicate Instance of the struct that is about to be duplicated
55 * @param Writer duplicate writer instance to write the duplicated data to
56 */
57void FBlueprintSupport::DuplicateAllFields(UStruct* StructToDuplicate, FDuplicateDataWriter& Writer)
58{
59 // This is a very simple fake topological-sort to make sure everything contained in the class
60 // is processed before the class itself is, and each function parameter is processed before the function
61 if (StructToDuplicate)
62 {
63 // Make sure each field gets allocated into the array
64 for (TFieldIterator<UField> FieldIt(StructToDuplicate, EFieldIteratorFlags::ExcludeSuper); FieldIt; ++FieldIt)
65 {
66 UField* Field = *FieldIt;
67
68 // Make sure functions also do their parameters and children first
69 if (UFunction* Function = dynamic_cast<UFunction*>(Field))
70 {
71 for (TFieldIterator<UField> FunctionFieldIt(Function, EFieldIteratorFlags::ExcludeSuper); FunctionFieldIt; ++FunctionFieldIt)
72 {
73 UField* InnerField = *FunctionFieldIt;
74 Writer.GetDuplicatedObject(InnerField);
75 }
76 }
77
78 Writer.GetDuplicatedObject(Field);
79 }
80 }
81}
82
83bool FBlueprintSupport::UseDeferredDependencyLoading()
84{
85#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
86 static const FBoolConfigValueHelper DeferDependencyLoads(TEXT("Kismet"), TEXT("bDeferDependencyLoads"), GEngineIni);
87 bool bUseDeferredDependencyLoading = DeferDependencyLoads;
88
89 if (FPlatformProperties::RequiresCookedData())
90 {
91 static const FBoolConfigValueHelper DisableCookedBuildDefering(TEXT("Kismet"), TEXT("bForceDisableCookedDependencyDeferring"), GEngineIni);
92 bUseDeferredDependencyLoading &= !((bool)DisableCookedBuildDefering);
93 }
94 return bUseDeferredDependencyLoading;
95#else
96 return false;
97#endif
98}
99
100bool FBlueprintSupport::IsDeferredExportCreationDisabled()
101{
102#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
103 static const FBoolConfigValueHelper NoDeferredExports(TEXT("Kismet"), TEXT("bForceDisableDeferredExportCreation"), GEngineIni);
104 return !UseDeferredDependencyLoading() || NoDeferredExports;
105#else
106 return false;
107#endif
108}
109
110bool FBlueprintSupport::IsDeferredCDOInitializationDisabled()
111{
112#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
113 static const FBoolConfigValueHelper NoDeferredCDOInit(TEXT("Kismet"), TEXT("bForceDisableDeferredCDOInitialization"), GEngineIni);
114 return !UseDeferredDependencyLoading() || NoDeferredCDOInit;
115#else
116 return false;
117#endif
118}
119
120void FBlueprintSupport::InitializeCompilationManager()
121{
122 // 'real' initialization is done lazily because we're in a pretty tough
123 // spot in terms of dependencies:
124 bool bCompilationManagerDisabled = false;
125 bool bCompilationManagerEnabled = true;
126 GConfig->GetBool(TEXT("/Script/UnrealEd.BlueprintEditorProjectSettings"), TEXT("bUseCompilationManager"), bCompilationManagerEnabled, GEditorIni);
127 GConfig->GetBool(TEXT("/Script/UnrealEd.BlueprintEditorProjectSettings"), TEXT("bDisableCompilationManager"), bCompilationManagerDisabled, GEditorIni);
128 if (bCompilationManagerDisabled)
129 {
130 GBlueprintUseCompilationManager = false;
131 }
132 else if(!bCompilationManagerEnabled)
133 {
134 UE_LOG(LogScript, Warning,
135 TEXT("Warning: Compilation manager enabled, compilation manager will be mandatory in 4.21. \
136 Use bDisableCompilationManager to disable the compilation manager as a last resort or remove \
137 bUseCompilationManager from project .ini to fix this warning.")
138 );
139 }
140}
141
142static FFlushReinstancingQueueFPtr FlushReinstancingQueueFPtr = nullptr;
143
144void FBlueprintSupport::FlushReinstancingQueue()
145{
146 if(FlushReinstancingQueueFPtr)
147 {
148 (*FlushReinstancingQueueFPtr)();
149 }
150}
151
152void FBlueprintSupport::SetFlushReinstancingQueueFPtr(FFlushReinstancingQueueFPtr Ptr)
153{
154 FlushReinstancingQueueFPtr = Ptr;
155}
156
157bool FBlueprintSupport::IsDeferredDependencyPlaceholder(UObject* LoadedObj)
158{
159 return LoadedObj && ( LoadedObj->IsA<ULinkerPlaceholderClass>() ||
160 LoadedObj->IsA<ULinkerPlaceholderFunction>() ||
161 LoadedObj->IsA<ULinkerPlaceholderExportObject>() );
162}
163
164void FBlueprintSupport::RegisterDeferredDependenciesInStruct(const UStruct* Struct, void* StructData)
165{
166#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
167 if (GEventDrivenLoaderEnabled)
168 {
169 return;
170 }
171
172 for (TPropertyValueIterator<const UObjectProperty> It(Struct, StructData); It; ++It)
173 {
174 const UObjectProperty* Property = It.Key();
175 void* PropertyValue = (void*)It.Value();
176 UObject* ObjectValue = *((UObject**)PropertyValue);
177
178 ULinkerPlaceholderExportObject* PlaceholderVal = Cast<ULinkerPlaceholderExportObject>(ObjectValue);
179 ULinkerPlaceholderClass* PlaceholderClass = Cast<ULinkerPlaceholderClass>(ObjectValue);
180
181 if (PlaceholderVal == nullptr && PlaceholderClass == nullptr)
182 {
183 continue;
184 }
185
186 // Create a stack of property trackers to deal with any outer Struct Properties
187 TArray<const UProperty*> PropertyChain;
188 It.GetPropertyChain(PropertyChain);
189 TIndirectArray<FScopedPlaceholderPropertyTracker> PlaceholderStack;
190
191 // Iterate property chain in reverse order as we need to start with parent
192 for (int32 PropertyIndex = PropertyChain.Num() - 1; PropertyIndex >= 0; PropertyIndex--)
193 {
194 if (const UStructProperty* StructProperty = Cast<UStructProperty>(PropertyChain[PropertyIndex]))
195 {
196 PlaceholderStack.Add(new FScopedPlaceholderPropertyTracker(StructProperty));
197 }
198 }
199
200 if (PlaceholderVal)
201 {
202 PlaceholderVal->AddReferencingPropertyValue(Property, PropertyValue);
203 }
204 else
205 {
206 PlaceholderClass->AddReferencingPropertyValue(Property, PropertyValue);
207 }
208
209 // Specifically destroy entries in reverse order they were added, to simulate unrolling a code stack
210 for (int32 StackIndex = PlaceholderStack.Num() - 1; StackIndex >= 0; StackIndex--)
211 {
212 PlaceholderStack.RemoveAt(StackIndex);
213 }
214 }
215#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
216}
217
218bool FBlueprintSupport::IsInBlueprintPackage(UObject* LoadedObj)
219{
220 UPackage* Pkg = LoadedObj->GetOutermost();
221 if (Pkg && !Pkg->HasAnyPackageFlags(PKG_CompiledIn))
222 {
223 TArray<UObject*> PkgObjects;
224 GetObjectsWithOuter(Pkg, PkgObjects, /*bIncludeNestedObjects =*/false);
225
226 UObject* PkgCDO = nullptr;
227 UClass* PkgClass = nullptr;
228
229 for (UObject* PkgObj : PkgObjects)
230 {
231 if (PkgObj->HasAnyFlags(RF_ClassDefaultObject))
232 {
233 PkgCDO = PkgObj;
234 }
235 else if (UClass* AsClass = Cast<UClass>(PkgObj))
236 {
237 PkgClass = AsClass;
238 }
239 }
240 const bool bHasBlueprintClass = PkgClass && PkgClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint);
241
242 return bHasBlueprintClass
243 //&& (PkgCDO && PkgCDO->GetClass() == PkgClass)
244#if WITH_EDITORONLY_DATA
245 //&& (PkgClass->ClassGeneratedBy != nullptr) && (PkgClass->ClassGeneratedBy->GetOuter() == Pkg)
246#endif
247 ;
248 }
249 return false;
250}
251
252static TArray<FBlueprintWarningDeclaration> BlueprintWarnings;
253static TSet<FName> BlueprintWarningsToTreatAsError;
254static TSet<FName> BlueprintWarningsToSuppress;
255
256void FBlueprintSupport::RegisterBlueprintWarning(const FBlueprintWarningDeclaration& Warning)
257{
258 BlueprintWarnings.Add(Warning);
259}
260
261const TArray<FBlueprintWarningDeclaration>& FBlueprintSupport::GetBlueprintWarnings()
262{
263 return BlueprintWarnings;
264}
265
266void FBlueprintSupport::UpdateWarningBehavior(const TArray<FName>& WarningIdentifiersToTreatAsError, const TArray<FName>& WarningIdentifiersToSuppress)
267{
268 BlueprintWarningsToTreatAsError = TSet<FName>(WarningIdentifiersToTreatAsError);
269 BlueprintWarningsToSuppress = TSet<FName>(WarningIdentifiersToSuppress);
270}
271
272bool FBlueprintSupport::ShouldTreatWarningAsError(FName WarningIdentifier)
273{
274 return BlueprintWarningsToTreatAsError.Find(WarningIdentifier) != nullptr;
275}
276
277bool FBlueprintSupport::ShouldSuppressWarning(FName WarningIdentifier)
278{
279 return BlueprintWarningsToSuppress.Find(WarningIdentifier) != nullptr;
280}
281
282bool FBlueprintSupport::IsClassPlaceholder(UClass* Class)
283{
284 while (Class)
285 {
286 if (Cast<ULinkerPlaceholderClass>(Class))
287 {
288 return true;
289 }
290
291 Class = Class->GetSuperClass();
292 }
293
294 return false;
295}
296
297#if WITH_EDITOR
298void FBlueprintSupport::ValidateNoRefsToOutOfDateClasses()
299{
300 // ensure no TRASH/REINST types remain:
301 TArray<UObject*> OutOfDateClasses;
302 GetObjectsOfClass(UClass::StaticClass(), OutOfDateClasses);
303 OutOfDateClasses.RemoveAllSwap(
304 [](UObject* Obj)
305 {
306 UClass* AsClass = CastChecked<UClass>(Obj);
307 return (!AsClass->HasAnyClassFlags(CLASS_NewerVersionExists)) || !AsClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint);
308 }
309 );
310
311 for(UObject* Obj : OutOfDateClasses)
312 {
313 FReferenceChainSearch RefChainSearch(Obj, EReferenceChainSearchMode::Shortest);
314 if( RefChainSearch.GetReferenceChains().Num() != 0 )
315 {
316 RefChainSearch.PrintResults();
317 ensureAlwaysMsgf(false, TEXT("Found and output bad class references"));
318 }
319 }
320}
321
322void FBlueprintSupport::ValidateNoExternalRefsToSkeletons()
323{
324 // bit of a hack to find the skel class, because UBlueprint is not visible here,
325 // but it's very useful to be able to validate BP assumptions in low level code:
326 auto IsSkeleton = [](UClass* InClass)
327 {
328 return InClass->ClassGeneratedBy && InClass->GetName().StartsWith(TEXT("SKEL_"));
329 };
330
331 auto IsOuteredToSkeleton = [IsSkeleton](UObject* Object)
332 {
333 UObject* Iter = Object->GetOuter();
334 while(Iter)
335 {
336 if(UClass* AsClass = Cast<UClass>(Iter))
337 {
338 if(IsSkeleton(AsClass))
339 {
340 return true;
341 }
342 }
343 Iter = Iter->GetOuter();
344 }
345 return false;
346 };
347
348 TArray<UObject*> SkeletonClasses;
349 GetObjectsOfClass(UClass::StaticClass(), SkeletonClasses);
350 SkeletonClasses.RemoveAllSwap(
351 [IsSkeleton](UObject* Obj)
352 {
353 UClass* AsClass = CastChecked<UClass>(Obj);
354 return !IsSkeleton(AsClass);
355 }
356 );
357
358 for(UObject* SkeletonClass : SkeletonClasses)
359 {
360 FReferenceChainSearch RefChainSearch(SkeletonClass, EReferenceChainSearchMode::Shortest | EReferenceChainSearchMode::ExternalOnly);
361 bool bBadRefs = false;
362 for(const FReferenceChainSearch::FReferenceChain* Chain : RefChainSearch.GetReferenceChains())
363 {
364 if(Chain->GetRootNode()->Object->GetOutermost() != SkeletonClass->GetOutermost())
365 {
366 bBadRefs = true;
367 for (int32 NodeIndex = 1; bBadRefs && NodeIndex < Chain->Num(); ++NodeIndex)
368 {
369 // if there's a skeleton class (or an object outered to a skeleton class) somewhere in the chain, then it's fine:
370 UObject* ObjectReferencingSkeletonClass = Chain->GetNode(NodeIndex)->Object;
371 if (UClass* AsClass = Cast<UClass>(ObjectReferencingSkeletonClass))
372 {
373 if (IsSkeleton(AsClass))
374 {
375 bBadRefs = false;
376 }
377 }
378 else if (IsOuteredToSkeleton(ObjectReferencingSkeletonClass))
379 {
380 bBadRefs = false;
381 }
382 }
383 }
384 }
385
386 if(bBadRefs)
387 {
388 RefChainSearch.PrintResults();
389 ensureAlwaysMsgf(false, TEXT("Found and output bad references to skeleton classes"));
390 }
391 }
392}
393#endif // WITH_EDITOR
394
395/*******************************************************************************
396 * FScopedClassDependencyGather
397 ******************************************************************************/
398
399#if WITH_EDITOR
400UClass* FScopedClassDependencyGather::BatchMasterClass = NULL;
401TArray<UClass*> FScopedClassDependencyGather::BatchClassDependencies;
402
403FScopedClassDependencyGather::FScopedClassDependencyGather(UClass* ClassToGather)
404 : bMasterClass(false)
405{
406 // Do NOT track duplication dependencies, as these are intermediate products that we don't care about
407 if( !GIsDuplicatingClassForReinstancing )
408 {
409 if( BatchMasterClass == NULL )
410 {
411 // If there is no current dependency master, register this class as the master, and reset the array
412 BatchMasterClass = ClassToGather;
413 BatchClassDependencies.Empty();
414 bMasterClass = true;
415 }
416 else
417 {
418 // This class was instantiated while another class was gathering dependencies, so record it as a dependency
419 BatchClassDependencies.AddUnique(ClassToGather);
420 }
421 }
422}
423
424FScopedClassDependencyGather::~FScopedClassDependencyGather()
425{
426 // If this gatherer was the initial gatherer for the current scope, process
427 // dependencies (unless compiling on load is explicitly disabled)
428 if( bMasterClass )
429 {
430 auto DependencyIter = BatchClassDependencies.CreateIterator();
431 // implemented as a lambda, to prevent duplicated code between
432 // BatchMasterClass and BatchClassDependencies entries
433 auto RecompileClassLambda = [&DependencyIter](UClass* Class)
434 {
435 Class->ConditionalRecompileClass(&FUObjectThreadContext::Get().ObjLoaded);
436
437 // because of the above call to ConditionalRecompileClass(), the
438 // specified Class gets "cleaned and sanitized" (meaning its old
439 // properties get moved to a TRASH class, and new ones are
440 // constructed in their place)... the unfortunate side-effect of
441 // this is that child classes that have already been linked are now
442 // referencing TRASH inherited properties; to resolve this issue,
443 // here we go back through dependencies that were already recompiled
444 // and re-link any that are sub-classes
445 //
446 // @TODO: this isn't the most optimal solution to this problem; we
447 // should probably instead prevent CleanAndSanitizeClass()
448 // from running for BytecodeOnly compiles (we would then need
449 // to block UField re-creation)... UE-14957 was created to
450 // track this issue
451 auto ReverseIt = DependencyIter;
452 for (--ReverseIt; ReverseIt.GetIndex() >= 0; --ReverseIt)
453 {
454 UClass* ProcessedDependency = *ReverseIt;
455 if (ProcessedDependency->IsChildOf(Class))
456 {
457 ProcessedDependency->StaticLink(/*bRelinkExistingProperties =*/true);
458 }
459 }
460 };
461
462 if(!GBlueprintUseCompilationManager)
463 {
464 for( ; DependencyIter; ++DependencyIter )
465 {
466 UClass* Dependency = *DependencyIter;
467 if( Dependency->ClassGeneratedBy != BatchMasterClass->ClassGeneratedBy )
468 {
469 RecompileClassLambda(Dependency);
470 }
471 }
472
473 // Finally, recompile the master class to make sure it gets updated too
474 RecompileClassLambda(BatchMasterClass);
475 }
476 else
477 {
478 BatchMasterClass->ConditionalRecompileClass(&FUObjectThreadContext::Get().ObjLoaded);
479 }
480
481 BatchMasterClass = NULL;
482 }
483}
484
485TArray<UClass*> const& FScopedClassDependencyGather::GetCachedDependencies()
486{
487 return BatchClassDependencies;
488}
489#endif //WITH_EDITOR
490
491
492
493/*******************************************************************************
494 * FLinkerLoad
495 ******************************************************************************/
496
497// rather than littering the code with USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
498// checks, let's just define DEFERRED_DEPENDENCY_CHECK for the file
499#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
500 #define DEFERRED_DEPENDENCY_CHECK(CheckExpr) ensure(CheckExpr)
501#else // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
502 #define DEFERRED_DEPENDENCY_CHECK(CheckExpr)
503#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
504
505struct FPreloadMembersHelper
506{
507 static void PreloadMembers(UObject* InObject)
508 {
509 // Collect a list of all things this element owns
510 TArray<UObject*> BPMemberReferences;
511 FReferenceFinder ComponentCollector(BPMemberReferences, InObject, false, true, true, true);
512 ComponentCollector.FindReferences(InObject);
513
514 // Iterate over the list, and preload everything so it is valid for refreshing
515 for (TArray<UObject*>::TIterator it(BPMemberReferences); it; ++it)
516 {
517 UObject* CurrentObject = *it;
518 if (!CurrentObject->HasAnyFlags(RF_LoadCompleted))
519 {
520 check(!GEventDrivenLoaderEnabled || !EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME);
521 CurrentObject->SetFlags(RF_NeedLoad);
522 if (FLinkerLoad* Linker = CurrentObject->GetLinker())
523 {
524 Linker->Preload(CurrentObject);
525 PreloadMembers(CurrentObject);
526 }
527 }
528 }
529 }
530
531 static void PreloadObject(UObject* InObject)
532 {
533 if (InObject && !InObject->HasAnyFlags(RF_LoadCompleted))
534 {
535 check(!GEventDrivenLoaderEnabled || !EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME);
536 InObject->SetFlags(RF_NeedLoad);
537 if (FLinkerLoad* Linker = InObject->GetLinker())
538 {
539 Linker->Preload(InObject);
540 }
541 }
542 }
543};
544
545/**
546 * A helper utility for tracking exports whose classes we're currently running
547 * through ForceRegenerateClass(). This is primarily relied upon to help prevent
548 * infinite recursion since ForceRegenerateClass() doesn't do anything to
549 * progress the state of the linker.
550 */
551struct FResolvingExportTracker : TThreadSingleton<FResolvingExportTracker>
552{
553public:
554 /** */
555 void FlagLinkerExportAsResolving(FLinkerLoad* Linker, int32 ExportIndex)
556 {
557 ResolvingExports.FindOrAdd(Linker).Add(ExportIndex);
558 }
559
560 /** */
561 bool IsLinkerExportBeingResolved(FLinkerLoad* Linker, int32 ExportIndex) const
562 {
563 if (const TSet<int32>* ExportIndices = ResolvingExports.Find(Linker))
564 {
565 return ExportIndices->Contains(ExportIndex);
566 }
567 return false;
568 }
569
570 /** */
571 void FlagExportClassAsFullyResolved(FLinkerLoad* Linker, int32 ExportIndex)
572 {
573 if (TSet<int32>* ExportIndices = ResolvingExports.Find(Linker))
574 {
575 ExportIndices->Remove(ExportIndex);
576 if (ExportIndices->Num() == 0)
577 {
578 ResolvingExports.Remove(Linker);
579 }
580 }
581 }
582
583#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
584 void FlagFullExportResolvePassComplete(FLinkerLoad* Linker)
585 {
586 FullyResolvedLinkers.Add(Linker);
587 }
588
589 bool HasPerformedFullExportResolvePass(FLinkerLoad* Linker)
590 {
591 return FullyResolvedLinkers.Contains(Linker);
592 }
593#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
594
595 void Reset(FLinkerLoad* Linker)
596 {
597 ResolvingExports.Remove(Linker);
598#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
599 FullyResolvedLinkers.Remove(Linker);
600#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
601
602 // ClassToPlaceholderMap may have entries because instances of placeholder classes (which
603 // will be resolved in ResolveDeferredExports()), will never have had ResolvePlaceholders
604 // for their class called. These entries are harmless and we can discard them here:
605 ClassToPlaceholderMap.Reset();
606 }
607
608 void AddLinkerPlaceholderObject(UClass* ClassWaitingFor, ULinkerPlaceholderExportObject* Placeholder)
609 {
610 ClassToPlaceholderMap.FindOrAdd(ClassWaitingFor).Add(Placeholder);
611 }
612
613 void ResolvePlaceholders(UClass* ForClass)
614 {
615 //ensureMsgf(false, TEXT("%s"), *ForClass->GetFName().ToString());
616 if (ForClass != nullptr)
617 {
618 UE_LOG(LogTemp, Warning, TEXT("test de compilation de ouf"));
619 //UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->StaticClass()->GetName());
620 UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->GetName());
621 //UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->ClassDefaultObject->GetName());
622 //UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->GetClass()->GetName());
623 UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->GetDefaultObjectName().ToString());
624 UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->GetPathName());
625 //UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->ClassDefaultObject->GetPathName());
626 UE_LOG(LogTemp, Warning, TEXT("Class = %s"), *ForClass->GetFullName());
627 }
628 UE_LOG(LogTemp, Warning, TEXT("test de compilation de ouf en dehors de la secu"));
629 TArray<ULinkerPlaceholderExportObject*>* Placeholders = ClassToPlaceholderMap.Find(ForClass);
630 if( Placeholders )
631 {
632 for(ULinkerPlaceholderExportObject* Placeholder : *Placeholders)
633 {
634 if(!Placeholder->IsMarkedResolved())
635 {
636 FLinkerLoad* Linker = Placeholder->GetLinker();
637 if(ensure(Linker))
638 {
639 Linker->ResolvePlaceholder( Placeholder );
640 }
641 }
642 }
643 // Remove from map as we could get GCd later
644 ClassToPlaceholderMap.Remove(ForClass);
645 }
646 }
647
648private:
649 /** */
650 TMap< FLinkerLoad*, TSet<int32> > ResolvingExports;
651
652#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
653 TSet<FLinkerLoad*> FullyResolvedLinkers;
654#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
655
656 TMap< UClass*, TArray<ULinkerPlaceholderExportObject*> > ClassToPlaceholderMap;
657};
658
659/**
660 * Regenerates/Refreshes a blueprint class
661 *
662 * @param LoadClass Instance of the class currently being loaded and which is the parent for the blueprint
663 * @param ExportObject Current object being exported
664 * @return Returns true if regeneration was successful, otherwise false
665 */
666bool FLinkerLoad::RegenerateBlueprintClass(UClass* LoadClass, UObject* ExportObject)
667{
668 // determine if somewhere further down the callstack, we're already in this
669 // function for this class
670 bool const bAlreadyRegenerating = LoadClass->ClassGeneratedBy->HasAnyFlags(RF_BeingRegenerated);
671 // Flag the class source object, so we know we're already in the process of compiling this class
672 LoadClass->ClassGeneratedBy->SetFlags(RF_BeingRegenerated);
673
674 // Cache off the current CDO, and specify the CDO for the load class
675 // manually... do this before we Preload() any children members so that if
676 // one of those preloads subsequently ends up back here for this class,
677 // then the ExportObject is carried along and used in the eventual RegenerateClass() call
678 UObject* CurrentCDO = ExportObject;
679 check(!bAlreadyRegenerating || (LoadClass->ClassDefaultObject == ExportObject));
680 LoadClass->ClassDefaultObject = CurrentCDO;
681
682 // Finish loading the class here, so we have all the appropriate data to copy over to the new CDO
683 TArray<UObject*> AllChildMembers;
684 GetObjectsWithOuter(LoadClass, /*out*/ AllChildMembers);
685 for (int32 Index = 0; Index < AllChildMembers.Num(); ++Index)
686 {
687 UObject* Member = AllChildMembers[Index];
688 Preload(Member);
689 }
690
691 // if this was subsequently regenerated from one of the above preloads, then
692 // we don't have to finish this off, it was already done
693 bool const bWasSubsequentlyRegenerated = !LoadClass->ClassGeneratedBy->HasAnyFlags(RF_BeingRegenerated);
694 // @TODO: find some other condition to block this if we've already
695 // regenerated the class (not just if we've regenerated the class
696 // from an above Preload(Member))... UBlueprint::RegenerateClass()
697 // has an internal conditional to block getting into it again, but we
698 // can't check UBlueprint members from this module
699 if (!bWasSubsequentlyRegenerated)
700 {
701 Preload(LoadClass);
702
703 LoadClass->StaticLink(true);
704 Preload(CurrentCDO);
705
706 // CDO preloaded - we can now resolve placeholders:
707 FResolvingExportTracker::Get().ResolvePlaceholders(LoadClass);
708
709 // Make sure that we regenerate any parent classes first before attempting to build a child
710 TArray<UClass*> ClassChainOrdered;
711 {
712 // Just ordering the class hierarchy from root to leafs:
713 UClass* ClassChain = LoadClass->GetSuperClass();
714 while (ClassChain && ClassChain->ClassGeneratedBy)
715 {
716 // O(n) insert, but n is tiny because this is a class hierarchy...
717 ClassChainOrdered.Insert(ClassChain, 0);
718 ClassChain = ClassChain->GetSuperClass();
719 }
720 }
721 for (UClass* Class : ClassChainOrdered)
722 {
723 UObject* BlueprintObject = Class->ClassGeneratedBy;
724 if (BlueprintObject && BlueprintObject->HasAnyFlags(RF_BeingRegenerated))
725 {
726 // This code appears to be completely unused:
727
728 // Always load the parent blueprint here in case there is a circular dependency. This will
729 // ensure that the blueprint is fully serialized before attempting to regenerate the class.
730 FPreloadMembersHelper::PreloadObject(BlueprintObject);
731
732 FPreloadMembersHelper::PreloadMembers(BlueprintObject);
733 // recurse into this function for this parent class;
734 // 'ClassDefaultObject' should be the class's original ExportObject
735 RegenerateBlueprintClass(Class, Class->ClassDefaultObject);
736 }
737 }
738
739 {
740 UObject* BlueprintObject = LoadClass->ClassGeneratedBy;
741 // Preload the blueprint to make sure it has all the data the class needs for regeneration
742 FPreloadMembersHelper::PreloadObject(BlueprintObject);
743
744 UClass* RegeneratedClass = BlueprintObject->RegenerateClass(LoadClass, CurrentCDO, FUObjectThreadContext::Get().ObjLoaded);
745 if (RegeneratedClass)
746 {
747 BlueprintObject->ClearFlags(RF_BeingRegenerated);
748 // Fix up the linker so that the RegeneratedClass is used
749 LoadClass->ClearFlags(RF_NeedLoad | RF_NeedPostLoad);
750 }
751 }
752 }
753
754 bool const bSuccessfulRegeneration = !LoadClass->ClassGeneratedBy->HasAnyFlags(RF_BeingRegenerated);
755 // if this wasn't already flagged as regenerating when we first entered this
756 // function, the clear it ourselves.
757 if (!bAlreadyRegenerating)
758 {
759 LoadClass->ClassGeneratedBy->ClearFlags(RF_BeingRegenerated);
760 }
761
762 return bSuccessfulRegeneration;
763}
764
765/**
766 * Frivolous helper functions, to provide unique identifying names for our different placeholder types.
767 */
768template<class PlaceholderType>
769static FString GetPlaceholderPrefix() { return TEXT("PLACEHOLDER_"); }
770template<>
771FString GetPlaceholderPrefix<ULinkerPlaceholderFunction>() { return TEXT("PLACEHOLDER-FUNCTION_"); }
772template<>
773FString GetPlaceholderPrefix<ULinkerPlaceholderClass>() { return TEXT("PLACEHOLDER-CLASS_"); }
774
775/** Internal utility function for spawning various type of placeholder objects. */
776template<class PlaceholderType>
777static PlaceholderType* MakeImportPlaceholder(UObject* Outer, const TCHAR* TargetObjName, int32 ImportIndex = INDEX_NONE)
778{
779 PlaceholderType* PlaceholderObj = nullptr;
780
781#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
782 FName PlaceholderName(*FString::Printf(TEXT("%s_%s"), *GetPlaceholderPrefix<PlaceholderType>(), TargetObjName));
783 PlaceholderName = MakeUniqueObjectName(Outer, PlaceholderType::StaticClass(), PlaceholderName);
784
785 PlaceholderObj = NewObject<PlaceholderType>(Outer, PlaceholderType::StaticClass(), PlaceholderName, RF_Public | RF_Transient);
786
787 if (ImportIndex != INDEX_NONE)
788 {
789 PlaceholderObj->PackageIndex = FPackageIndex::FromImport(ImportIndex);
790 }
791 // else, this is probably coming from something like an ImportText() call,
792 // and isn't referenced by the ImportMap... instead, this should be stored
793 // in the FLinkerLoad's ImportPlaceholders map
794
795 // make sure the class is fully formed (has its
796 // ClassAddReferencedObjects/ClassConstructor members set)
797 PlaceholderObj->Bind();
798 PlaceholderObj->StaticLink(/*bRelinkExistingProperties =*/true);
799
800#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
801 if (ULinkerPlaceholderClass* OuterAsPlaceholder = dynamic_cast<ULinkerPlaceholderClass*>(Outer))
802 {
803 OuterAsPlaceholder->AddChildObject(PlaceholderObj);
804 }
805#endif //USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
806#endif //USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
807
808 return PlaceholderObj;
809}
810
811/** Recursive utility function, set up to find a specific import that has already been created (emulates a block from FLinkerLoad::CreateImport)*/
812static UObject* FindExistingImportObject(const int32 Index, const TArray<FObjectImport>& ImportMap)
813{
814 const FObjectImport& Import = ImportMap[Index];
815
816 UObject* FindOuter = nullptr;
817 if (Import.OuterIndex.IsImport())
818 {
819 int32 OuterIndex = Import.OuterIndex.ToImport();
820 const FObjectImport& OuterImport = ImportMap[OuterIndex];
821
822 if (OuterImport.XObject != nullptr)
823 {
824 FindOuter = OuterImport.XObject;
825 }
826 else
827 {
828 FindOuter = FindExistingImportObject(OuterIndex, ImportMap);
829 }
830 }
831
832 UObject* FoundObject = nullptr;
833 if (FindOuter != nullptr || Import.OuterIndex.IsNull())
834 {
835 if (UObject* ClassPackage = FindObject<UPackage>(/*Outer =*/nullptr, *Import.ClassPackage.ToString()))
836 {
837 if (UClass* ImportClass = FindObject<UClass>(ClassPackage, *Import.ClassName.ToString()))
838 {
839 // This function is set up to emulate a block towards the top of
840 // FLinkerLoad::CreateImport(). However, since this is used in
841 // deferred dependency loading we need to be careful not to invoke
842 // subsequent loads. The block in CreateImport() calls Preload()
843 // and GetDefaultObject() which are not suitable here, so to
844 // emulate/keep the contract that that block provides, we'll only
845 // lookup the object if its class is loaded, and has a CDO (this
846 // is just to mitigate risk from this change)
847 if (!ImportClass->HasAnyFlags(RF_NeedLoad) && ImportClass->ClassDefaultObject)
848 {
849 FoundObject = StaticFindObjectFast(ImportClass, FindOuter, Import.ObjectName);
850 }
851 }
852 }
853 }
854 return FoundObject;
855}
856
857/**
858 * This utility struct helps track blueprint structs/linkers that are currently
859 * in the middle of a call to ResolveDeferredDependencies(). This can be used
860 * to know if a dependency's resolve needs to be finished (to avoid unwanted
861 * placeholder references ending up in script-code).
862 */
863struct FUnresolvedStructTracker
864{
865public:
866 /** Marks the specified struct (and its linker) as "resolving" for the lifetime of this instance */
867 FUnresolvedStructTracker(UStruct* LoadStruct)
868 : TrackedStruct(LoadStruct)
869 {
870 DEFERRED_DEPENDENCY_CHECK((LoadStruct != nullptr) && (LoadStruct->GetLinker() != nullptr));
871 FScopeLock UnresolvedStructsLock(&UnresolvedStructsCritical);
872 UnresolvedStructs.Add(LoadStruct);
873 }
874
875 /** Removes the wrapped struct from the "resolving" set (it has been fully "resolved") */
876 ~FUnresolvedStructTracker()
877 {
878 // even if another FUnresolvedStructTracker added this struct earlier,
879 // we want the most nested one removing it from the set (because this
880 // means the struct is fully resolved, even if we're still in the middle
881 // of a ResolveDeferredDependencies() call further up the stack)
882 FScopeLock UnresolvedStructsLock(&UnresolvedStructsCritical);
883 UnresolvedStructs.Remove(TrackedStruct);
884 }
885
886 /**
887 * Checks to see if the specified import object is a blueprint class/struct
888 * that is currently in the midst of resolving (and hasn't completed that
889 * resolve elsewhere in some nested call).
890 *
891 * @param ImportObject The object you wish to check.
892 * @return True if the specified object is a class/struct that hasn't been fully resolved yet (otherwise false).
893 */
894 static bool IsImportStructUnresolved(UObject* ImportObject)
895 {
896 FScopeLock UnresolvedStructsLock(&UnresolvedStructsCritical);
897 return UnresolvedStructs.Contains(ImportObject);
898 }
899
900 /**
901 * Checks to see if the specified linker is associated with any of the
902 * unresolved structs that this is currently tracking.
903 *
904 * NOTE: This could return false, even if the linker is in a
905 * ResolveDeferredDependencies() call futher up the callstack... in
906 * that scenario, the associated struct was fully resolved by a
907 * subsequent call to the same function (for the same linker/struct).
908 *
909 * @param Linker The linker you want to check.
910 * @return True if the specified linker is in the midst of an unfinished ResolveDeferredDependencies() call (otherwise false).
911 */
912 static bool IsAssociatedStructUnresolved(const FLinkerLoad* Linker)
913 {
914 FScopeLock UnresolvedStructsLock(&UnresolvedStructsCritical);
915 for (UObject* UnresolvedObj : UnresolvedStructs)
916 {
917 // each unresolved struct should have a linker set on it, because
918 // they would have had to go through Preload()
919 if (UnresolvedObj->GetLinker() == Linker)
920 {
921 return true;
922 }
923 }
924 return false;
925 }
926
927 /** */
928 static void Reset(const FLinkerLoad* Linker)
929 {
930 FScopeLock UnresolvedStructsLock(&UnresolvedStructsCritical);
931 TArray<UObject*> ToRemove;
932 for (UObject* UnresolvedObj : UnresolvedStructs)
933 {
934 if (UnresolvedObj->GetLinker() == Linker)
935 {
936 ToRemove.Add(UnresolvedObj);
937 }
938 }
939 for (UObject* ResetingObj : ToRemove)
940 {
941 UnresolvedStructs.Remove(ResetingObj);
942 }
943 }
944
945private:
946 /** The struct that is currently being "resolved" */
947 UStruct* TrackedStruct;
948
949 /**
950 * A set of blueprint structs & classes that are currently being "resolved"
951 * by ResolveDeferredDependencies() (using UObject* instead of UStruct, so
952 * we don't have to cast import objects before checking for their presence).
953 */
954 static TSet<UObject*> UnresolvedStructs;
955 static FCriticalSection UnresolvedStructsCritical;
956};
957/** A global set that tracks structs currently being ran through (and unfinished by) FLinkerLoad::ResolveDeferredDependencies() */
958TSet<UObject*> FUnresolvedStructTracker::UnresolvedStructs;
959FCriticalSection FUnresolvedStructTracker::UnresolvedStructsCritical;
960
961bool FLinkerLoad::DeferPotentialCircularImport(const int32 Index)
962{
963#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
964 if (!FBlueprintSupport::UseDeferredDependencyLoading())
965 {
966 return false;
967 }
968
969 //--------------------------------------
970 // Phase 1: Stub in Dependencies
971 //--------------------------------------
972
973 FObjectImport& Import = ImportMap[Index];
974
975 if (Import.XObject != nullptr)
976 {
977 FLinkerPlaceholderBase* ImportPlaceholder = nullptr;
978 if (ULinkerPlaceholderClass* AsPlaceholderClass = Cast<ULinkerPlaceholderClass>(Import.XObject))
979 {
980 ImportPlaceholder = AsPlaceholderClass;
981 }
982 else if (ULinkerPlaceholderFunction* AsPlaceholderFunc = Cast<ULinkerPlaceholderFunction>(Import.XObject))
983 {
984 ImportPlaceholder = AsPlaceholderFunc;
985 }
986
987 const bool bIsResolvingPlaceholders = ImportPlaceholder && (LoadFlags & LOAD_DeferDependencyLoads) == LOAD_None;
988 // if this import already had a placeholder spawned for it, but the package
989 // has passed the need for placeholders (it's in the midst of ResolveDeferredDependencies)
990 if (bIsResolvingPlaceholders)
991 {
992 // this is to validate our assumption that this package is in ResolveDeferredDependencies() earlier up the stack
993 DEFERRED_DEPENDENCY_CHECK(FUnresolvedStructTracker::IsAssociatedStructUnresolved(this));
994
995 UClass* LoadClass = nullptr;
996 // Get the LoadClass that is currently in the midst of being resolved (needed to pass to ResolveDependencyPlaceholder)
997 {
998 // if DeferredCDOIndex is not set, then this is presumably a struct package (it should always be
999 // set at this point for class BP packages - see Preload() where DeferredCDOIndex is assigned)
1000 if (DeferredCDOIndex != INDEX_NONE)
1001 {
1002 FPackageIndex ClassIndex = ExportMap[DeferredCDOIndex].ClassIndex;
1003 DEFERRED_DEPENDENCY_CHECK(ClassIndex.IsExport());
1004
1005 if (ClassIndex.IsExport())
1006 {
1007 FObjectExport ClassExport = ExportMap[ClassIndex.ToExport()];
1008 LoadClass = Cast<UClass>(ClassExport.Object);
1009 }
1010
1011 DEFERRED_DEPENDENCY_CHECK(LoadClass != nullptr);
1012 }
1013 }
1014
1015 // go ahead and resolve the placeholder here (since someone's requesting it and we're already in the
1016 // midst of resolving placeholders earlier in the stack) - the idea is that the resolve, already in progress, will
1017 // eventually get to this placeholder, it just hasn't looped there yet
1018 //
1019 // this will prevent other, needless placeholders from being created (export templates that are relying on this class, etc.)
1020 ResolveDependencyPlaceholder(ImportPlaceholder, LoadClass);
1021
1022#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1023 const bool bIsStillPlaceholder = Import.XObject && (Import.XObject->IsA<ULinkerPlaceholderClass>() || Import.XObject->IsA<ULinkerPlaceholderFunction>());
1024 DEFERRED_DEPENDENCY_CHECK(!bIsStillPlaceholder);
1025 return bIsStillPlaceholder;
1026#else
1027 // presume that ResolveDependencyPlaceholder() worked and the import is no longer a placeholder
1028 return false;
1029#endif
1030
1031 }
1032 return (ImportPlaceholder != nullptr);
1033 }
1034
1035 if ((LoadFlags & LOAD_DeferDependencyLoads) && !IsImportNative(Index))
1036 {
1037 // emulate the block in CreateImport(), that attempts to find an existing
1038 // object in memory first... this is to account for async loading, which
1039 // can clear Import.XObject (via FLinkerManager::DissociateImportsAndForcedExports)
1040 // at inopportune times (after it's already been set) - in this case
1041 // we shouldn't need a placeholder, because the object already exists; we
1042 // just need to keep from serializing it any further (which is why we've
1043 // emulated it here, to cut out on a Preload() call)
1044 if (!GIsEditor && !IsRunningCommandlet())
1045 {
1046 Import.XObject = FindExistingImportObject(Index, ImportMap);
1047 if (Import.XObject)
1048 {
1049 return true;
1050 }
1051 }
1052
1053 if (UObject* ClassPackage = FindObject<UPackage>(/*Outer =*/nullptr, *Import.ClassPackage.ToString()))
1054 {
1055 if (const UClass* ImportClass = FindObject<UClass>(ClassPackage, *Import.ClassName.ToString()))
1056 {
1057 if (ImportClass->IsChildOf<UClass>())
1058 {
1059 Import.XObject = MakeImportPlaceholder<ULinkerPlaceholderClass>(LinkerRoot, *Import.ObjectName.ToString(), Index);
1060 }
1061 else if (ImportClass->IsChildOf<UFunction>() && Import.OuterIndex.IsImport())
1062 {
1063 const int32 OuterImportIndex = Import.OuterIndex.ToImport();
1064 // @TODO: if the sole reason why we have ULinkerPlaceholderFunction
1065 // is that it's outer is a placeholder, then we
1066 // could instead log it (with the placeholder) as
1067 // a referencer, and then move the function later
1068 if (DeferPotentialCircularImport(OuterImportIndex))
1069 {
1070 UObject* FuncOuter = ImportMap[OuterImportIndex].XObject;
1071 // This is an ugly check to make sure we don't make a placeholder function for a missing native instance.
1072 // We likely also need to avoid making placeholders for anything that's not outered to a ULinkerPlaceholderClass,
1073 // but the DEFERRED_DEPENDENCY_CHECK may be out of date...
1074 if(Cast<UClass>(FuncOuter))
1075 {
1076 Import.XObject = MakeImportPlaceholder<ULinkerPlaceholderFunction>(FuncOuter, *Import.ObjectName.ToString(), Index);
1077 DEFERRED_DEPENDENCY_CHECK(dynamic_cast<ULinkerPlaceholderClass*>(FuncOuter) != nullptr);
1078 }
1079 }
1080 }
1081 }
1082 }
1083
1084 // not the best way to check this (but we don't have ObjectFlags on an
1085 // import), but we don't want non-native (blueprint) CDO refs slipping
1086 // through... we've only seen these needed when serializing a class's
1087 // bytecode, and we resolved that by deferring script serialization
1088 DEFERRED_DEPENDENCY_CHECK(!Import.ObjectName.ToString().StartsWith("Default__"));
1089 }
1090 return (Import.XObject != nullptr);
1091#else // !USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1092 return false;
1093#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1094}
1095
1096#if WITH_EDITOR
1097bool FLinkerLoad::IsSuppressableBlueprintImportError(int32 ImportIndex) const
1098{
1099 // We want to suppress any import errors that target a BlueprintGeneratedClass
1100 // since these issues can occur when an externally referenced Blueprint is saved
1101 // without compiling. This should not be a problem because all Blueprints are
1102 // compiled-on-load.
1103 static const FName NAME_BlueprintGeneratedClass("BlueprintGeneratedClass");
1104
1105 // We will look at each outer of the Import to see if any of them are a BPGC
1106 while (ImportMap.IsValidIndex(ImportIndex))
1107 {
1108 const FObjectImport& TestImport = ImportMap[ImportIndex];
1109 if (TestImport.ClassName == NAME_BlueprintGeneratedClass)
1110 {
1111 // The import is a BPGC, suppress errors
1112 return true;
1113 }
1114
1115 // Check if this is a BP CDO, if so our class will be in the import table
1116 for (const FObjectImport& PotentialBPClass : ImportMap)
1117 {
1118 if (PotentialBPClass.ObjectName == TestImport.ClassName && PotentialBPClass.ClassName == NAME_BlueprintGeneratedClass)
1119 {
1120 return true;
1121 }
1122 }
1123
1124 if (!TestImport.OuterIndex.IsNull() && TestImport.OuterIndex.IsImport())
1125 {
1126 ImportIndex = TestImport.OuterIndex.ToImport();
1127 }
1128 else
1129 {
1130 // It's not an import, we are done
1131 break;
1132 }
1133 }
1134
1135 return false;
1136}
1137#endif // WITH_EDITOR
1138
1139/**
1140 * A helper struct that adds and removes its linker/export combo from the
1141 * thread's FResolvingExportTracker (based off the scope it was declared within).
1142 */
1143struct FScopedResolvingExportTracker
1144{
1145public:
1146 FScopedResolvingExportTracker(FLinkerLoad* Linker, int32 ExportIndex)
1147 : TrackedLinker(Linker), TrackedExport(ExportIndex)
1148 {
1149 FResolvingExportTracker::Get().FlagLinkerExportAsResolving(Linker, ExportIndex);
1150 }
1151
1152 ~FScopedResolvingExportTracker()
1153 {
1154 FResolvingExportTracker::Get().FlagExportClassAsFullyResolved(TrackedLinker, TrackedExport);
1155 }
1156
1157private:
1158 FLinkerLoad* TrackedLinker;
1159 int32 TrackedExport;
1160};
1161
1162bool FLinkerLoad::DeferExportCreation(const int32 Index, UObject* Outer)
1163{
1164 FObjectExport& Export = ExportMap[Index];
1165
1166#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1167 if (!FBlueprintSupport::UseDeferredDependencyLoading() || FBlueprintSupport::IsDeferredExportCreationDisabled())
1168 {
1169 return false;
1170 }
1171
1172 if ((Export.Object != nullptr))
1173 {
1174 return false;
1175 }
1176
1177 UClass* LoadClass = GetExportLoadClass(Index);
1178
1179 if (LoadClass == nullptr)
1180 {
1181 return false;
1182 }
1183
1184 if(ULinkerPlaceholderExportObject* OuterPlaceholder = Cast<ULinkerPlaceholderExportObject>(Outer))
1185 {
1186 // we deferred the outer, so its constructor has not had a chance
1187 // to create and initialize native subobjects. We must defer this subobject:
1188 FString ClassName = LoadClass->GetName();
1189 FName PlaceholderName(*FString::Printf(TEXT("PLACEHOLDER-INST_of_%s"), *ClassName));
1190 UClass* PlaceholderType = ULinkerPlaceholderExportObject::StaticClass();
1191 PlaceholderName = MakeUniqueObjectName(Outer, PlaceholderType, PlaceholderName);
1192
1193 ULinkerPlaceholderExportObject* Placeholder = NewObject<ULinkerPlaceholderExportObject>(Outer, PlaceholderType, PlaceholderName, RF_Public | RF_Transient);
1194 Placeholder->SetLinker(this, Index, false);
1195 Placeholder->PackageIndex = FPackageIndex::FromExport(Index);
1196
1197 Export.Object = Placeholder;
1198
1199 // the subobject placeholder must be resolved after its outer has been resolved:
1200 OuterPlaceholder->SetupPlaceholderSubobject(Placeholder);
1201
1202 return true;
1203 }
1204
1205 if (LoadClass->HasAnyClassFlags(CLASS_Native))
1206 {
1207 return false;
1208 }
1209
1210 ULinkerPlaceholderClass* AsPlaceholderClass = Cast<ULinkerPlaceholderClass>(LoadClass);
1211 bool const bIsPlaceholderClass = (AsPlaceholderClass != nullptr);
1212
1213 FLinkerLoad* ClassLinker = LoadClass->GetLinker();
1214 if ( !bIsPlaceholderClass
1215 && ((ClassLinker == nullptr) || !ClassLinker->IsBlueprintFinalizationPending())
1216 && (!LoadClass->ClassDefaultObject || LoadClass->ClassDefaultObject->HasAnyFlags(RF_LoadCompleted) || !LoadClass->ClassDefaultObject->HasAnyFlags(RF_WasLoaded)) )
1217 {
1218 return false;
1219 }
1220
1221 bool const bIsLoadingExportClass = (LoadFlags & LOAD_DeferDependencyLoads) ||
1222 IsBlueprintFinalizationPending();
1223 // if we're not in the process of "loading/finalizing" this package's
1224 // Blueprint class, then we're either running this before the linker has got
1225 // to that class, or we're finished and in the midst of regenerating that
1226 // class... either way, we don't have to defer the export (as long as we
1227 // make sure the export's class is fully regenerated... presumably it is in
1228 // the midst of doing so somewhere up the callstack)
1229 if (!bIsLoadingExportClass || (LoadFlags & LOAD_ResolvingDeferredExports) != 0 )
1230 {
1231 DEFERRED_DEPENDENCY_CHECK(!IsExportBeingResolved(Index));
1232 FScopedResolvingExportTracker ReentranceGuard(this, Index);
1233
1234 // we want to be very careful, since we haven't filled in the export yet,
1235 // we could get stuck in a recursive loop here (force-finalizing the
1236 // class here ends us back
1237 ForceRegenerateClass(LoadClass);
1238 return false;
1239 }
1240
1241 UPackage* PlaceholderOuter = LinkerRoot;
1242 UClass* PlaceholderType = ULinkerPlaceholderExportObject::StaticClass();
1243
1244 FString ClassName = LoadClass->GetName();
1245 //ClassName.RemoveFromEnd("_C");
1246 FName PlaceholderName(*FString::Printf(TEXT("PLACEHOLDER-INST_of_%s"), *ClassName));
1247 PlaceholderName = MakeUniqueObjectName(PlaceholderOuter, PlaceholderType, PlaceholderName);
1248
1249 ULinkerPlaceholderExportObject* Placeholder = NewObject<ULinkerPlaceholderExportObject>(PlaceholderOuter, PlaceholderType, PlaceholderName, RF_Public | RF_Transient);
1250 Placeholder->PackageIndex = FPackageIndex::FromExport(Index);
1251 Placeholder->SetLinker(this, Index, false);
1252 FResolvingExportTracker::Get().AddLinkerPlaceholderObject(LoadClass, Placeholder);
1253
1254 Export.Object = Placeholder;
1255#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1256
1257 return true;
1258}
1259
1260int32 FLinkerLoad::FindCDOExportIndex(UClass* LoadClass)
1261{
1262 DEFERRED_DEPENDENCY_CHECK(LoadClass->GetLinker() == this);
1263 int32 const ClassExportIndex = LoadClass->GetLinkerIndex();
1264
1265 // @TODO: the cdo SHOULD be listed after the class in the ExportMap, so we
1266 // could start with ClassExportIndex to save on some cycles
1267 for (int32 ExportIndex = 0; ExportIndex < ExportMap.Num(); ++ExportIndex)
1268 {
1269 FObjectExport& Export = ExportMap[ExportIndex];
1270 if ((Export.ObjectFlags & RF_ClassDefaultObject) && Export.ClassIndex.IsExport() && (Export.ClassIndex.ToExport() == ClassExportIndex))
1271 {
1272 return ExportIndex;
1273 }
1274 }
1275 return INDEX_NONE;
1276}
1277
1278UPackage* LoadPackageInternal(UPackage* InOuter, const TCHAR* InLongPackageName, uint32 LoadFlags, FLinkerLoad* ImportLinker, FArchive* InReaderOverride);
1279
1280void FLinkerLoad::ResolveDeferredDependencies(UStruct* LoadStruct)
1281{
1282#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1283 //--------------------------------------
1284 // Phase 2: Resolve Dependency Stubs
1285 //--------------------------------------
1286 TGuardValue<uint32> LoadFlagsGuard(LoadFlags, (LoadFlags & ~LOAD_DeferDependencyLoads));
1287
1288#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1289 static int32 RecursiveDepth = 0;
1290 int32 const MeasuredDepth = RecursiveDepth;
1291 TGuardValue<int32> RecursiveDepthGuard(RecursiveDepth, RecursiveDepth + 1);
1292
1293 DEFERRED_DEPENDENCY_CHECK( (LoadStruct != nullptr) && (LoadStruct->GetLinker() == this) );
1294 DEFERRED_DEPENDENCY_CHECK( LoadStruct->HasAnyFlags(RF_LoadCompleted) );
1295#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1296
1297 // scoped block to manage the lifetime of ScopedResolveTracker, so that
1298 // this resolve is only tracked for the duration of resolving all its
1299 // placeholder classes, all member struct's placholders, and its parent's
1300 {
1301 FUnresolvedStructTracker ScopedResolveTracker(LoadStruct);
1302
1303 UClass* const LoadClass = Cast<UClass>(LoadStruct);
1304
1305 int32 StartingImportIndex = 0;
1306 // this function (for this linker) could be reentrant (see where we
1307 // recursively call ResolveDeferredDependencies() for super-classes below);
1308 // if that's the case, then we want to finish resolving the pending class
1309 // before we continue on
1310 if (ResolvingPlaceholderStack.Num() > 0)
1311 {
1312 // Since this method is recursive, we don't need to needlessly loop over all the imports we've already
1313 // resolved. However, we can only guarantee that the oldest entry in the 'resolving' stack is from a loop below.
1314 // Now that other places call ResolveDependencyPlaceholder(), the ResolvingPlaceholderStack may jump around and
1315 // skip some entries. The only certainty is that this function is the initial entry point for ResolveDependencyPlaceholder().
1316 const FPackageIndex& FirstResolvingIndex = ResolvingPlaceholderStack[0]->PackageIndex;
1317 if (FirstResolvingIndex.IsNull())
1318 {
1319 // if the placeholder's package index is null, that means we've already looped over the entire
1320 // ImportMap, and moved on to the loop below it (where we resolve placeholders from ImportText()
1321 // and such - they don't have entries in the ImportMap), so skip the ImportMap loop
1322 StartingImportIndex = ImportMap.Num();
1323 }
1324 else
1325 {
1326 DEFERRED_DEPENDENCY_CHECK(FirstResolvingIndex.IsImport());
1327
1328 // Since the ImportMap loop below resolves ULinkerPlaceholderFunction's owner first (out of order), we cannot
1329 // even guarantee that we've resolved everything prior to FirstResolvingIndex, so don't set StartingImportIndex in this case
1330// if (FirstResolvingIndex.IsImport())
1331// {
1332// StartingImportIndex = FirstResolvingIndex.ToImport()+1;
1333// }
1334 }
1335
1336 while (ResolvingPlaceholderStack.Num() > 0)
1337 {
1338 FLinkerPlaceholderBase* ResolvingPlaceholder = ResolvingPlaceholderStack.Pop();
1339 // If this is a placeholder outside the ImportMap (from ImportText(), etc.), then it needs a PackagePath to
1340 // resolve. Don't worry that one isn't passed in as a param here, ResolveDependencyPlaceholder() will
1341 // look it up itself in ImportPlaceholders (the param is just an optimization)
1342 ResolveDependencyPlaceholder(ResolvingPlaceholder, LoadClass);
1343 }
1344
1345#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1346 for (int32 ImportIndex = 0; ImportIndex < StartingImportIndex; ++ImportIndex)
1347 {
1348 if (UObject* ImportObj = ImportMap[ImportIndex].XObject)
1349 {
1350 DEFERRED_DEPENDENCY_CHECK(Cast<ULinkerPlaceholderClass>(ImportObj) == nullptr);
1351 DEFERRED_DEPENDENCY_CHECK(Cast<ULinkerPlaceholderFunction>(ImportObj) == nullptr);
1352 }
1353 }
1354#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1355 }
1356
1357 // because this loop could recurse (and end up finishing all of this for
1358 // us), we check HasUnresolvedDependencies() so we can early out
1359 // from this loop in that situation (the loop has been finished elsewhere)
1360 for (int32 ImportIndex = StartingImportIndex; ImportIndex < ImportMap.Num() && HasUnresolvedDependencies(); ++ImportIndex)
1361 {
1362 FObjectImport& Import = ImportMap[ImportIndex];
1363
1364 FLinkerLoad* SourceLinker = Import.SourceLinker;
1365 // we cannot rely on Import.SourceLinker being set, if you look
1366 // at FLinkerLoad::CreateImport(), you'll see in game builds
1367 // that we try to circumvent the normal Import loading with a
1368 // FindImportFast() call... if this is successful (the import
1369 // has already been somewhat loaded), then we don't fill out the
1370 // SourceLinker field
1371 if (SourceLinker == nullptr)
1372 {
1373 if (Import.XObject != nullptr)
1374 {
1375 SourceLinker = Import.XObject->GetLinker();
1376 //if (SourceLinker == nullptr)
1377 //{
1378 // UPackage* ImportPkg = Import.XObject->GetOutermost();
1379 // // we use this package as placeholder for our
1380 // // placeholders, so make sure to skip those (all other
1381 // // imports should belong to another package)
1382 // if (ImportPkg && ImportPkg != LinkerRoot)
1383 // {
1384 // SourceLinker = FindExistingLinkerForPackage(ImportPkg);
1385 // }
1386 //}
1387 }
1388 }
1389
1390 const UPackage* SourcePackage = (SourceLinker != nullptr) ? SourceLinker->LinkerRoot : nullptr;
1391 // this package may not have introduced any (possible) cyclic
1392 // dependencies, but it still could have been deferred (kept from
1393 // fully loading... we need to make sure metadata gets loaded, etc.)
1394 if ((SourcePackage != nullptr) && !SourcePackage->HasAnyFlags(RF_WasLoaded))
1395 {
1396 uint32 InternalLoadFlags = LoadFlags & (LOAD_NoVerify | LOAD_NoWarn | LOAD_Quiet);
1397 // make sure LoadAllObjects() is called for this package
1398 LoadPackageInternal(/*Outer =*/nullptr, *SourceLinker->Filename, InternalLoadFlags, this, nullptr); //-V595
1399 }
1400
1401 DEFERRED_DEPENDENCY_CHECK(ResolvingPlaceholderStack.Num() == 0);
1402 if (ULinkerPlaceholderClass* PlaceholderClass = Cast<ULinkerPlaceholderClass>(Import.XObject))
1403 {
1404 DEFERRED_DEPENDENCY_CHECK(PlaceholderClass->PackageIndex.ToImport() == ImportIndex);
1405
1406 // NOTE: we don't check that this resolve successfully replaced any
1407 // references (by the return value), because this resolve
1408 // could have been re-entered and completed by a nested call
1409 // to the same function (for the same placeholder)
1410 ResolveDependencyPlaceholder(PlaceholderClass, LoadClass);
1411 }
1412 else if (ULinkerPlaceholderFunction* PlaceholderFunction = Cast<ULinkerPlaceholderFunction>(Import.XObject))
1413 {
1414 if (ULinkerPlaceholderClass* PlaceholderOwner = Cast<ULinkerPlaceholderClass>(PlaceholderFunction->GetOwnerClass()))
1415 {
1416 ResolveDependencyPlaceholder(PlaceholderOwner, LoadClass);
1417 }
1418
1419 DEFERRED_DEPENDENCY_CHECK(PlaceholderFunction->PackageIndex.ToImport() == ImportIndex);
1420 ResolveDependencyPlaceholder(PlaceholderFunction, LoadClass);
1421 }
1422 else if (UScriptStruct* StructObj = Cast<UScriptStruct>(Import.XObject))
1423 {
1424 // in case this is a user defined struct, we have to resolve any
1425 // deferred dependencies in the struct
1426 if (SourceLinker != nullptr)
1427 {
1428 SourceLinker->ResolveDeferredDependencies(StructObj);
1429 }
1430 }
1431 DEFERRED_DEPENDENCY_CHECK(ResolvingPlaceholderStack.Num() == 0);
1432 }
1433
1434 // resolve any placeholders that were imported through methods like
1435 // ImportText() (meaning the ImportMap wouldn't reference them)
1436 while (ImportPlaceholders.Num() > 0)
1437 {
1438 auto PlaceholderIt = ImportPlaceholders.CreateIterator();
1439
1440 // store off the key before we resolve, in case this has been recursively removed
1441 const FName PlaceholderKey = PlaceholderIt.Key();
1442 ResolveDependencyPlaceholder(PlaceholderIt.Value(), LoadClass, PlaceholderKey);
1443
1444 ImportPlaceholders.Remove(PlaceholderKey);
1445 }
1446
1447 if (UStruct* SuperStruct = LoadStruct->GetSuperStruct())
1448 {
1449 FLinkerLoad* SuperLinker = SuperStruct->GetLinker();
1450 // NOTE: there is no harm in calling this when the super is not
1451 // "actively resolving deferred dependencies"... this condition
1452 // just saves on wasted ops, looping over the super's ImportMap
1453 if ((SuperLinker != nullptr) && SuperLinker->HasUnresolvedDependencies())
1454 {
1455 // a resolve could have already been started up the stack, and in turn
1456 // started loading a different package that resulted in another (this)
1457 // resolve beginning... in that scenario, the original resolve could be
1458 // for this class's super and we want to make sure that finishes before
1459 // we regenerate this class (else the generated script code could end up
1460 // with unwanted placeholder references; ones that would have been
1461 // resolved by the super's linker)
1462 SuperLinker->ResolveDeferredDependencies(SuperStruct);
1463 }
1464 }
1465
1466 // close the scope on ScopedResolveTracker (so LoadClass doesn't appear to
1467 // be resolving through the rest of this function)
1468 }
1469
1470 // @TODO: don't know if we need this, but could be good to have (as class
1471 // regeneration is about to force load a lot of this), BUT! this
1472 // doesn't work for map packages (because this would load the level's
1473 // ALevelScriptActor instance BEFORE the class has been regenerated)
1474 //LoadAllObjects();
1475
1476#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1477 auto CheckPlaceholderReferences = [this](FLinkerPlaceholderBase* Placeholder)
1478 {
1479 UObject* PlaceholderObj = Placeholder->GetPlaceholderAsUObject();
1480 if (PlaceholderObj->GetOuter() == LinkerRoot)
1481 {
1482 // there shouldn't be any deferred dependencies (belonging to this
1483 // linker) that need to be resolved by this point
1484 DEFERRED_DEPENDENCY_CHECK(!Placeholder->HasKnownReferences());
1485
1486 if (!Placeholder->PackageIndex.IsNull() && ensure(Placeholder->PackageIndex.IsImport()))
1487 {
1488 const UObject* ImportObj = ImportMap[Placeholder->PackageIndex.ToImport()].XObject;
1489 DEFERRED_DEPENDENCY_CHECK(ImportObj != PlaceholderObj);
1490 DEFERRED_DEPENDENCY_CHECK(Cast<ULinkerPlaceholderClass>(ImportObj) == nullptr);
1491 DEFERRED_DEPENDENCY_CHECK(Cast<ULinkerPlaceholderFunction>(ImportObj) == nullptr);
1492 }
1493 }
1494 };
1495
1496 for (TObjectIterator<ULinkerPlaceholderClass> PlaceholderIt; PlaceholderIt; ++PlaceholderIt)
1497 {
1498 CheckPlaceholderReferences(*PlaceholderIt);
1499 }
1500 for (TObjectIterator<ULinkerPlaceholderFunction> PlaceholderIt; PlaceholderIt; ++PlaceholderIt)
1501 {
1502 CheckPlaceholderReferences(*PlaceholderIt);
1503 }
1504
1505 DEFERRED_DEPENDENCY_CHECK(ImportPlaceholders.Num() == 0);
1506#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1507#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1508}
1509
1510bool FLinkerLoad::HasUnresolvedDependencies() const
1511{
1512#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1513 // checking (ResolvingPlaceholderStack.Num() <= 0) is not sufficient,
1514 // because the linker could be in the midst of a nested resolve (for a
1515 // struct, or super... see FLinkerLoad::ResolveDeferredDependencies)
1516 bool bIsClassExportUnresolved = FUnresolvedStructTracker::IsAssociatedStructUnresolved(this);
1517
1518 // (ResolvingPlaceholderStack.Num() <= 0) should imply
1519 // bIsClassExportUnresolved is true (but not the other way around)
1520 DEFERRED_DEPENDENCY_CHECK((ResolvingPlaceholderStack.Num() <= 0) || bIsClassExportUnresolved);
1521
1522 return bIsClassExportUnresolved;
1523
1524#else // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1525 return false;
1526#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1527}
1528
1529int32 FLinkerLoad::ResolveDependencyPlaceholder(FLinkerPlaceholderBase* PlaceholderIn, UClass* ReferencingClass, const FName ObjectPathIn)
1530{
1531#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1532 TGuardValue<uint32> LoadFlagsGuard(LoadFlags, (LoadFlags & ~LOAD_DeferDependencyLoads));
1533 ResolvingPlaceholderStack.Push(PlaceholderIn);
1534
1535 UObject* PlaceholderObj = PlaceholderIn->GetPlaceholderAsUObject();
1536#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1537 DEFERRED_DEPENDENCY_CHECK(PlaceholderObj != nullptr);
1538 DEFERRED_DEPENDENCY_CHECK(PlaceholderObj->GetOutermost() == LinkerRoot);
1539 const int32 ResolvingStackDepth = ResolvingPlaceholderStack.Num();
1540#endif
1541
1542 UObject* RealImportObj = nullptr;
1543
1544 FName ObjectPathName = NAME_None;
1545 if (PlaceholderIn->PackageIndex.IsNull())
1546 {
1547 ObjectPathName = ObjectPathIn;
1548 if (!ObjectPathIn.IsValid() || ObjectPathIn.IsNone())
1549 {
1550 const FName* ObjectPathPtr = ImportPlaceholders.FindKey(PlaceholderIn);
1551 DEFERRED_DEPENDENCY_CHECK(ObjectPathPtr != nullptr);
1552 if (ObjectPathPtr)
1553 {
1554 ObjectPathName = *ObjectPathPtr;
1555 }
1556 }
1557 DEFERRED_DEPENDENCY_CHECK(ObjectPathName.IsValid() && !ObjectPathName.IsNone());
1558
1559 // emulating the StaticLoadObject() call in UObjectPropertyBase::FindImportedObject(),
1560 // since this was most likely a placeholder
1561 RealImportObj = StaticLoadObject(UObject::StaticClass(), /*Outer =*/nullptr, *ObjectPathName.ToString(), /*Filename =*/nullptr, (LOAD_NoWarn | LOAD_FindIfFail));
1562 }
1563 else
1564 {
1565 DEFERRED_DEPENDENCY_CHECK(PlaceholderIn->PackageIndex.IsImport());
1566 int32 const ImportIndex = PlaceholderIn->PackageIndex.ToImport();
1567 FObjectImport& Import = ImportMap[ImportIndex];
1568
1569 if ((Import.XObject != nullptr) && (Import.XObject != PlaceholderObj))
1570 {
1571 DEFERRED_DEPENDENCY_CHECK(ResolvingPlaceholderStack.Num() > 0 && ResolvingPlaceholderStack.Top() == PlaceholderIn);
1572 DEFERRED_DEPENDENCY_CHECK(ResolvingPlaceholderStack.Num() == ResolvingStackDepth);
1573
1574 RealImportObj = Import.XObject;
1575 }
1576 else
1577 {
1578 // clear the placeholder from the import, so that a call to CreateImport()
1579 // properly fills it in
1580 Import.XObject = nullptr;
1581 // NOTE: this is a possible point of recursion... CreateImport() could
1582 // continue to load a package already started up the stack and you
1583 // could end up in another ResolveDependencyPlaceholder() for some
1584 // other placeholder before this one has completely finished resolving
1585 RealImportObj = CreateImport(ImportIndex);
1586 }
1587 }
1588
1589#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1590 UFunction* AsFunction = Cast<UFunction>(RealImportObj);
1591 UClass* FunctionOwner = (AsFunction != nullptr) ? AsFunction->GetOwnerClass() : nullptr;
1592 // it's ok if super functions come in not fully loaded (missing
1593 // RF_LoadCompleted... meaning it's in the middle of serializing in somewhere
1594 // up the stack); the function will be forcefully ran through Preload(),
1595 // when we regenerate the super class (see FRegenerationHelper::ForcedLoadMembers)
1596 bool const bIsSuperFunction = (AsFunction != nullptr) && (ReferencingClass != nullptr) && ReferencingClass->IsChildOf(FunctionOwner);
1597 // it's also possible that the loaded version of this function has been
1598 // thrown out and replaced with a regenerated version (presumably from a
1599 // blueprint compiling on load)... if that's the case, then this function
1600 // will not have a corresponding linker assigned to it
1601 bool const bIsRegeneratedFunc = (AsFunction != nullptr) && (AsFunction->GetLinker() == nullptr);
1602
1603 bool const bExpectsLoadCompleteFlag = (RealImportObj != nullptr) && !bIsSuperFunction && !bIsRegeneratedFunc;
1604 // if we can't rely on the Import object's RF_LoadCompleted flag, then its
1605 // owner class should at least have it
1606 DEFERRED_DEPENDENCY_CHECK( (RealImportObj == nullptr) || bExpectsLoadCompleteFlag ||
1607 (FunctionOwner && FunctionOwner->HasAnyFlags(RF_LoadCompleted | RF_Dynamic)) );
1608
1609 DEFERRED_DEPENDENCY_CHECK(RealImportObj != PlaceholderObj);
1610 DEFERRED_DEPENDENCY_CHECK(!bExpectsLoadCompleteFlag || RealImportObj->HasAnyFlags(RF_LoadCompleted | RF_Dynamic));
1611#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1612
1613 int32 ReplacementCount = 0;
1614 if (ReferencingClass != nullptr)
1615 {
1616 // @TODO: roll this into ULinkerPlaceholderClass's ResolveAllPlaceholderReferences()
1617 for (FImplementedInterface& Interface : ReferencingClass->Interfaces)
1618 {
1619 if (Interface.Class == PlaceholderObj)
1620 {
1621 ++ReplacementCount;
1622 Interface.Class = CastChecked<UClass>(RealImportObj, ECastCheckedType::NullAllowed);
1623 }
1624 }
1625 }
1626
1627 // make sure that we know what utilized this placeholder class... right now
1628 // we only expect UObjectProperties/UClassProperties/UInterfaceProperties/
1629 // FImplementedInterfaces to, but something else could have requested the
1630 // class without logging itself with the placeholder... if the placeholder
1631 // doesn't have any known references (and it hasn't already been resolved in
1632 // some recursive call), then there is something out there still using this
1633 // placeholder class
1634 DEFERRED_DEPENDENCY_CHECK( (ReplacementCount > 0) || PlaceholderIn->HasKnownReferences() || PlaceholderIn->HasBeenFullyResolved() );
1635
1636 ReplacementCount += PlaceholderIn->ResolveAllPlaceholderReferences(RealImportObj);
1637
1638#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1639 // @TODO: not an actual method, but would be nice to circumvent the need for bIsAsyncLoadRef below
1640 //FAsyncObjectsReferencer::Get().RemoveObject(PlaceholderObj);
1641
1642 // there should not be any references left to this placeholder class
1643 // (if there is, then we didn't log that referencer with the placeholder)
1644 FReferencerInformationList UnresolvedReferences;
1645 bool const bIsReferenced = false;// IsReferenced(PlaceholderObj, RF_NoFlags, /*bCheckSubObjects =*/false, &UnresolvedReferences);
1646
1647 // when we're running with async loading there may be an acceptable
1648 // reference left in FAsyncObjectsReferencer (which reports its refs
1649 // through FGCObject::GGCObjectReferencer)... since garbage collection can
1650 // be ran during async loading, FAsyncObjectsReferencer is in charge of
1651 // holding onto objects that are spawned during the process (to ensure
1652 // they're not thrown away prematurely)
1653 bool const bIsAsyncLoadRef = (UnresolvedReferences.ExternalReferences.Num() == 1) &&
1654 PlaceholderObj->HasAnyInternalFlags(EInternalObjectFlags::AsyncLoading) && (UnresolvedReferences.ExternalReferences[0].Referencer == FGCObject::GGCObjectReferencer);
1655
1656 DEFERRED_DEPENDENCY_CHECK(!bIsReferenced || bIsAsyncLoadRef);
1657#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1658
1659 // this could recurse back into ResolveDeferredDependencies(), which resolves all placeholders from this list,
1660 // so by the time we're returned here, the list may be empty
1661 if (ResolvingPlaceholderStack.Num() > 0)
1662 {
1663 DEFERRED_DEPENDENCY_CHECK(ResolvingPlaceholderStack.Top() == PlaceholderIn);
1664 DEFERRED_DEPENDENCY_CHECK(ResolvingPlaceholderStack.Num() == ResolvingStackDepth);
1665
1666 ResolvingPlaceholderStack.Pop();
1667 }
1668 ImportPlaceholders.Remove(ObjectPathName);
1669
1670 return ReplacementCount;
1671#else // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1672 return 0;
1673#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1674}
1675
1676void FLinkerLoad::PRIVATE_ForceLoadAllDependencies(UPackage* Package)
1677{
1678 if (FLinkerLoad* PkgLinker = FindExistingLinkerForPackage(Package))
1679 {
1680 PkgLinker->ResolveAllImports();
1681 }
1682}
1683
1684void FLinkerLoad::ResolveAllImports()
1685{
1686 for (int32 ImportIndex = 0; ImportIndex < ImportMap.Num() && IsBlueprintFinalizationPending(); ++ImportIndex)
1687 {
1688 // first, make sure every import object is available... just because
1689 // it isn't present in the map already, doesn't mean it isn't in the
1690 // middle of a resolve (the CreateImport() brings in an export
1691 // object from another package, which could be resolving itself)...
1692 //
1693 // don't fret, all these imports were bound to get created sooner or
1694 // later (like when the blueprint was regenerated)
1695 //
1696 // NOTE: this is a possible root point for recursion... accessing a
1697 // separate package could continue its loading process which
1698 // in turn, could end us back in this function before we ever
1699 // returned from this
1700 FObjectImport& Import = ImportMap[ImportIndex];
1701 UObject* ImportObject = CreateImport(ImportIndex);
1702
1703 // see if this import is currently being resolved (presumably somewhere
1704 // up the callstack)... if it is, we need to ensure that this dependency
1705 // is fully resolved before we get to regenerating the blueprint (else,
1706 // we could end up with placeholder classes in our script-code)
1707 if (FUnresolvedStructTracker::IsImportStructUnresolved(ImportObject))
1708 {
1709 // because it is tracked by FUnresolvedStructTracker, it must be a struct
1710 DEFERRED_DEPENDENCY_CHECK(Cast<UStruct>(ImportObject) != nullptr);
1711 FLinkerLoad* SourceLinker = FindExistingLinkerForImport(ImportIndex);
1712 if (SourceLinker)
1713 {
1714 SourceLinker->ResolveDeferredDependencies((UStruct*)ImportObject);
1715 }
1716 }
1717 }
1718}
1719
1720void FLinkerLoad::FinalizeBlueprint(UClass* LoadClass)
1721{
1722#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1723 if (!FBlueprintSupport::UseDeferredDependencyLoading())
1724 {
1725 return;
1726 }
1727 DEFERRED_DEPENDENCY_CHECK(LoadClass->HasAnyFlags(RF_LoadCompleted));
1728
1729 //--------------------------------------
1730 // Phase 3: Finalize (serialize CDO & regenerate class)
1731 //--------------------------------------
1732 TGuardValue<uint32> LoadFlagsGuard(LoadFlags, LoadFlags & ~LOAD_DeferDependencyLoads);
1733
1734 // we can get in a state where a sub-class is getting finalized here, before
1735 // its super-class has been "finalized" (like when the super's
1736 // ResolveDeferredDependencies() ends up Preloading a sub-class), so we
1737 // want to make sure that its properly finalized before this sub-class is
1738 // (so we can have a properly formed sub-class)
1739 if (UClass* SuperClass = LoadClass->GetSuperClass())
1740 {
1741 FLinkerLoad* SuperLinker = SuperClass->GetLinker();
1742 if ((SuperLinker != nullptr) && SuperLinker->IsBlueprintFinalizationPending())
1743 {
1744 DEFERRED_DEPENDENCY_CHECK(SuperLinker->DeferredCDOIndex != INDEX_NONE || SuperLinker->bForceBlueprintFinalization);
1745 UObject* SuperCDO = SuperLinker->DeferredCDOIndex != INDEX_NONE ? SuperLinker->ExportMap[SuperLinker->DeferredCDOIndex].Object : SuperClass->ClassDefaultObject;
1746 // we MUST have the super fully serialized before we can finalize
1747 // this (class and CDO); if the SuperCDO is already in the midst of
1748 // serializing somewhere up the stack (and a cyclic dependency has
1749 // landed us here, finalizing one of it's children), then it is
1750 // paramount that we force it through serialization (so we reset the
1751 // RF_NeedLoad guard, and leave it to ResolveDeferredExports, for it
1752 // to re-run the serialization)
1753 if ( (SuperCDO != nullptr) && !SuperCDO->HasAnyFlags(RF_NeedLoad|RF_LoadCompleted) )
1754 {
1755 check(!GEventDrivenLoaderEnabled || !EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME);
1756 SuperCDO->SetFlags(RF_NeedLoad);
1757 }
1758 SuperLinker->FinalizeBlueprint(SuperClass);
1759 }
1760 }
1761
1762 // at this point, we're sure that LoadClass doesn't contain any class
1763 // placeholders (because ResolveDeferredDependencies() was ran on it);
1764 // however, once we get to regenerating/compiling the blueprint, the graph
1765 // (nodes, pins, etc.) could bring in new dependencies that weren't part of
1766 // the class... this would normally be all fine and well, but in complicated
1767 // dependency chains those graph-dependencies could already be in the middle
1768 // of resolving themselves somewhere up the stack... if we just continue
1769 // along and let the blueprint compile, then it could end up with
1770 // placeholder refs in its script code (which it bad); we need to make sure
1771 // that all dependencies don't have any placeholder classes left in them
1772 //
1773 // we don't want this to be part of ResolveDeferredDependencies()
1774 // because we don't want this to count as a linker's "class resolution
1775 // phase"; at this point, it is ok if other blueprints compile with refs to
1776 // this LoadClass since it doesn't have any placeholders left in it (we also
1777 // don't want this linker externally claiming that it has resolving left to
1778 // do, otherwise other linkers could want to finish this off when they don't
1779 // have to)... we do however need it here in FinalizeBlueprint(), because
1780 // we need it ran for any super-classes before we regen
1781 ResolveAllImports();
1782
1783 // Now that imports have been resolved we optionally flush the compilation
1784 // queue. This is only done for level blueprints, which will have instances
1785 // of actors in them that cannot reliably be reinstanced on load (see useage
1786 // of Scene pointers in things like UActorComponent::ExecuteRegisterEvents)
1787 // - on load the Scene may not yet be created, meaning this code cannot
1788 // correctly be run. We could address that, but avoiding reinstancings is
1789 // also a performance win:
1790#if WITH_EDITOR
1791 LoadClass->FlushCompilationQueueForLevel();
1792#endif
1793
1794 // interfaces can invalidate classes which implement them (when the
1795 // interface is regenerated), they essentially define the makeup of the
1796 // implementing class; so here, like we do with the parent class above, we
1797 // ensure that all implemented interfaces are finalized first - this helps
1798 // avoid cyclic issues where an interface ends up invalidating a dependent
1799 // class by being regenerated after the class (see UE-28846)
1800 for (const FImplementedInterface& InterfaceDesc : LoadClass->Interfaces)
1801 {
1802 FLinkerLoad* InterfaceLinker = (InterfaceDesc.Class) ? InterfaceDesc.Class->GetLinker() : nullptr;
1803 if ((InterfaceLinker != nullptr) && InterfaceLinker->IsBlueprintFinalizationPending())
1804 {
1805#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1806 // the interface import should have been properly resolved above, in
1807 // ResolveAllImports()
1808 if (!ensure(!InterfaceLinker->HasUnresolvedDependencies()))
1809#else
1810 if (InterfaceLinker->HasUnresolvedDependencies())
1811#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1812 {
1813 InterfaceLinker->ResolveDeferredDependencies(InterfaceDesc.Class);
1814 }
1815 InterfaceLinker->FinalizeBlueprint(InterfaceDesc.Class);
1816 }
1817 }
1818
1819 // replace any export placeholders that were created, and serialize in the
1820 // class's CDO
1821 ResolveDeferredExports(LoadClass);
1822
1823 // the above calls (ResolveAllImports(), ResolveDeferredExports(), etc.)
1824 // could have caused some recursion... if it ended up finalizing a sub-class
1825 // (of LoadClass), then that would have finalized this as well; so, before
1826 // we continue, make sure that this didn't already get fully executed in
1827 // some nested call
1828 if (IsBlueprintFinalizationPending())
1829 {
1830 int32 DeferredCDOIndexCopy = DeferredCDOIndex;
1831 UObject* CDO = DeferredCDOIndex != INDEX_NONE ? ExportMap[DeferredCDOIndexCopy].Object : LoadClass->ClassDefaultObject;
1832 // clear this so IsBlueprintFinalizationPending() doesn't report true:
1833 FLinkerLoad::bForceBlueprintFinalization = false;
1834 // clear this because we're processing this CDO now:
1835 DeferredCDOIndex = INDEX_NONE;
1836
1837#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1838 // at this point there should not be any instances of the Blueprint
1839 // (else, we'd have to re-instance and that is too expensive an
1840 // operation to have at load time)
1841 TArray<UObject*> ClassInstances;
1842 GetObjectsOfClass(LoadClass, ClassInstances, /*bIncludeDerivedClasses =*/true);
1843
1844 // Filter out instances that are part of this package, they were handled in ResolveDeferredExports:
1845 ClassInstances.RemoveAllSwap([LoadClass](UObject* Obj){return Obj->GetOutermost() == LoadClass->GetOutermost();});
1846
1847 for (UObject* ClassInst : ClassInstances)
1848 {
1849 // in the case that we do end up with instances, use this to find
1850 // where they are referenced (to help sleuth out when/where they
1851 // were created)
1852 FReferencerInformationList InstanceReferences;
1853 bool const bIsReferenced = false;// IsReferenced(ClassInst, RF_NoFlags, /*bCheckSubObjects =*/false, &InstanceReferences);
1854 DEFERRED_DEPENDENCY_CHECK(!bIsReferenced);
1855 }
1856 DEFERRED_DEPENDENCY_CHECK(ClassInstances.Num() == 0);
1857
1858 UClass* BlueprintClass = DeferredCDOIndexCopy != INDEX_NONE ? Cast<UClass>(IndexToObject(ExportMap[DeferredCDOIndexCopy].ClassIndex)) : LoadClass;
1859 DEFERRED_DEPENDENCY_CHECK(BlueprintClass == LoadClass);
1860 DEFERRED_DEPENDENCY_CHECK(BlueprintClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint));
1861#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1862
1863 // for cooked builds (we skip script serialization for editor builds),
1864 // certain scripts can contain references to external dependencies; and
1865 // since the script is serialized in with the class (functions) we want
1866 // those dependencies deferred until now (we expect this to be the right
1867 // spot, because in editor builds, with RegenerateBlueprintClass(), this
1868 // is where script code is regenerated)
1869 FStructScriptLoader::ResolveDeferredScriptLoads(this);
1870
1871 DEFERRED_DEPENDENCY_CHECK(ImportPlaceholders.Num() == 0);
1872 DEFERRED_DEPENDENCY_CHECK(LoadClass->GetOutermost() != GetTransientPackage());
1873 // just in case we choose to enable the deferred dependency loading for
1874 // cooked builds... we want to keep from regenerating in that scenario
1875 if (!LoadClass->bCooked)
1876 {
1877 UObject* OldCDO = LoadClass->ClassDefaultObject;
1878 if (RegenerateBlueprintClass(LoadClass, CDO))
1879 {
1880 // emulate class CDO serialization (RegenerateBlueprintClass() could
1881 // have a side-effect where it overwrites the class's CDO; so we
1882 // want to make sure that we don't overwrite that new CDO with a
1883 // stale one)
1884 if (OldCDO == LoadClass->ClassDefaultObject)
1885 {
1886 LoadClass->ClassDefaultObject = CDO;
1887 }
1888 }
1889 }
1890 }
1891#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1892}
1893
1894void FLinkerLoad::ResolveDeferredExports(UClass* LoadClass)
1895{
1896#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
1897 if (!IsBlueprintFinalizationPending())
1898 {
1899 return;
1900 }
1901
1902 DEFERRED_DEPENDENCY_CHECK(DeferredCDOIndex != INDEX_NONE || bForceBlueprintFinalization);
1903
1904 UObject* BlueprintCDO = DeferredCDOIndex != INDEX_NONE ? ExportMap[DeferredCDOIndex].Object : LoadClass->ClassDefaultObject;
1905 DEFERRED_DEPENDENCY_CHECK(BlueprintCDO != nullptr);
1906
1907 TArray<int32> DeferredTemplateObjects;
1908
1909 if (!FBlueprintSupport::IsDeferredExportCreationDisabled())
1910 {
1911#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1912 auto IsPlaceholderReferenced = [](ULinkerPlaceholderExportObject* ExportPlaceholder)->bool
1913 {
1914 UObject* PlaceholderObj = ExportPlaceholder;
1915
1916 FReferencerInformationList UnresolvedReferences;
1917 bool bIsReferenced = IsReferenced(PlaceholderObj, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, /*bCheckSubObjects =*/false, &UnresolvedReferences);
1918
1919 if (bIsReferenced && IsAsyncLoading())
1920 {
1921 // if we're async loading, then we assume a single external
1922 // reference belongs to FAsyncObjectsReferencer, which is allowable
1923 bIsReferenced = (UnresolvedReferences.ExternalReferences.Num() != 1) || (UnresolvedReferences.InternalReferences.Num() > 0);
1924 }
1925 return bIsReferenced;
1926 };
1927#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1928
1929 // we may have circumvented an export creation or two to avoid instantiating
1930 // an BP object before its class has been finalized (to avoid costly re-
1931 // instancing operations when the class ultimately finalizes)... so here, we
1932 // find those skipped exports and properly create them (before we finalize
1933 // our own class)
1934
1935 // Mark this linker as ResolvingDeferredExports so that we don't continue deferring exports
1936 // we clear this flag after the loop. We have no TGuardValue for flags and so I'm setting
1937 // and clearing the bit manually:
1938 LoadFlags |= LOAD_ResolvingDeferredExports;
1939
1940 for (int32 ExportIndex = 0; ExportIndex < ExportMap.Num() && IsBlueprintFinalizationPending(); ++ExportIndex)
1941 {
1942 FObjectExport& Export = ExportMap[ExportIndex];
1943 if (ULinkerPlaceholderExportObject* PlaceholderExport = Cast<ULinkerPlaceholderExportObject>(Export.Object))
1944 {
1945 if(Export.ClassIndex.IsExport())
1946 {
1947 DeferredTemplateObjects.Push(ExportIndex);
1948 continue;
1949 }
1950
1951 if(PlaceholderExport->IsDeferredSubobject())
1952 {
1953 continue;
1954 }
1955
1956 UClass* ExportClass = GetExportLoadClass(ExportIndex);
1957 // export class could be null... we create these placeholder
1958 // exports for objects that are instances of an external
1959 // (Blueprint) type, not knowing if that type (class) will
1960 // successfully load... it may resolve to null in scenarios
1961 // where its super class has been deleted, or its super belonged
1962 // to a plugin that has been removed/disabled
1963 if (ExportClass != nullptr)
1964 {
1965#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1966 DEFERRED_DEPENDENCY_CHECK(!ExportClass->HasAnyClassFlags(CLASS_Intrinsic) && ExportClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint));
1967 FLinkerLoad* ClassLinker = ExportClass->GetLinker();
1968 DEFERRED_DEPENDENCY_CHECK((ClassLinker != nullptr) && (ClassLinker != this));
1969#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
1970
1971 FScopedResolvingExportTracker ForceRegenGuard(this, ExportIndex);
1972 // make sure this export's class is fully regenerated before
1973 // we instantiate it (so we don't have to re-inst on load)
1974 ForceRegenerateClass(ExportClass);
1975
1976 if (PlaceholderExport != Export.Object)
1977 {
1978 DEFERRED_DEPENDENCY_CHECK( !IsPlaceholderReferenced(PlaceholderExport) );
1979 continue;
1980 }
1981 }
1982
1983 // replace the placeholder with the proper object instance
1984 PlaceholderExport->SetLinker(nullptr, INDEX_NONE);
1985 Export.ResetObject();
1986 UObject* ExportObj = CreateExport(ExportIndex);
1987
1988 // NOTE: we don't count how many references were resolved (and
1989 // assert on it), because this could have only been created as
1990 // part of the LoadAllObjects() pass (not for any specific
1991 // container object).
1992 PlaceholderExport->ResolveAllPlaceholderReferences(ExportObj);
1993
1994 ResolvedDeferredSubobjects(PlaceholderExport);
1995
1996 PlaceholderExport->MarkPendingKill();
1997
1998 // if we hadn't used a ULinkerPlaceholderExportObject in place of
1999 // the expected export, then someone may have wanted it preloaded
2000 if (ExportObj != nullptr)
2001 {
2002 Preload(ExportObj);
2003 }
2004 DEFERRED_DEPENDENCY_CHECK( !IsPlaceholderReferenced(PlaceholderExport) );
2005 }
2006 }
2007
2008 LoadFlags &= ~LOAD_ResolvingDeferredExports;
2009 }
2010
2011#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
2012 // this helps catch any placeholder export objects that may be created
2013 // between now and when DeferredCDOIndex is cleared (they won't be resolved,
2014 // so that is a problem!)
2015 FResolvingExportTracker::Get().FlagFullExportResolvePassComplete(this);
2016#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
2017
2018 // the ExportMap loop above could have recursed back into "finalization" for
2019 // this asset, subsequently resolving all exports before this function could
2020 // finish... that means there's no work left for this to do (and trying to
2021 // redo the work would cause a crash), so we guard here against that
2022 if (IsBlueprintFinalizationPending())
2023 {
2024 // have to prematurely set the CDO's linker so we can force a Preload()/
2025 // Serialization of the CDO before we regenerate the Blueprint class
2026 {
2027 if (DeferredCDOIndex != INDEX_NONE)
2028 {
2029 const EObjectFlags OldFlags = BlueprintCDO->GetFlags();
2030 BlueprintCDO->ClearFlags(RF_NeedLoad | RF_NeedPostLoad);
2031 BlueprintCDO->SetLinker(this, DeferredCDOIndex, /*bShouldDetatchExisting =*/false);
2032 BlueprintCDO->SetFlags(OldFlags);
2033 }
2034 }
2035 DEFERRED_DEPENDENCY_CHECK(BlueprintCDO->GetClass() == LoadClass);
2036
2037 // should load the CDO (ensuring that it has been serialized in by the
2038 // time we get to class regeneration)
2039 //
2040 // NOTE: this is point where circular dependencies could reveal
2041 // themselves, as the CDO could depend on a class not listed in
2042 // the package's imports
2043 //
2044 // NOTE: how we don't guard against re-entrant behavior... if the CDO
2045 // has already been "finalized", then its RF_NeedLoad flag would
2046 // be cleared (and this will do nothing the 2nd time around)
2047 Preload(BlueprintCDO);
2048
2049 // Ensure that all default subobject exports belonging to the CDO have been created. DSOs may no longer be
2050 // referenced by a tagged property and thus may not get created and registered until after class regeneration.
2051 // This can cause invalid subobjects to register themselves with a regenerated CDO if the native parent class
2052 // has been changed to inherit from an entirely different type since the last time the class asset was saved.
2053 // By constructing them here, we make sure that LoadAllObjects() won't construct them after class regeneration.
2054 for (int32 ExportIndex = 0; ExportIndex < ExportMap.Num(); ++ExportIndex)
2055 {
2056 FObjectExport& Export = ExportMap[ExportIndex];
2057 if((Export.ObjectFlags & RF_DefaultSubObject) != 0 && Export.OuterIndex.ToExport() == DeferredCDOIndex)
2058 {
2059 if (Export.Object == nullptr && Export.OuterIndex.IsExport())
2060 {
2061 CreateExport(ExportIndex);
2062 }
2063
2064 // In order to complete loading of the CDO we need to also preload its subobjects. Other CDOs
2065 // will use these subobjects as archetypes for their own subobjects when they run InitSubobjectProperties
2066 if(Export.Object)
2067 {
2068 Preload(Export.Object);
2069 }
2070 }
2071 }
2072
2073 {
2074 // Create any objects that (non CDO) objects that were deferred in this package:
2075 TGuardValue<int32> ClearDeferredCDOToPreventDeferExportCreation(DeferredCDOIndex, INDEX_NONE);
2076 for(int32 ExportIndex : DeferredTemplateObjects)
2077 {
2078 FObjectExport& Export = ExportMap[ExportIndex];
2079 ULinkerPlaceholderExportObject* PlaceholderExport = Cast<ULinkerPlaceholderExportObject>(Export.Object);
2080 if (ensure(PlaceholderExport))
2081 {
2082 // replace the placeholder with the proper object instance
2083 PlaceholderExport->SetLinker(nullptr, INDEX_NONE);
2084 Export.ResetObject();
2085 UObject* ExportObj = CreateExport(ExportIndex);
2086
2087 PlaceholderExport->ResolveAllPlaceholderReferences(ExportObj);
2088 ResolvedDeferredSubobjects(PlaceholderExport);
2089
2090 PlaceholderExport->MarkPendingKill();
2091 if (ExportObj != nullptr)
2092 {
2093 Preload(ExportObj);
2094 }
2095 }
2096 }
2097 }
2098
2099 // sub-classes of this Blueprint could have had their CDO's
2100 // initialization deferred (this occurs when the sub-class CDO is
2101 // created before this super CDO has been fully serialized; we do this
2102 // because the sub-class's CDO would not have been initialized with
2103 // accurate values)
2104 //
2105 // in that case, the sub-class CDOs are waiting around until their
2106 // super CDO is fully loaded (which is now)... we want to do this here,
2107 // before this (super) Blueprint gets regenerated, because after it's
2108 // regenerated the class layout (and property offsets) may no longer
2109 // match the layout that sub-class CDOs were constructed with (making
2110 // property copying dangerous)
2111 FDeferredObjInitializationHelper::ResolveDeferredInitsFromArchetype(BlueprintCDO);
2112
2113 DEFERRED_DEPENDENCY_CHECK(BlueprintCDO->HasAnyFlags(RF_LoadCompleted));
2114 }
2115#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2116}
2117
2118void FLinkerLoad::ResolvePlaceholder(ULinkerPlaceholderExportObject* Placeholder)
2119{
2120 int32 ExportIndex = Placeholder->PackageIndex.ToExport();
2121
2122 Placeholder->SetLinker(nullptr, INDEX_NONE);
2123
2124 FObjectExport& Export = ExportMap[ExportIndex];
2125 Export.Object = nullptr;
2126
2127 UObject* ReplacementObject = CreateExport(ExportIndex);
2128 Placeholder->ResolveAllPlaceholderReferences(ReplacementObject);
2129 Placeholder->MarkPendingKill();
2130
2131 // recurse:
2132 ResolvedDeferredSubobjects(Placeholder);
2133
2134 // attempt to preload, we don't really care if this doesn't complete but we don't want to fail
2135 // to serialize an object:
2136 if (ReplacementObject != nullptr)
2137 {
2138 Preload(ReplacementObject);
2139 }
2140}
2141
2142void FLinkerLoad::ResolvedDeferredSubobjects(ULinkerPlaceholderExportObject* OwningPlaceholder)
2143{
2144#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2145 ensure(OwningPlaceholder->IsMarkedResolved());
2146 for(ULinkerPlaceholderExportObject* PlaceholderSubobject : OwningPlaceholder->GetSubobjectPlaceholders() )
2147 {
2148 int32 ExportIndex = PlaceholderSubobject->PackageIndex.ToExport();
2149
2150 PlaceholderSubobject->SetLinker(nullptr, INDEX_NONE);
2151
2152 FObjectExport& Export = ExportMap[ExportIndex];
2153
2154 Export.ResetObject();
2155
2156 UObject* ReplacementObject = CreateExport(ExportIndex);
2157 PlaceholderSubobject->ResolveAllPlaceholderReferences(ReplacementObject);
2158 PlaceholderSubobject->MarkPendingKill();
2159
2160 // recurse:
2161 ResolvedDeferredSubobjects(PlaceholderSubobject);
2162
2163 // serialize:
2164 if (ReplacementObject != nullptr)
2165 {
2166 Preload(ReplacementObject);
2167 }
2168 }
2169#endif//USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2170}
2171
2172void FLinkerLoad::ForceBlueprintFinalization()
2173{
2174#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2175 check(!bForceBlueprintFinalization);
2176 bForceBlueprintFinalization = true;
2177#endif
2178}
2179
2180bool FLinkerLoad::IsBlueprintFinalizationPending() const
2181{
2182#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2183 return (DeferredCDOIndex != INDEX_NONE) || bForceBlueprintFinalization;
2184#else // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2185 return false;
2186#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2187}
2188
2189bool FLinkerLoad::ForceRegenerateClass(UClass* ImportClass)
2190{
2191#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2192 if (FLinkerLoad* ClassLinker = ImportClass->GetLinker())
2193 {
2194 //
2195 // BE VERY CAREFUL with this! if these following statements are called
2196 // in the wrong place, we could end up infinitely recursing
2197
2198 Preload(ImportClass);
2199 DEFERRED_DEPENDENCY_CHECK(ImportClass->HasAnyFlags(RF_LoadCompleted));
2200
2201 if (ClassLinker->HasUnresolvedDependencies())
2202 {
2203 ClassLinker->ResolveDeferredDependencies(ImportClass);
2204 }
2205 if (ClassLinker->IsBlueprintFinalizationPending())
2206 {
2207 ClassLinker->FinalizeBlueprint(ImportClass);
2208 }
2209 return true;
2210 }
2211#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2212 return false;
2213}
2214
2215bool FLinkerLoad::IsExportBeingResolved(int32 ExportIndex)
2216{
2217 FObjectExport& Export = ExportMap[ExportIndex];
2218 bool bIsExportClassBeingForceRegened = FResolvingExportTracker::Get().IsLinkerExportBeingResolved(this, ExportIndex);
2219
2220 FPackageIndex OuterIndex = Export.OuterIndex;
2221 // since child exports require their outers be set upon creation, then those
2222 // too count as being "resolved"... so here we check this export's outers too
2223 while (!bIsExportClassBeingForceRegened && !OuterIndex.IsNull())
2224 {
2225 DEFERRED_DEPENDENCY_CHECK(OuterIndex.IsExport());
2226 int32 OuterExportIndex = OuterIndex.ToExport();
2227
2228 if (OuterExportIndex != INDEX_NONE)
2229 {
2230 FObjectExport& OuterExport = ExportMap[OuterExportIndex];
2231 bIsExportClassBeingForceRegened |= FResolvingExportTracker::Get().IsLinkerExportBeingResolved(this, OuterExportIndex);
2232
2233 OuterIndex = OuterExport.OuterIndex;
2234 }
2235 else
2236 {
2237 break;
2238 }
2239 }
2240 return bIsExportClassBeingForceRegened;
2241}
2242
2243void FLinkerLoad::ResetDeferredLoadingState()
2244{
2245#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2246 DeferredCDOIndex = INDEX_NONE;
2247 bForceBlueprintFinalization = false;
2248 ResolvingPlaceholderStack.Empty();
2249 ImportPlaceholders.Empty();
2250 LoadFlags &= ~(LOAD_DeferDependencyLoads);
2251
2252 FResolvingExportTracker::Get().Reset(this);
2253 FUnresolvedStructTracker::Reset(this);
2254#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2255}
2256
2257bool FLinkerLoad::HasPerformedFullExportResolvePass()
2258{
2259#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
2260 return FResolvingExportTracker::Get().HasPerformedFullExportResolvePass(this);
2261#else
2262 return false;
2263#endif
2264
2265}
2266
2267UObject* FLinkerLoad::RequestPlaceholderValue(UClass* ObjectType, const TCHAR* ObjectPath)
2268{
2269#if !USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2270 return nullptr;
2271#else
2272 FLinkerPlaceholderBase* Placeholder = nullptr;
2273
2274 if (FBlueprintSupport::UseDeferredDependencyLoading() && (LoadFlags & LOAD_DeferDependencyLoads))
2275 {
2276 const FName ObjId(ObjectPath);
2277 if (FLinkerPlaceholderBase** PlaceholderPtr = ImportPlaceholders.Find(ObjId))
2278 {
2279 Placeholder = *PlaceholderPtr;
2280 }
2281 // right now we only support external parties requesting CLASS placeholders;
2282 // if there is a scenario where they're, through a different ObjectType,
2283 // loading another Blueprint package when they shouldn't, then we need to
2284 // handle that here as well
2285 else if (ObjectType->IsChildOf<UClass>())
2286 {
2287 const FString ObjectPathStr(ObjectPath);
2288 // we don't need placeholders for native object references (the
2289 // calling code should properly handle null return values)
2290 if (!FPackageName::IsScriptPackage(ObjectPathStr))
2291 {
2292 const FString ObjectName = FPackageName::ObjectPathToObjectName(ObjectPathStr);
2293 Placeholder = MakeImportPlaceholder<ULinkerPlaceholderClass>(LinkerRoot, *ObjectName);
2294 ImportPlaceholders.Add(ObjId, Placeholder);
2295 }
2296 }
2297 }
2298
2299 return Placeholder ? Placeholder->GetPlaceholderAsUObject() : nullptr;
2300#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
2301}
2302
2303#if WITH_EDITORONLY_DATA
2304extern int32 GLinkerAllowDynamicClasses;
2305#endif
2306
2307UObject* FLinkerLoad::FindImport(UClass* ImportClass, UObject* ImportOuter, const TCHAR* Name)
2308{
2309 UObject* Result = StaticFindObject(ImportClass, ImportOuter, Name);
2310#if WITH_EDITORONLY_DATA
2311 static FName NAME_BlueprintGeneratedClass(TEXT("BlueprintGeneratedClass"));
2312 if (GLinkerAllowDynamicClasses && !Result && ImportClass->GetFName() == NAME_BlueprintGeneratedClass)
2313 {
2314 Result = StaticFindObject(UDynamicClass::StaticClass(), ImportOuter, Name);
2315 }
2316#endif
2317 return Result;
2318}
2319
2320UObject* FLinkerLoad::FindImportFast(UClass* ImportClass, UObject* ImportOuter, FName Name)
2321{
2322 UObject* Result = StaticFindObjectFast(ImportClass, ImportOuter, Name);
2323#if WITH_EDITORONLY_DATA
2324 static FName NAME_BlueprintGeneratedClass(TEXT("BlueprintGeneratedClass"));
2325 if (GLinkerAllowDynamicClasses && !Result && ImportClass->GetFName() == NAME_BlueprintGeneratedClass)
2326 {
2327 Result = StaticFindObjectFast(UDynamicClass::StaticClass(), ImportOuter, Name);
2328 }
2329#endif
2330 return Result;
2331}
2332
2333void FLinkerLoad::CreateDynamicTypeLoader()
2334{
2335 // In this case we can skip serializing PackageFileSummary and fill all the required info here
2336 bHasSerializedPackageFileSummary = true;
2337
2338 // Try to get dependencies for dynamic classes
2339 TArray<FBlueprintDependencyData> DependencyData;
2340 FConvertedBlueprintsDependencies::Get().GetAssets(LinkerRoot->GetFName(), DependencyData);
2341 if (!IsEventDrivenLoaderEnabled())
2342 {
2343 DependencyData.RemoveAll([=](const FBlueprintDependencyData& InData) -> bool
2344 {
2345 return InData.ObjectRef.PackageName == LinkerRoot->GetFName();
2346 });
2347 }
2348
2349 const FName DynamicClassName = UDynamicClass::StaticClass()->GetFName();
2350 const FName DynamicClassPackageName = UDynamicClass::StaticClass()->GetOuterUPackage()->GetFName();
2351
2352 ensure(!ImportMap.Num());
2353
2354 // Create Imports
2355 for (int32 DependencyIndex = 0; DependencyIndex < DependencyData.Num(); ++DependencyIndex)
2356 {
2357 FBlueprintDependencyData& Import = DependencyData[DependencyIndex];
2358
2359 FObjectImport* ObjectImport = new(ImportMap)FObjectImport(nullptr);
2360 ObjectImport->ClassName = Import.ObjectRef.ClassName;
2361 ObjectImport->ClassPackage = Import.ObjectRef.ClassPackageName;
2362 ObjectImport->ObjectName = Import.ObjectRef.ObjectName;
2363
2364 if(Import.ObjectRef.OuterName == NAME_None)
2365 {
2366 ObjectImport->OuterIndex = FPackageIndex::FromImport(ImportMap.Num());
2367 }
2368 else
2369 {
2370 // A subobject - look for our outer in the previously setup imports. Iterate backwards here as it will usually be found in a few iterations
2371 for(int32 OuterSearchIndex = ImportMap.Num() - 2; OuterSearchIndex >= 0; --OuterSearchIndex)
2372 {
2373 FObjectImport& SearchImport = ImportMap[OuterSearchIndex];
2374 if(SearchImport.ObjectName == Import.ObjectRef.OuterName)
2375 {
2376 ObjectImport->OuterIndex = FPackageIndex::FromImport(OuterSearchIndex);
2377 break;
2378 }
2379 }
2380
2381 // We must find out outer in the above search or the import table will be invalid
2382 check(!ObjectImport->OuterIndex.IsNull());
2383 }
2384
2385 FObjectImport* OuterImport = new(ImportMap)FObjectImport(nullptr);
2386 OuterImport->ClassName = NAME_Package;
2387 OuterImport->ClassPackage = GLongCoreUObjectPackageName;
2388 OuterImport->ObjectName = Import.ObjectRef.PackageName;
2389
2390 if ((Import.ObjectRef.ClassName == DynamicClassName)
2391 && (!GEventDrivenLoaderEnabled || !EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME)
2392 && (Import.ObjectRef.ClassPackageName == DynamicClassPackageName))
2393 {
2394 const FString DynamicClassPath = Import.ObjectRef.PackageName.ToString() + TEXT(".") + Import.ObjectRef.ObjectName.ToString();
2395 const FName DynamicClassPathName(*DynamicClassPath);
2396 FDynamicClassStaticData* ClassConstructFn = GetDynamicClassMap().Find(DynamicClassPathName);
2397 if (ensure(ClassConstructFn))
2398 {
2399 // The class object is created here. The class is not fully constructed yet (no CLASS_Constructed flag), ZConstructor will do that later.
2400 // The class object is needed to resolve circular dependencies. Regular native classes use deferred initialization/registration to avoid them.
2401
2402 ClassConstructFn->StaticClassFn();
2403
2404 //We don't fill the ObjectImport->XObject and OuterImport->XObject, because the class still must be created as export.
2405 }
2406 }
2407 }
2408
2409 // Create Export
2410 const int32 DynamicTypeExportIndex = ExportMap.Num();
2411 FObjectExport* const DynamicTypeExport = new (ExportMap)FObjectExport();
2412 {
2413 const FName* TypeNamePtr = GetConvertedDynamicPackageNameToTypeName().Find(LinkerRoot->GetFName());
2414 DynamicTypeExport->ObjectName = TypeNamePtr ? *TypeNamePtr : NAME_None;
2415 DynamicTypeExport->ThisIndex = FPackageIndex::FromExport(DynamicTypeExportIndex);
2416 // This allows us to skip creating two additional imports for UDynamicClass and its package
2417 DynamicTypeExport->DynamicType = FObjectExport::EDynamicType::DynamicType;
2418 DynamicTypeExport->ObjectFlags |= RF_Public;
2419 }
2420
2421 if (GEventDrivenLoaderEnabled)
2422 {
2423 const FString DynamicTypePath = GetExportPathName(DynamicTypeExportIndex);
2424 const FName DynamicTypeClassName = GetDynamicTypeClassName(*DynamicTypePath);
2425 if (DynamicTypeClassName == NAME_None)
2426 {
2427 UE_LOG(LogTemp, Error, TEXT("Exports %d, DynamicTypePath %s, Export Name %s, Package Root %s"), ExportMap.Num(), *DynamicTypePath, *DynamicTypeExport->ObjectName.ToString(), *LinkerRoot->GetPathName());
2428 }
2429 ensure(DynamicTypeClassName != NAME_None);
2430 const bool bIsDynamicClass = DynamicTypeClassName == DynamicClassName;
2431 const bool bIsDynamicStruct = DynamicTypeClassName == UScriptStruct::StaticClass()->GetFName();
2432
2433 if(bIsDynamicClass || bIsDynamicStruct)
2434 {
2435 FObjectExport* const CDOExport = bIsDynamicClass ? (new (ExportMap)FObjectExport()) : nullptr;
2436 if(CDOExport)
2437 {
2438 const FString CDOName = FString(DEFAULT_OBJECT_PREFIX) + DynamicTypeExport->ObjectName.ToString();
2439 CDOExport->ObjectName = *CDOName;
2440 CDOExport->ThisIndex = FPackageIndex::FromExport(ExportMap.Num() - 1);
2441 CDOExport->DynamicType = FObjectExport::EDynamicType::ClassDefaultObject;
2442 CDOExport->ObjectFlags |= RF_Public | RF_ClassDefaultObject; //?
2443 CDOExport->ClassIndex = DynamicTypeExport->ThisIndex;
2444 }
2445
2446 // Note, the layout of the fake export table is assumed elsewhere
2447 //check(ImportLinker->ExportMap.Num() == 2); // we assume there are two elements in the fake export table and the second one is the CDO
2448 //LocalExportIndex = FPackageIndex::FromExport(1);
2449
2450
2451 FObjectExport* const FakeExports[] = { DynamicTypeExport , CDOExport }; // must be sync'ed with FBlueprintDependencyData::DependencyTypes
2452 int32 RunningIndex = 0;
2453 for(int32 LocExportIndex = 0; LocExportIndex < (sizeof(FakeExports)/sizeof(FakeExports[0])); LocExportIndex++)
2454 {
2455 FObjectExport* const Export = FakeExports[LocExportIndex];
2456 if (!Export)
2457 {
2458 continue;
2459 }
2460 Export->FirstExportDependency = RunningIndex;
2461
2462 enum class EDependencyType : uint8
2463 {
2464 SerializationBeforeSerialization,
2465 CreateBeforeSerialization,
2466 SerializationBeforeCreate,
2467 CreateBeforeCreate,
2468 };
2469
2470 auto HandleDependencyTypeForExport = [&](EDependencyType InDependencyType)
2471 {
2472 for (int32 DependencyDataIndex = 0; DependencyDataIndex < DependencyData.Num(); DependencyDataIndex++)
2473 {
2474 const FBlueprintDependencyData& Import = DependencyData[DependencyDataIndex];
2475 const FBlueprintDependencyType DependencyType = Import.DependencyTypes[LocExportIndex];
2476 auto IsMatchingDependencyType = [](FBlueprintDependencyType InDependencyTypeStruct, EDependencyType InDependencyTypeLoc) -> bool
2477 {
2478 switch (InDependencyTypeLoc)
2479 {
2480 case EDependencyType::SerializationBeforeSerialization:
2481 return InDependencyTypeStruct.bSerializationBeforeSerializationDependency;
2482 case EDependencyType::CreateBeforeSerialization:
2483 return InDependencyTypeStruct.bCreateBeforeSerializationDependency;
2484 case EDependencyType::SerializationBeforeCreate:
2485 return InDependencyTypeStruct.bSerializationBeforeCreateDependency;
2486 case EDependencyType::CreateBeforeCreate:
2487 return InDependencyTypeStruct.bCreateBeforeCreateDependency;
2488 }
2489 check(false);
2490 return false;
2491 };
2492 if (IsMatchingDependencyType(DependencyType, InDependencyType))
2493 {
2494 auto IncreaseDependencyTypeInExport = [](FObjectExport* InExport, EDependencyType InDependencyTypeLoc)
2495 {
2496 check(InExport);
2497 switch (InDependencyTypeLoc)
2498 {
2499 case EDependencyType::SerializationBeforeSerialization:
2500 InExport->SerializationBeforeSerializationDependencies++;
2501 break;
2502 case EDependencyType::CreateBeforeSerialization:
2503 InExport->CreateBeforeSerializationDependencies++;
2504 break;
2505 case EDependencyType::SerializationBeforeCreate:
2506 InExport->SerializationBeforeCreateDependencies++;
2507 break;
2508 case EDependencyType::CreateBeforeCreate:
2509 InExport->CreateBeforeCreateDependencies++;
2510 break;
2511 }
2512 };
2513 IncreaseDependencyTypeInExport(Export, InDependencyType);
2514
2515 auto IndexInDependencyDataToImportIndex = [](int32 ArrayIndex) -> int32 { return ArrayIndex * 2; };
2516 const int32 ImportIndex = IndexInDependencyDataToImportIndex(DependencyDataIndex);
2517 PreloadDependencies.Add(FPackageIndex::FromImport(ImportIndex));
2518 RunningIndex++;
2519 }
2520 }
2521 };
2522
2523 // the order of Packages in PreloadDependencie must match FAsyncPackage::SetupExports_Event
2524
2525 HandleDependencyTypeForExport(EDependencyType::SerializationBeforeSerialization);
2526 HandleDependencyTypeForExport(EDependencyType::CreateBeforeSerialization);
2527
2528 if (bIsDynamicClass && (Export == CDOExport))
2529 {
2530 // Add a serializebeforecreate arc from the class on the CDO. That will force us to finish the class before we create the CDO....
2531 // and that will make sure that we load the class before we serialize things that reference the CDO.
2532 Export->SerializationBeforeCreateDependencies++;
2533 PreloadDependencies.Add(DynamicTypeExport->ThisIndex);
2534 RunningIndex++;
2535 }
2536
2537 HandleDependencyTypeForExport(EDependencyType::SerializationBeforeCreate);
2538 HandleDependencyTypeForExport(EDependencyType::CreateBeforeCreate);
2539 }
2540 }
2541 }
2542
2543 LinkerRoot->SetPackageFlags(LinkerRoot->GetPackageFlags() | PKG_CompiledIn);
2544}
2545
2546/*******************************************************************************
2547 * UObject
2548 ******************************************************************************/
2549
2550/**
2551 * Returns whether this object is contained in or part of a blueprint object
2552 */
2553bool UObject::IsInBlueprint() const
2554{
2555 // Exclude blueprint classes as they may be regenerated at any time
2556 // Need to exclude classes, CDOs, and their subobjects
2557 const UObject* TestObject = this;
2558 while (TestObject)
2559 {
2560 const UClass *ClassObject = dynamic_cast<const UClass*>(TestObject);
2561 if (ClassObject
2562 && ClassObject->HasAnyClassFlags(CLASS_CompiledFromBlueprint)
2563 && ClassObject->ClassGeneratedBy)
2564 {
2565 return true;
2566 }
2567 else if (TestObject->HasAnyFlags(RF_ClassDefaultObject)
2568 && TestObject->GetClass()
2569 && TestObject->GetClass()->HasAnyClassFlags(CLASS_CompiledFromBlueprint)
2570 && TestObject->GetClass()->ClassGeneratedBy)
2571 {
2572 return true;
2573 }
2574 TestObject = TestObject->GetOuter();
2575 }
2576
2577 return false;
2578}
2579
2580/**
2581 * Destroy properties that won't be destroyed by the native destructor
2582 */
2583void UObject::DestroyNonNativeProperties()
2584{
2585 // Destroy properties that won't be destroyed by the native destructor
2586#if USE_UBER_GRAPH_PERSISTENT_FRAME
2587 GetClass()->DestroyPersistentUberGraphFrame(this);
2588#endif
2589 {
2590 for (UProperty* P = GetClass()->DestructorLink; P; P = P->DestructorLinkNext)
2591 {
2592 P->DestroyValue_InContainer(this);
2593 }
2594 }
2595}
2596
2597/*******************************************************************************
2598 * FObjectInitializer
2599 ******************************************************************************/
2600
2601/**
2602 * Initializes a non-native property, according to the initialization rules. If the property is non-native
2603 * and does not have a zero constructor, it is initialized with the default value.
2604 * @param Property Property to be initialized
2605 * @param Data Default data
2606 * @return Returns true if that property was a non-native one, otherwise false
2607 */
2608bool FObjectInitializer::InitNonNativeProperty(UProperty* Property, UObject* Data)
2609{
2610 if (!Property->GetOwnerClass()->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic)) // if this property belongs to a native class, it was already initialized by the class constructor
2611 {
2612 if (!Property->HasAnyPropertyFlags(CPF_ZeroConstructor)) // this stuff is already zero
2613 {
2614 Property->InitializeValue_InContainer(Data);
2615 }
2616 return true;
2617 }
2618 else
2619 {
2620 // we have reached a native base class, none of the rest of the properties will need initialization
2621 return false;
2622 }
2623}
2624
2625/*******************************************************************************
2626 * FDeferredInitializationTrackerBase
2627 ******************************************************************************/
2628
2629FObjectInitializer* FDeferredInitializationTrackerBase::Add(const UObject* InitDependecy, const FObjectInitializer& DeferringInitializer)
2630{
2631 FObjectInitializer* DeferredInitializerCopy = nullptr;
2632
2633 DEFERRED_DEPENDENCY_CHECK(InitDependecy);
2634 if (InitDependecy)
2635 {
2636 UObject* InstanceObj = DeferringInitializer.GetObj();
2637 ArchetypeInstanceMap.AddUnique(InitDependecy, InstanceObj);
2638
2639 DEFERRED_DEPENDENCY_CHECK(DeferredInitializers.Find(InstanceObj) == nullptr); // did we try to init the object twice?
2640
2641 // NOTE: we copy the FObjectInitializer, because it is most likely in the process of being destroyed
2642 DeferredInitializerCopy = &DeferredInitializers.Add(InstanceObj, DeferringInitializer);
2643 }
2644 return DeferredInitializerCopy;
2645}
2646
2647void FDeferredInitializationTrackerBase::ResolveArchetypeInstances(UObject* InitDependecy)
2648{
2649 TArray<UObject*> ArchetypeInstances;
2650 ArchetypeInstanceMap.MultiFind(InitDependecy, ArchetypeInstances);
2651
2652 for (UObject* Instance : ArchetypeInstances)
2653 {
2654 DEFERRED_DEPENDENCY_CHECK(ResolvingObjects.Contains(Instance) == false);
2655 ResolvingObjects.Push(Instance);
2656
2657 if (ResolveDeferredInitialization(InitDependecy, Instance))
2658 {
2659 // For sub-objects, this has to come after ResolveDeferredInitialization(), since InitSubObjectProperties() is
2660 // invoked there (which is where we fill this sub-object with values from the super)
2661 PreloadDeferredDependents(Instance);
2662 }
2663
2664 DEFERRED_DEPENDENCY_CHECK(ResolvingObjects.Top() == Instance);
2665 ResolvingObjects.Pop();
2666 }
2667
2668 ArchetypeInstanceMap.Remove(InitDependecy);
2669}
2670
2671bool FDeferredInitializationTrackerBase::IsInitializationDeferred(const UObject* Object) const
2672{
2673 return DeferredInitializers.Contains(Object);
2674}
2675
2676bool FDeferredInitializationTrackerBase::DeferPreload(UObject* Object)
2677{
2678 const bool bDeferPreload = IsInitializationDeferred(Object);
2679 if (bDeferPreload && !IsResolving(Object))
2680 {
2681 DeferredPreloads.AddUnique(Object, Object);
2682 }
2683 return bDeferPreload;
2684}
2685
2686bool FDeferredInitializationTrackerBase::IsResolving(UObject* ArchetypeInstance) const
2687{
2688 return ResolvingObjects.Contains(ArchetypeInstance);
2689}
2690
2691bool FDeferredInitializationTrackerBase::ResolveDeferredInitialization(UObject* /*ResolvingObject*/, UObject* ArchetypeInstance)
2692{
2693 if (FObjectInitializer* DeferredInitializer = DeferredInitializers.Find(ArchetypeInstance))
2694 {
2695 // initializes and instances CDO properties (copies inherited values
2696 // from the super's CDO)
2697 FScriptIntegrationObjectHelper::PostConstructInitObject(*DeferredInitializer);
2698
2699 DeferredInitializers.Remove(ArchetypeInstance);
2700 }
2701
2702 return true;
2703}
2704
2705void FDeferredInitializationTrackerBase::PreloadDeferredDependents(UObject* ArchetypeInstance)
2706{
2707 TArray<UObject*> ObjsToPreload;
2708 DeferredPreloads.MultiFind(ArchetypeInstance, ObjsToPreload);
2709
2710 for (UObject* Object : ObjsToPreload)
2711 {
2712 FLinkerLoad* Linker = Object->GetLinker();
2713 DEFERRED_DEPENDENCY_CHECK(Linker != nullptr);
2714 if (Linker)
2715 {
2716 Linker->Preload(Object);
2717 }
2718 }
2719
2720 DeferredPreloads.Remove(ArchetypeInstance);
2721}
2722
2723/*******************************************************************************
2724 * FDeferredCdoInitializationTracker
2725 ******************************************************************************/
2726
2727bool FDeferredCdoInitializationTracker::DeferPreload(UObject* Object)
2728{
2729 bool bDeferPostload = false;
2730
2731 if (Object->HasAnyFlags(RF_ClassDefaultObject))
2732 {
2733 // When the initialization has been deferred we have to make sure to
2734 // defer serialization as well - don't worry, for CDOs, Preload() will be invoked
2735 // again from FinalizeBlueprint()->ResolveDeferredExports()
2736 bDeferPostload = !IsResolving(Object) && IsInitializationDeferred(Object);
2737 }
2738 else
2739 {
2740 auto ShouldDeferSubObjectPreload = [this, Object](UObject* OwnerObject)->bool
2741 {
2742 if (IsInitializationDeferred(OwnerObject))
2743 {
2744 const bool bDeferSubObjPostload = !IsResolving(OwnerObject);
2745 if (bDeferSubObjPostload)
2746 {
2747 DeferredPreloads.AddUnique(OwnerObject, Object);
2748 }
2749 return bDeferSubObjPostload;
2750 }
2751#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
2752 else if (OwnerObject)
2753 {
2754 UObject* OwnerClass = OwnerObject->GetClass();
2755 for (auto& DeferredCdo : DeferredInitializers)
2756 {
2757 // we used to index these by class, so to ensure the same behavior validate
2758 // our assumption that we can use the CDO object itself as the key (and that
2759 // using the class wouldn't find a match instead)
2760 DEFERRED_DEPENDENCY_CHECK(DeferredCdo.Key->GetClass() != OwnerClass);
2761 }
2762 }
2763#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
2764 return false;
2765 };
2766
2767 if (Object->HasAnyFlags(RF_DefaultSubObject))
2768 {
2769 UObject* SubObjOuter = Object->GetOuter();
2770 // NOTE: The outer of a DSO may not be a CDO like we want. It could
2771 // be something like a component template. Right now we ignore
2772 // those cases (IsDeferred() will reject this - only CDOs are
2773 // deferred in this struct), but if this case proves to be a problem,
2774 // then we may need to look up the outer chain, or see if the outer
2775 // sub-obj is deferred itself.
2776 bDeferPostload = ShouldDeferSubObjectPreload(SubObjOuter);
2777 }
2778 else if (Object->HasAnyFlags(RF_InheritableComponentTemplate))
2779 {
2780 UClass* OwningClass = Cast<UClass>(Object->GetOuter());
2781
2782 DEFERRED_DEPENDENCY_CHECK(OwningClass && OwningClass->ClassDefaultObject);
2783 if (OwningClass)
2784 {
2785 bDeferPostload = ShouldDeferSubObjectPreload(OwningClass->ClassDefaultObject);
2786 }
2787 }
2788 }
2789 return bDeferPostload;
2790}
2791
2792/*******************************************************************************
2793 * FDeferredSubObjInitializationTracker
2794 ******************************************************************************/
2795
2796bool FDeferredSubObjInitializationTracker::ResolveDeferredInitialization(UObject* ResolvingObject, UObject* ArchetypeInstance)
2797{
2798 bool bInitializerRan = false;
2799
2800 // If we deferred the sub-object because the super CDO wasn't ready, we still
2801 // need to check that its archetype is in a ready state (ready to be copied from)
2802 if (ResolvingObject->HasAnyFlags(RF_ClassDefaultObject))
2803 {
2804 if (FObjectInitializer* DeferredInitializer = DeferredInitializers.Find(ArchetypeInstance))
2805 {
2806 UObject* Archetype = DeferredInitializer->GetArchetype();
2807 // When this sub-object was created it's archetype object (the
2808 // super's sub-obj) may not have been created yet. In that scenario, the
2809 // component class's CDO would have been used in its place; now that
2810 // the super is good, we should update the archetype
2811 if (ArchetypeInstance->HasAnyFlags(RF_ClassDefaultObject))
2812 {
2813 Archetype = UObject::GetArchetypeFromRequiredInfo(ArchetypeInstance->GetClass(), ArchetypeInstance->GetOuter(), ArchetypeInstance->GetFName(), ArchetypeInstance->GetFlags());
2814 }
2815
2816 const bool bArchetypeLoadPending = Archetype &&
2817 (Archetype->HasAnyFlags(RF_NeedLoad) || (Archetype->HasAnyFlags(RF_WasLoaded) && !Archetype->HasAnyFlags(RF_LoadCompleted)));
2818
2819 if (bArchetypeLoadPending)
2820 {
2821 // Archetype isn't ready, move the deferred initializer to wait for its archetype
2822 ArchetypeInstanceMap.Add(Archetype, ArchetypeInstance);
2823 // don't need to add this to DeferredInitializers, as it is already there
2824 }
2825 else
2826 {
2827 bInitializerRan = FDeferredInitializationTrackerBase::ResolveDeferredInitialization(ResolvingObject, ArchetypeInstance);
2828 }
2829 }
2830 }
2831 else
2832 {
2833 bInitializerRan = FDeferredInitializationTrackerBase::ResolveDeferredInitialization(ResolvingObject, ArchetypeInstance);
2834 }
2835
2836 return bInitializerRan;
2837}
2838
2839/*******************************************************************************
2840 * FDeferredObjInitializationHelper
2841 ******************************************************************************/
2842
2843FObjectInitializer* FDeferredObjInitializationHelper::DeferObjectInitializerIfNeeded(const FObjectInitializer& DeferringInitializer)
2844{
2845 FObjectInitializer* DeferredInitializerCopy = nullptr;
2846
2847 UObject* TargetObj = DeferringInitializer.GetObj();
2848 if (TargetObj)
2849 {
2850 FDeferredCdoInitializationTracker& CdoInitDeferalSys = FDeferredCdoInitializationTracker::Get();
2851 auto IsSuperCdoReadyToBeCopied = [&CdoInitDeferalSys](const UClass* LoadClass, const UObject* SuperCDO)->bool
2852 {
2853 // RF_WasLoaded indicates that this Super was loaded from disk (and hasn't been regenerated on load)
2854 // regenerated CDOs will not have the RF_LoadCompleted
2855 const bool bSuperCdoLoadPending = CdoInitDeferalSys.IsInitializationDeferred(SuperCDO) ||
2856 SuperCDO->HasAnyFlags(RF_NeedLoad) || (SuperCDO->HasAnyFlags(RF_WasLoaded) && !SuperCDO->HasAnyFlags(RF_LoadCompleted));
2857
2858 if (bSuperCdoLoadPending)
2859 {
2860 const FLinkerLoad* ObjLinker = LoadClass->GetLinker();
2861 const bool bIsBpClassSerializing = ObjLinker && (ObjLinker->LoadFlags & LOAD_DeferDependencyLoads);
2862 const bool bIsResolvingDeferredObjs = LoadClass->HasAnyFlags(RF_LoadCompleted) &&
2863 ObjLinker && ObjLinker->IsBlueprintFinalizationPending();
2864
2865 DEFERRED_DEPENDENCY_CHECK(bIsBpClassSerializing || bIsResolvingDeferredObjs);
2866 return !bIsBpClassSerializing && !bIsResolvingDeferredObjs;
2867 }
2868 return true;
2869 };
2870
2871 const bool bIsCDO = TargetObj->HasAnyFlags(RF_ClassDefaultObject);
2872 if (bIsCDO)
2873 {
2874 const UClass* CdoClass = DeferringInitializer.GetClass();
2875 UClass* SuperClass = CdoClass->GetSuperClass();
2876
2877 if (!CdoClass->IsNative() && !SuperClass->IsNative())
2878 {
2879 DEFERRED_DEPENDENCY_CHECK(CdoClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint));
2880 DEFERRED_DEPENDENCY_CHECK(SuperClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint));
2881
2882 const UObject* SuperCDO = DeferringInitializer.GetArchetype();
2883 DEFERRED_DEPENDENCY_CHECK(SuperCDO && SuperCDO->HasAnyFlags(RF_ClassDefaultObject));
2884 // use the ObjectArchetype for the super CDO because the SuperClass may have a REINST CDO cached currently
2885 SuperClass = SuperCDO->GetClass();
2886
2887 if (!IsSuperCdoReadyToBeCopied(CdoClass, SuperCDO))
2888 {
2889 DeferredInitializerCopy = CdoInitDeferalSys.Add(SuperCDO, DeferringInitializer);
2890 }
2891 }
2892 }
2893 // since "InheritableComponentTemplate"s are not default sub-objects,
2894 // they won't be fixed up by the owner's FObjectInitializer (CDO
2895 // FObjectInitializers will init default sub-object properties, copying
2896 // from the super's DSOs) - this means that we need to separately defer
2897 // init'ing these sub-objects when their archetype hasn't been loaded yet
2898 else if (TargetObj->HasAnyFlags(RF_InheritableComponentTemplate))
2899 {
2900 const UClass* OwnerClass = Cast<UClass>(TargetObj->GetOuter());
2901 DEFERRED_DEPENDENCY_CHECK(OwnerClass && OwnerClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint));
2902 const UClass* SuperClass = OwnerClass->GetSuperClass();
2903
2904 if (SuperClass && !SuperClass->IsNative())
2905 {
2906 // It is possible that the archetype isn't even correct, if the
2907 // super's sub-object hasn't even been created yet (in this case the
2908 // component's CDO is used, which is probably wrong)
2909 //
2910 // So if the super CDO isn't ready, we need to defer this sub-object
2911 const UObject* SuperCDO = SuperClass->ClassDefaultObject;
2912 if (!IsSuperCdoReadyToBeCopied(OwnerClass, SuperCDO))
2913 {
2914 FDeferredSubObjInitializationTracker& SubObjInitDeferalSys = FDeferredSubObjInitializationTracker::Get();
2915 DeferredInitializerCopy = SubObjInitDeferalSys.Add(SuperCDO, DeferringInitializer);
2916 }
2917 }
2918
2919 // if it passed the super CDO check above, assume the archetype is kosher
2920 if (!DeferredInitializerCopy)
2921 {
2922 UObject* Archetype = DeferringInitializer.GetArchetype();
2923
2924 const bool bArchetypeLoadPending = Archetype &&
2925 ( Archetype->HasAnyFlags(RF_NeedLoad) || (Archetype->HasAnyFlags(RF_WasLoaded) && !Archetype->HasAnyFlags(RF_LoadCompleted)) );
2926
2927 if (bArchetypeLoadPending)
2928 {
2929 FDeferredSubObjInitializationTracker& SubObjInitDeferalSys = FDeferredSubObjInitializationTracker::Get();
2930 DeferredInitializerCopy = SubObjInitDeferalSys.Add(Archetype, DeferringInitializer);
2931 }
2932 }
2933 }
2934 }
2935
2936 return DeferredInitializerCopy;
2937}
2938
2939bool FDeferredObjInitializationHelper::DeferObjectPreload(UObject* Object)
2940{
2941 return FDeferredCdoInitializationTracker::Get().DeferPreload(Object) || FDeferredSubObjInitializationTracker::Get().DeferPreload(Object);
2942}
2943
2944void FDeferredObjInitializationHelper::ResolveDeferredInitsFromArchetype(UObject* Archetype)
2945{
2946 FDeferredCdoInitializationTracker& DeferredCdoTracker = FDeferredCdoInitializationTracker::Get();
2947 FDeferredSubObjInitializationTracker& DeferredSubObjTracker = FDeferredSubObjInitializationTracker::Get();
2948
2949#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
2950 if (Archetype->HasAnyFlags(RF_ClassDefaultObject))
2951 {
2952 // we used to index the deferred initialization by class, so to ensure the same behavior validate
2953 // our assumption that we can use the CDO object itself as the key (and that using the class wouldn't find a match instead)
2954 auto IsDeferredByClass = [Archetype](const TMultiMap<const UObject*, UObject*>& ArchetypeMap)->bool
2955 {
2956 for (auto& DeferredObj : ArchetypeMap)
2957 {
2958 if (DeferredObj.Key->GetClass() == Archetype->GetClass())
2959 {
2960 return true;
2961 }
2962 }
2963 return false;
2964 };
2965
2966 if (!DeferredCdoTracker.ArchetypeInstanceMap.Contains(Archetype))
2967 {
2968 DEFERRED_DEPENDENCY_CHECK(IsDeferredByClass(DeferredCdoTracker.ArchetypeInstanceMap) == false);
2969 }
2970 if (!DeferredSubObjTracker.ArchetypeInstanceMap.Contains(Archetype))
2971 {
2972 DEFERRED_DEPENDENCY_CHECK(IsDeferredByClass(DeferredSubObjTracker.ArchetypeInstanceMap) == false);
2973 }
2974 }
2975#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
2976
2977 DeferredCdoTracker.ResolveArchetypeInstances(Archetype);
2978 DeferredSubObjTracker.ResolveArchetypeInstances(Archetype);
2979}
2980
2981// don't want other files ending up with this internal define
2982#undef DEFERRED_DEPENDENCY_CHECK
2983
2984FBlueprintDependencyObjectRef::FBlueprintDependencyObjectRef(const TCHAR* InPackageFolder
2985 , const TCHAR* InShortPackageName
2986 , const TCHAR* InObjectName
2987 , const TCHAR* InClassPackageName
2988 , const TCHAR* InClassName
2989 , const TCHAR* InOuterName)
2990 : PackageName(*(FString(InPackageFolder) + TEXT("/") + InShortPackageName))
2991 , ObjectName(InObjectName)
2992 , ClassPackageName(InClassPackageName)
2993 , ClassName(InClassName)
2994 , OuterName(InOuterName)
2995{}
2996
2997FConvertedBlueprintsDependencies& FConvertedBlueprintsDependencies::Get()
2998{
2999 static FConvertedBlueprintsDependencies ConvertedBlueprintsDependencies;
3000 return ConvertedBlueprintsDependencies;
3001}
3002
3003void FConvertedBlueprintsDependencies::RegisterConvertedClass(FName PackageName, GetDependenciesNamesFunc GetAssets)
3004{
3005 check(!PackageNameToGetter.Contains(PackageName));
3006 ensure(GetAssets);
3007 PackageNameToGetter.Add(PackageName, GetAssets);
3008}
3009
3010static bool IsBlueprintDependencyDataNull(const FBlueprintDependencyData& Dependency)
3011{
3012 return Dependency.ObjectRef.ObjectName == NAME_None;
3013}
3014
3015void FConvertedBlueprintsDependencies::GetAssets(FName PackageName, TArray<FBlueprintDependencyData>& OutDependencies) const
3016{
3017 auto FuncPtr = PackageNameToGetter.Find(PackageName);
3018 auto Func = (FuncPtr) ? (*FuncPtr) : nullptr;
3019 ensure(Func || !FuncPtr);
3020 if (Func)
3021 {
3022 Func(OutDependencies);
3023 OutDependencies.RemoveAll(IsBlueprintDependencyDataNull);
3024 }
3025}
3026
3027void FConvertedBlueprintsDependencies::FillUsedAssetsInDynamicClass(UDynamicClass* DynamicClass, GetDependenciesNamesFunc GetUsedAssets)
3028{
3029 check(DynamicClass && GetUsedAssets);
3030 ensure(DynamicClass->UsedAssets.Num() == 0);
3031
3032 TArray<FBlueprintDependencyData> UsedAssetdData;
3033 GetUsedAssets(UsedAssetdData);
3034
3035 if (GEventDrivenLoaderEnabled && EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME)
3036 {
3037 FLinkerLoad* Linker = DynamicClass->GetOutermost()->LinkerLoad;
3038 if (Linker)
3039 {
3040 int32 ImportIndex = 0;
3041 for (FBlueprintDependencyData& ItData : UsedAssetdData)
3042 {
3043 if (!IsBlueprintDependencyDataNull(ItData))
3044 {
3045 FObjectImport& Import = Linker->Imp(FPackageIndex::FromImport(ImportIndex));
3046 check(Import.ObjectName == ItData.ObjectRef.ObjectName);
3047 UObject* TheAsset = Import.XObject;
3048 UE_CLOG(!TheAsset, LogBlueprintSupport, Error, TEXT("Could not find UDynamicClass dependent asset (EDL) %s in %s"), *ItData.ObjectRef.ObjectName.ToString(), *ItData.ObjectRef.PackageName.ToString());
3049 DynamicClass->UsedAssets.Add(TheAsset);
3050 ImportIndex += 2;
3051 }
3052 else
3053 {
3054 DynamicClass->UsedAssets.Add(nullptr);
3055 }
3056 }
3057 return;
3058 }
3059 check(0);
3060 }
3061
3062 for (FBlueprintDependencyData& ItData : UsedAssetdData)
3063 {
3064 if (ItData.ObjectRef.ObjectName != NAME_None)
3065 {
3066 const FString PathToObj = FString::Printf(TEXT("%s.%s"), *ItData.ObjectRef.PackageName.ToString(), *ItData.ObjectRef.ObjectName.ToString());
3067 UObject* TheAsset = LoadObject<UObject>(nullptr, *PathToObj);
3068 UE_CLOG(!TheAsset, LogBlueprintSupport, Error, TEXT("Could not find UDynamicClass dependent asset (non-EDL) %s in %s"), *ItData.ObjectRef.ObjectName.ToString(), *ItData.ObjectRef.PackageName.ToString());
3069 DynamicClass->UsedAssets.Add(TheAsset);
3070 }
3071 else
3072 {
3073 DynamicClass->UsedAssets.Add(nullptr);
3074 }
3075 }
3076}
3077
3078UObject* FConvertedBlueprintsDependencies::LoadObjectForStructConstructor(UScriptStruct* ScriptStruct, const TCHAR* ObjectPath)
3079{
3080 if (GEventDrivenLoaderEnabled && EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME)
3081 {
3082 // Find Object should work here as the blueprints have scheduled it for load
3083 return FindObject<UObject>(nullptr, ObjectPath);
3084 }
3085
3086 return LoadObject<UObject>(nullptr, ObjectPath);
3087}
3088
3089bool FBlueprintDependencyData::ContainsDependencyData(TArray<FBlueprintDependencyData>& Assets, int16 ObjectRefIndex)
3090{
3091 return nullptr != Assets.FindByPredicate([=](const FBlueprintDependencyData& Data) -> bool
3092 {
3093 return Data.ObjectRefIndex == ObjectRefIndex;
3094 });
3095};
3096
3097void FBlueprintDependencyData::AppendUniquely(TArray<FBlueprintDependencyData>& Destination, const TArray<FBlueprintDependencyData>& AdditionalData)
3098{
3099 for (const FBlueprintDependencyData& Data : AdditionalData)
3100 {
3101 Destination.AddUnique(Data);
3102 }
3103}
3104
3105
3106#if WITH_EDITOR
3107
3108/*******************************************************************************
3109* IBlueprintNativeCodeGenCore
3110******************************************************************************/
3111static const IBlueprintNativeCodeGenCore* CoordinatorInstance = nullptr;
3112
3113const IBlueprintNativeCodeGenCore* IBlueprintNativeCodeGenCore::Get()
3114{
3115 return CoordinatorInstance;
3116}
3117
3118void IBlueprintNativeCodeGenCore::Register(const IBlueprintNativeCodeGenCore* Coordinator)
3119{
3120 CoordinatorInstance = Coordinator;
3121}
3122
3123#endif // WITH_EDITOR