FString FEmitDefaultValueHelper::HandleSpecialTypes(FEmitterLocalContext& Context, const UProperty* Property, const uint8* ValuePtr) { //TODO: Use Path maps for Objects if (auto ObjectProperty = Cast<UObjectProperty>(Property)) { UObject* Object = ObjectProperty->GetPropertyValue(ValuePtr); if (Object) { { UClass* ObjectClassToUse = Context.GetFirstNativeOrConvertedClass(ObjectProperty->PropertyClass); const FString MappedObject = Context.FindGloballyMappedObject(Object, ObjectClassToUse); if (!MappedObject.IsEmpty()) { return MappedObject; } } const bool bCreatingSubObjectsOfClass = (Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass); { auto BPGC = Context.GetCurrentlyGeneratedClass(); auto CDO = BPGC ? BPGC->GetDefaultObject(false) : nullptr; if (BPGC && Object && CDO && Object->IsIn(BPGC) && !Object->IsIn(CDO) && bCreatingSubObjectsOfClass) { return HandleClassSubobject(Context, Object, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, true, true); } } if (!bCreatingSubObjectsOfClass && Property->HasAnyPropertyFlags(CPF_InstancedReference)) { const FString CreateAsInstancedSubobject = HandleInstancedSubobject(Context, Object, Object->HasAnyFlags(RF_ArchetypeObject)); if (!CreateAsInstancedSubobject.IsEmpty()) { return CreateAsInstancedSubobject; } } } else if (ObjectProperty->HasMetaData(FBlueprintMetadata::MD_LatentCallbackTarget)) { return TEXT("this"); } } if (auto StructProperty = Cast<UStructProperty>(Property)) { FString StructConstructor; if (SpecialStructureConstructor(StructProperty->Struct, ValuePtr, &StructConstructor)) { return StructConstructor; } } return FString(); }
void FHotReloadClassReinstancer::RecreateCDOAndSetupOldClassReinstancing(UClass* InOldClass) { // Set base class members to valid values ClassToReinstance = InOldClass; DuplicatedClass = InOldClass; OriginalCDO = InOldClass->GetDefaultObject(); bHasReinstanced = false; bSkipGarbageCollection = false; bNeedsReinstancing = false; NewClass = InOldClass; // The class doesn't change in this case // Collect the original property values SerializeCDOProperties(InOldClass->GetDefaultObject(), OriginalCDOProperties); // Remember all the basic info about the object before we rename it EObjectFlags CDOFlags = OriginalCDO->GetFlags(); UObject* CDOOuter = OriginalCDO->GetOuter(); FName CDOName = OriginalCDO->GetFName(); // Rename original CDO, so we can store this one as OverridenArchetypeForCDO // and create new one with the same name and outer. OriginalCDO->Rename( *MakeUniqueObjectName( GetTransientPackage(), OriginalCDO->GetClass(), *FString::Printf(TEXT("BPGC_ARCH_FOR_CDO_%s"), *InOldClass->GetName()) ).ToString(), GetTransientPackage(), REN_DoNotDirty | REN_DontCreateRedirectors | REN_NonTransactional | REN_SkipGeneratedClasses | REN_ForceNoResetLoaders); // Re-create the CDO, re-running its constructor ReconstructClassDefaultObject(InOldClass, CDOOuter, CDOName, CDOFlags); ReconstructedCDOsMap.Add(OriginalCDO, InOldClass->GetDefaultObject()); // Collect the property values after re-constructing the CDO SerializeCDOProperties(InOldClass->GetDefaultObject(), ReconstructedCDOProperties); // We only want to re-instance the old class if its CDO's values have changed or any of its DSOs' property values have changed if (DefaultPropertiesHaveChanged()) { bNeedsReinstancing = true; SaveClassFieldMapping(InOldClass); TArray<UClass*> ChildrenOfClass; GetDerivedClasses(InOldClass, ChildrenOfClass); for (auto ClassIt = ChildrenOfClass.CreateConstIterator(); ClassIt; ++ClassIt) { UClass* ChildClass = *ClassIt; UBlueprint* ChildBP = Cast<UBlueprint>(ChildClass->ClassGeneratedBy); if (ChildBP && !ChildBP->HasAnyFlags(RF_BeingRegenerated)) { if (!ChildBP->HasAnyFlags(RF_NeedLoad)) { Children.AddUnique(ChildBP); auto BPGC = Cast<UBlueprintGeneratedClass>(ChildBP->GeneratedClass); auto CurrentCDO = BPGC ? BPGC->GetDefaultObject(false) : nullptr; if (CurrentCDO && (OriginalCDO == CurrentCDO->GetArchetype())) { BPGC->OverridenArchetypeForCDO = OriginalCDO; } } } } } }
void FEmitDefaultValueHelper::GenerateConstructor(FEmitterLocalContext& Context) { GenerateCustomDynamicClassInitialization(Context); auto BPGC = CastChecked<UBlueprintGeneratedClass>(Context.GetCurrentlyGeneratedClass()); const FString CppClassName = FEmitHelper::GetCppName(BPGC); UClass* SuperClass = BPGC->GetSuperClass(); const bool bSuperHasOnlyDefaultConstructor = SuperClass && SuperClass->HasMetaData(TEXT("OnlyDefaultConstructorDeclared")); Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::CommonConstructor; Context.ResetPropertiesForInaccessibleStructs(); Context.AddLine(FString::Printf(TEXT("%s::%s(const FObjectInitializer& ObjectInitializer) : Super(%s)") , *CppClassName , *CppClassName , bSuperHasOnlyDefaultConstructor ? TEXT("") : TEXT("ObjectInitializer"))); Context.AddLine(TEXT("{")); Context.IncreaseIndent(); // Call CustomDynamicClassInitialization Context.AddLine(FString::Printf(TEXT("if(HasAnyFlags(RF_ClassDefaultObject) && (%s::StaticClass() == GetClass()))"), *CppClassName)); Context.AddLine(TEXT("{")); Context.IncreaseIndent(); Context.AddLine(FString::Printf(TEXT("%s::__CustomDynamicClassInitialization(CastChecked<UDynamicClass>(GetClass()));"), *CppClassName)); Context.DecreaseIndent(); Context.AddLine(TEXT("}")); // Components that must be fixed after serialization TArray<FString> NativeCreatedComponentProperties; { UObject* CDO = BPGC->GetDefaultObject(false); UObject* ParentCDO = BPGC->GetSuperClass()->GetDefaultObject(false); check(CDO && ParentCDO); Context.AddLine(TEXT("")); FString NativeRootComponentFallback; TSet<const UProperty*> HandledProperties; // Generate ctor init code for native class default subobjects that are always instanced (e.g. components). // @TODO (pkavan) - We can probably make this faster by generating code to index through the DSO array instead (i.e. in place of HandleInstancedSubobject which will generate a lookup call per DSO). TArray<UObject*> NativeDefaultObjectSubobjects; BPGC->GetDefaultObjectSubobjects(NativeDefaultObjectSubobjects); for (auto DSO : NativeDefaultObjectSubobjects) { if (DSO && DSO->GetClass()->HasAnyClassFlags(CLASS_DefaultToInstanced)) { // Determine if this is an editor-only subobject. bool bIsEditorOnlySubobject = false; if (const UActorComponent* ActorComponent = Cast<UActorComponent>(DSO)) { bIsEditorOnlySubobject = ActorComponent->IsEditorOnly(); } // Skip ctor code gen for editor-only subobjects, since they won't be used by the runtime. Any dependencies on editor-only subobjects will be handled later (see HandleInstancedSubobject). if (!bIsEditorOnlySubobject) { const FString VariableName = HandleInstancedSubobject(Context, DSO, false, true); // Keep track of which component can be used as a root, in case it's not explicitly set. if (NativeRootComponentFallback.IsEmpty()) { USceneComponent* SceneComponent = Cast<USceneComponent>(DSO); if (SceneComponent && !SceneComponent->GetAttachParent() && SceneComponent->CreationMethod == EComponentCreationMethod::Native) { NativeRootComponentFallback = VariableName; } } } } } // Check for a valid RootComponent property value; mark it as handled if already set in the defaults. bool bNeedsRootComponentAssignment = false; static const FName RootComponentPropertyName(TEXT("RootComponent")); const UObjectProperty* RootComponentProperty = FindField<UObjectProperty>(BPGC, RootComponentPropertyName); if (RootComponentProperty) { if (RootComponentProperty->GetObjectPropertyValue_InContainer(CDO)) { HandledProperties.Add(RootComponentProperty); } else if (!NativeRootComponentFallback.IsEmpty()) { Context.AddLine(FString::Printf(TEXT("RootComponent = %s;"), *NativeRootComponentFallback)); HandledProperties.Add(RootComponentProperty); } else { bNeedsRootComponentAssignment = true; } } // Generate ctor init code for the SCS node hierarchy (i.e. non-native components). SCS nodes may have dependencies on native DSOs, but not vice-versa. TArray<const UBlueprintGeneratedClass*> BPGCStack; const bool bErrorFree = UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(BPGC, BPGCStack); if (bErrorFree) { TArray<FNonativeComponentData> ComponentsToInit; // Start at the base of the hierarchy so that dependencies are handled first. for (int32 i = BPGCStack.Num() - 1; i >= 0; --i) { if (BPGCStack[i]->SimpleConstructionScript) { for (auto Node : BPGCStack[i]->SimpleConstructionScript->GetRootNodes()) { if (Node) { const FString NativeVariablePropertyName = HandleNonNativeComponent(Context, Node, HandledProperties, NativeCreatedComponentProperties, nullptr, ComponentsToInit); if (bNeedsRootComponentAssignment && Node->ComponentTemplate && Node->ComponentTemplate->IsA<USceneComponent>() && !NativeVariablePropertyName.IsEmpty()) { // Only emit the explicit root component assignment statement if we're looking at the child BPGC that we're generating ctor code // for. In all other cases, the root component will already be set up by a chained parent ctor call, so we avoid stomping it here. if (i == 0) { Context.AddLine(FString::Printf(TEXT("RootComponent = %s;"), *NativeVariablePropertyName)); HandledProperties.Add(RootComponentProperty); } bNeedsRootComponentAssignment = false; } } } } } for (auto& ComponentToInit : ComponentsToInit) { ComponentToInit.EmitProperties(Context); } } // Generate ctor init code for generated Blueprint class property values that may differ from parent class defaults (or that otherwise belong to the generated Blueprint class). for (auto Property : TFieldRange<const UProperty>(BPGC)) { const bool bNewProperty = Property->GetOwnerStruct() == BPGC; const bool bIsAccessible = bNewProperty || !Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPrivate); if (bIsAccessible && !HandledProperties.Contains(Property)) { OuterGenerate(Context, Property, TEXT(""), reinterpret_cast<const uint8*>(CDO), bNewProperty ? nullptr : reinterpret_cast<const uint8*>(ParentCDO), EPropertyAccessOperator::None, true); } } } Context.DecreaseIndent(); Context.AddLine(TEXT("}")); Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::Regular; Context.ResetPropertiesForInaccessibleStructs(); Context.AddLine(FString::Printf(TEXT("void %s::PostLoadSubobjects(FObjectInstancingGraph* OuterInstanceGraph)"), *CppClassName)); Context.AddLine(TEXT("{")); Context.IncreaseIndent(); Context.AddLine(TEXT("Super::PostLoadSubobjects(OuterInstanceGraph);")); for (auto& ComponentToFix : NativeCreatedComponentProperties) { Context.AddLine(FString::Printf(TEXT("if(ensure(%s))"), *ComponentToFix)); Context.AddLine(TEXT("{")); Context.IncreaseIndent(); Context.AddLine(FString::Printf(TEXT("%s->CreationMethod = EComponentCreationMethod::Native;"), *ComponentToFix)); Context.DecreaseIndent(); Context.AddLine(TEXT("}")); } Context.DecreaseIndent(); Context.AddLine(TEXT("}")); FDependenciesHelper::AddStaticFunctionsForDependencies(Context); FBackendHelperUMG::EmitWidgetInitializationFunctions(Context); }
FString FEmitDefaultValueHelper::HandleInstancedSubobject(FEmitterLocalContext& Context, UObject* Object, bool bCreateInstance, bool bSkipEditorOnlyCheck) { check(Object); // Make sure we don't emit initialization code for the same object more than once. FString LocalNativeName = Context.FindGloballyMappedObject(Object); if (!LocalNativeName.IsEmpty()) { return LocalNativeName; } else { LocalNativeName = Context.GenerateUniqueLocalName(); } if (Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass) { Context.AddClassSubObject_InConstructor(Object, LocalNativeName); } else if (Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::CommonConstructor) { Context.AddCommonSubObject_InConstructor(Object, LocalNativeName); } UClass* ObjectClass = Object->GetClass(); // Determine if this is an editor-only subobject. When handling as a dependency, we'll create a "dummy" object in its place (below). bool bIsEditorOnlySubobject = false; if (!bSkipEditorOnlyCheck) { if (UActorComponent* ActorComponent = Cast<UActorComponent>(Object)) { bIsEditorOnlySubobject = ActorComponent->IsEditorOnly(); if (bIsEditorOnlySubobject) { // Replace the potentially editor-only class with a base actor/scene component class that's available to the runtime. We'll create a "dummy" object of this type to stand in for the editor-only subobject below. ObjectClass = ObjectClass->IsChildOf<USceneComponent>() ? USceneComponent::StaticClass() : UActorComponent::StaticClass(); } } } auto BPGC = Context.GetCurrentlyGeneratedClass(); auto CDO = BPGC ? BPGC->GetDefaultObject(false) : nullptr; if (!bIsEditorOnlySubobject && ensure(CDO) && (CDO == Object->GetOuter())) { if (bCreateInstance) { Context.AddLine(FString::Printf(TEXT("auto %s = CreateDefaultSubobject<%s>(TEXT(\"%s\"));") , *LocalNativeName, *FEmitHelper::GetCppName(ObjectClass), *Object->GetName())); } else { Context.AddLine(FString::Printf(TEXT("auto %s = CastChecked<%s>(GetDefaultSubobjectByName(TEXT(\"%s\")));") , *LocalNativeName, *FEmitHelper::GetCppName(ObjectClass), *Object->GetName())); } const UObject* ObjectArchetype = Object->GetArchetype(); for (auto Property : TFieldRange<const UProperty>(ObjectClass)) { OuterGenerate(Context, Property, LocalNativeName , reinterpret_cast<const uint8*>(Object) , reinterpret_cast<const uint8*>(ObjectArchetype) , EPropertyAccessOperator::Pointer); } } else { const FString OuterStr = Context.FindGloballyMappedObject(Object); if (OuterStr.IsEmpty()) { ensure(false); return FString(); } const FString ActualClass = Context.FindGloballyMappedObject(ObjectClass, UClass::StaticClass()); const FString NativeType = FEmitHelper::GetCppName(Context.GetFirstNativeOrConvertedClass(ObjectClass)); Context.AddLine(FString::Printf( TEXT("auto %s = NewObject<%s>(%s, %s, TEXT(\"%s\"));") , *LocalNativeName , *NativeType , *OuterStr , *ActualClass , *Object->GetName())); } return LocalNativeName; }