Files
UnrealEngineUWP/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendValueHelper.cpp
Mike Beach e73b4a7c84 Copying //UE4/Dev-Blueprints to //UE4/Dev-Main (Source: //UE4/Dev-Blueprints @ 3255454)
#lockdown Nick.Penwarden
#rb none

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3228496 on 2016/12/09 by Ben.Cosh

	This change adds extra information to component template arrays so that the component class can be determined in builds that strip out objects of certain class types such as the editor dedicated server build.
	#Jira UE-38842 - "LogBlueprint:Error: [Compiler BP_Skybox_World_RandomTrees_01] Error Can't connect pins ReturnValue and Target" after entering a lobby in a synced server
	#Proj KismetCompiler, BlueprintGraph, UnrealEd, Core, Engine, Kismet, BlueprintCompilerCppBackend

Change 3232435 on 2016/12/13 by Ben.Cosh

	Fix for a bug introduced in CL 3228496 that caused component templates to fail to be identified by name and resulted in blueprint compilation issues for add component nodes.
	#Jira UE-39623 - Unknown template referenced by Add Component Node
	#Proj BlueprintGraph, Engine

Change 3234581 on 2016/12/14 by Mike.Beach

	Backing out fix for UE-38842 (CL 3228496/3232435/3232564) - mapping UBlueprintGeneratedClass's ComponentTemplates array to a new format was causing issues with deferred dependency loading during serialization (trying to extract type information from a placeholder object). We're opting for a smaller/simpler solution to UE-38842, which will be to store the component information on the node itself (not with the templates).

	#jira UE-39707

Change 3236615 on 2016/12/15 by Maciej.Mroz

	Nativization: Fixed getter fuctions in FUnconvertedWrapper, the returned ref won;t be const.

Change 3236967 on 2016/12/15 by Dan.Oconnor

	Test data showing an error for jira issue UE-39808

Change 3237021 on 2016/12/15 by Dan.Oconnor

	UE31622 test data

Change 3237046 on 2016/12/15 by Dan.Oconnor

	UE-14123 test data

Change 3239289 on 2016/12/17 by Phillip.Kavan

	[UE-38999] Dump component tree node hierarchy to the output log on error state during widget generation.

	change summary:
	- added FOnTableViewBadState delegate parameter to SSCSEditorDragDropTree
	- added SSCSEditor::DumpTree() as the FOnTableViewBadState delegate implementatioon for the STableView widget (to provide us with more info on future occurrences)

	#jira UE-38999

Change 3239448 on 2016/12/19 by Maciej.Mroz

	#jira UE-39794

	New way of collecting dependencies assets. Only directly used assets are listed. It will be used in projects with EDL enabled, once the "EDL boot time" is enabled.

	Nativized projects with EDL disabled  use the new mechanism as well. They use new  __StaticDependenciesAssets functions generator. __StaticDependenciesAssets calls recursively __StaticDependenciesAssets of all Blueprints, that the current BP depends on. It reduces the size of __StaticDependenciesAssets.

	Notice, that at the moment, this change should no affect any projects with EDL enabled (because the  EDL boot time doesn't work yet).

Change 3239778 on 2016/12/19 by Phillip.Kavan

	[UE-39854] Fix nativized assets build error when there are no native code dependencies.

	change summary:
	- modified FDependenciesGlobalMapHelper::EmitBodyCode() to emit a NULL entry when there are no other entries to emit, in order to avoid a zero-length array initialization error at compile time.

	#jira UE-39854

Change 3239965 on 2016/12/19 by Phillip.Kavan

	[UE-39733] Fix incorrect graph pin value display names for user-defined enum types.

	change summary:
	- switched UEnum::GetDisplayNameText() to be a virtual API
	- added a UUserDefinedEnum::GetDisplayNameText() override to call FEnumEditorUtils::GetEnumeratorDisplayName()

	#jira UE-39733

Change 3240422 on 2016/12/19 by Dan.Oconnor

	Remove useless counter. ensureMsgf only fires once

Change 3242313 on 2016/12/21 by Phillip.Kavan

	[UE-35418] The Actor details view will now refresh property mappings for the current Actor instance selection after a bytecode-only recompile of its Blueprint class as a dependency during reinstancing.

	change summary:
	- Modified FBlueprintCompileReinstancer::ReinstanceObjects() to invoke SelectActor() on the current editor selection after a bytecode-only recompile of its class as a dependent BP (i.e. when reinstancing/finalization is not actually incurred). This is meant to be consistent with how the refresh for the current Actor selection is handled by the reinstancer in the other cases.

	#jira UE-35418

Change 3242409 on 2016/12/21 by Dan.Oconnor

	PR #2995: Arbitrary base logarithm blueprint node (Contributed by Valkrysa)

	#jira UE-39169

Change 3243207 on 2016/12/22 by Phillip.Kavan

	[UE-39816] Renaming interface input/output parameters will no longer cause broken pin links at interface function call sites in Blueprints that are currently loaded.

	change summary:
	- modified FBasePinChangeHelper::Broadcast() to consider pin changes sourced from an interface Blueprint
	- also revised non-interface function call site check code a bit to try and avoid doing some unnecessary work when possible, since this code runs through every call site node

	#jira UE-39816

Change 3243210 on 2016/12/22 by Phillip.Kavan

	[UE-39944] Extend the GetClassDefaults node to include output pin exceptions for TSet/TMap properties (i.e. mirror safeguards already in place for TArray).

	change summary:
	- deprecated 'bExcludeObjectArrays' and replaced with 'bExcludeObjectContainers' (not using a redirect because I need the old property serialized on load for backwards-compatibility)
	- modified FClassDefaultsOptionalPinManager's ctor to consider both flags
	- modified FClassDefaultsOptionalPinManager::CanTreatPropertyAsOptional() to also test for TSet/TMap types, and exclude them if they contain a non-class UObject property as the inner type (same as we do for TArray)
	- modified UK2Node_GetClassDefaults::ValidateNodeDuringCompilation() to also test for TSet/TMap types, and emit a warning for existing pin connections that would have otherwise failed for newly-placed nodes (same as TArray)
	- modified UK2Node_GetClassDefaults::ExpandNode() to generate the necessary script needed to create a copy of TSet/TMap property value outputs (same as we do for TArray types)

	#jira UE-39944

Change 3243373 on 2016/12/23 by Maciej.Mroz

	#jira UE-39794

	-Nativized build with EDL enabled use new dependency-gathering system. It shrinks size of the .exe file. This code will change once the boot time EDL is enabled.
	-In nativized code, ZCOnstructors are not called. They are replaced by ::StaticClass(). Static class doesn't call noting recursively. We still need to fix interfaces (they have no StaticClass).
	- Workaround for  UE-40026
	- Various minior improvements.

Change 3244038 on 2016/12/28 by Phillip.Kavan

	[UE-34488] Child Blueprint assets are now marked as dirty if its parent Blueprint incurs a strucutural change.

	change summary:
	- modified FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified() to call MarkPackageDirty() after a skeleton-only compile pass on a child Blueprint.

	notes:
	- this only fixes the issue for child Blueprints that are loaded when the change to the parent Blueprint occurs. unloaded child Blueprints would still need a redirector to fix this up on load.

	#jira UE-34488

Change 3244087 on 2016/12/28 by Phillip.Kavan

	CIS warning fix (C6236).

Change 3244388 on 2016/12/30 by Phillip.Kavan

	[UE-39816] Fix broken pin links caused by renaming interface function input/output parameters prior to compiling the interface, but after renaming the function itself.

	change summary:
	- modified FBlueprintEditorUtils::FindFunctionInImplementedInterfaces() to prefer the skeleton class when searching, as that is always up-to-date.
	- modified FBlueprintEditorUtils::RenameGraph() to update *all* function call site nodes, rather than just those within the current Blueprint class scope.

	#jira UE-39816

Change 3245322 on 2017/01/03 by Maciej.Mroz

	#jira UE-40125
	Fixed a crash when implementing a native interface in a BP

Change 3245667 on 2017/01/03 by Mike.Beach

	Mirroring CL 3245664 - Making it so level script bound nodes are fixed up on load (in case the level instances were saved out without the bindings).

	#jira UE-39950

Change 3247675 on 2017/01/05 by Maciej.Mroz

	BP is not DataOnly, when it overrides an inherited component

	related to UE-40131

Change 3247985 on 2017/01/05 by Maciej.Mroz

	NativizationSummary object is always present.

	#jira UE-40035

Change 3249423 on 2017/01/06 by Mike.Beach

	Mirroring CL 3248792 from Orion.

	Fix to keep placeholder classes from being needlessly created (when the object they represent already exists) - instead, attempt to lookup and find the existing import objects (which used to be set, but could be cleared during async loading by FLinkerManager::DissociateImportsAndForcedExports()).

	#jira OR-34038

Change 3249568 on 2017/01/06 by Mike.Beach

	Updating the UBlueprintThumbnailRenderer API so it can be sub-classed by plugin/projects.

	PR #2899: Expose UBlueprintThumbnailRenderer in the API (Contributed by e-agaubatz)

	#jira UE-38004

Change 3251903 on 2017/01/10 by Phillip.Kavan

	[UE-31640] Function inputs are now exposed as variable "Get" nodes via the right-click context menu in a Blueprint function graph context.

	Note: Changes are based on shelved CL# 2452724, original proof-of-concept code (credit: Michael.Noland).

	#jira UE-31640
	#fyi Michael.Noland

Change 3252119 on 2017/01/10 by Phillip.Kavan

	CIS fix (shadowed variable).

Change 3252744 on 2017/01/10 by Dan.Oconnor

	Add GMinimalCompileOnLoad path for postponing compile on load until all data has been loaded - long term this will be used to improve bp compiler infrastructure (performance, correctness, succinctness)

Change 3252968 on 2017/01/10 by Phillip.Kavan

	[UE-36798] Fix for an infinite loop case in the math expression parser.

	#jira UE-36798

Change 3253153 on 2017/01/10 by Dan.Oconnor

	Fixed construction scripts not being loaded before instancing occurs when using new lighter compile path

Change 3253171 on 2017/01/10 by Mike.Beach

	Mirrored CL 3253147.

	Properly fills out FPropertyChangedEvent's MemberProperty field, for the property editor's NotifyHook. As its comment implies, "MemberProperty" is meant to represent the outermost property (in scenarios, like with nested struct properties). It was not working this way, and was instead set to the same nested property.

Change 3253220 on 2017/01/10 by Dan.Oconnor

	These pins should infer together
	#jira UE-40427

Change 3253223 on 2017/01/10 by Phillip.Kavan

	[UE-35050] Fix a crash that occurs on an attempt to rename the category of an implemented interface function inherited from a native parent class in the My Blueprint panel.

	change summary:
	- modified FBlueprintEditorUtils::FindFunctionInImplementedInterfaces() to accept an additional input parameter so that callers can opt-in to searching all inherited interface classes
	- modified SMyBlueprint::CanRequestRenameOnActionNode() to prevent rename on implemented interface functions inherited from a native parent class

	#jira UE-35050

Change 3253259 on 2017/01/10 by Dan.Oconnor

	Quick cleanup, this stuff is in progress

Change 3253983 on 2017/01/11 by Phillip.Kavan

	[UE-35629] Disable external curve asset creation when editing a local variable's default value in a Blueprint graph.

	change summary:
	- modified FCurveStructCustomization::CustomizeChildren() to set the "create" button visibility to "collapsed" when the Owner is NULL (as the default asset path is inferred from the Owner's package).

	#jira UE-35629

Change 3254024 on 2017/01/11 by Phillip.Kavan

	[UE-40131] Non-native child BPs can now properly override a nativized parent BP's components in a cooked build with exclusive Blueprint class nativiation.

	change summary:
	- added UBlueprintGeneratedClass::CheckAndApplyComponentTemplateOverrides()
	- switched FComponentKey::OwnerClass type from UBlueprintGeneratedClass to UClass to allow the reference to be loaded when it's a nativized BP class.
	- modified AActor::PostLoadSubobjects() to apply ICH template overrides to inherited component subobjects on load (only in a cooked build)
	- modified AActor::PostSpawnInitialize() to also apply ICH template overrides to inherited component subobjects on new Actor spawns (only in a cooked build)
	- modified FBlueprintEditorUtils::IsDataOnlyBlueprint() to additionally reject BPs that have a non-empty ICH (from Maciej)
	- added UBlueprint::bHasNativizedParent to cache this as an indicator for cooked builds to allow Actor instances to avoid incurring additional overhead for this at load time in the general case

	#jira UE-40131

Change 3254176 on 2017/01/11 by Mike.Beach

	Mirroring CL 3245838 to unblock the Odin build. Downgrading new error to warning until we can address it in content (and fully understand why it is triggering).

	#jira UE-40470

Change 3254391 on 2017/01/11 by Phillip.Kavan

	[UE-40131] CIS fix (non-unity).

Change 3254442 on 2017/01/11 by Mike.Beach

	Mirroring CL 3245069 (from RobM), which is described as a "temp fix".

	#jira UE-40399

Change 3254599 on 2017/01/11 by Mike.Beach

	Updating some of our GetRedirectPinNames() functions - making local copy of an array element instead of a reference. We're adding to the array right after, and the ref can become invalid (if the array is grown and reallocated). Came to us via UDN.

Change 3254624 on 2017/01/11 by Mike.Beach

	Backing out CL 3247675, as it removed the "data-only" status from certain Blueprints that, IMO, are still considered "data-only". This would have reprecussions to editor (compile-on) load times, and affect how these Blueprints are presented to the user.

	#fyi Maciej.Mroz, Phillip.Kavan

Change 3254671 on 2017/01/11 by Mike.Beach

	Resolving CIS warning - USE_EVENT_DRIVEN_ASYNC_LOAD was replaced with GEventDrivenLoaderEnabled.

[CL 3255791 by Mike Beach in Main branch]
2017-01-12 14:36:04 -05:00

1902 lines
75 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "Misc/Guid.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "UObject/Class.h"
#include "UObject/Package.h"
#include "UObject/StructOnScope.h"
#include "UObject/UnrealType.h"
#include "UObject/UObjectHash.h"
#include "Misc/PackageName.h"
#include "Engine/Blueprint.h"
#include "Components/ActorComponent.h"
#include "Components/SceneComponent.h"
#include "Engine/LatentActionManager.h"
#include "PhysicsEngine/BodyInstance.h"
#include "Components/PrimitiveComponent.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "Engine/TimelineTemplate.h"
#include "Engine/UserDefinedEnum.h"
#include "Engine/UserDefinedStruct.h"
#include "EdGraphSchema_K2.h"
#include "KismetCompiler.h"
#include "BlueprintCompilerCppBackend.h"
#include "BlueprintCompilerCppBackendGatherDependencies.h"
#include "IBlueprintCompilerCppBackendModule.h"
#include "BlueprintCompilerCppBackendUtils.h"
#include "Kismet2/StructureEditorUtils.h"
#include "Engine/SCS_Node.h"
#include "Engine/InheritableComponentHandler.h"
#include "Engine/DynamicBlueprintBinding.h"
#include "Blueprint/BlueprintSupport.h"
void FEmitDefaultValueHelper::OuterGenerate(FEmitterLocalContext& Context
, const UProperty* Property
, const FString& OuterPath
, const uint8* DataContainer
, const uint8* OptionalDefaultDataContainer
, EPropertyAccessOperator AccessOperator
, bool bAllowProtected)
{
// Determine if the given property contains an instanced default subobject reference. We only get here if the values are not identical.
auto IsInstancedSubobjectLambda = [&](int32 ArrayIndex) -> bool
{
if (auto ObjectProperty = Cast<UObjectProperty>(Property))
{
check(DataContainer);
check(OptionalDefaultDataContainer);
auto ObjectPropertyValue = ObjectProperty->GetObjectPropertyValue_InContainer(DataContainer, ArrayIndex);
auto DefaultObjectPropertyValue = ObjectProperty->GetObjectPropertyValue_InContainer(OptionalDefaultDataContainer, ArrayIndex);
if (ObjectPropertyValue && ObjectPropertyValue->IsDefaultSubobject() && DefaultObjectPropertyValue && DefaultObjectPropertyValue->IsDefaultSubobject() && ObjectPropertyValue->GetFName() == DefaultObjectPropertyValue->GetFName())
{
return true;
}
}
return false;
};
check(Property);
if (Property->HasAnyPropertyFlags(CPF_EditorOnly | CPF_Transient))
{
UE_LOG(LogK2Compiler, Verbose, TEXT("FEmitDefaultValueHelper Skip EditorOnly or Transient property: %s"), *Property->GetPathName());
return;
}
if (Property->IsA<UDelegateProperty>() || Property->IsA<UMulticastDelegateProperty>())
{
UE_LOG(LogK2Compiler, Verbose, TEXT("FEmitDefaultValueHelper delegate property: %s"), *Property->GetPathName());
return;
}
for (int32 ArrayIndex = 0; ArrayIndex < Property->ArrayDim; ++ArrayIndex)
{
if (!OptionalDefaultDataContainer
|| ((Property->PropertyFlags & CPF_Config) != 0)
|| (!Property->Identical_InContainer(DataContainer, OptionalDefaultDataContainer, ArrayIndex) && !IsInstancedSubobjectLambda(ArrayIndex)))
{
FNativizationSummaryHelper::PropertyUsed(Context.GetCurrentlyGeneratedClass(), Property);
FString PathToMember;
UBlueprintGeneratedClass* PropertyOwnerAsBPGC = Cast<UBlueprintGeneratedClass>(Property->GetOwnerClass());
UScriptStruct* PropertyOwnerAsScriptStruct = Cast<UScriptStruct>(Property->GetOwnerStruct());
const bool bNoExportProperty = PropertyOwnerAsScriptStruct
&& PropertyOwnerAsScriptStruct->IsNative()
&& (PropertyOwnerAsScriptStruct->StructFlags & STRUCT_NoExport)
// && !PropertyOwnerAsScriptStruct->GetBoolMetaData(TEXT("BlueprintType"))
&& ensure(EPropertyAccessOperator::Dot == AccessOperator);
if (PropertyOwnerAsBPGC && !Context.Dependencies.WillClassBeConverted(PropertyOwnerAsBPGC))
{
ensure(EPropertyAccessOperator::None != AccessOperator);
const FString OperatorStr = (EPropertyAccessOperator::Dot == AccessOperator) ? TEXT("&") : TEXT("");
const FString ContainerStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("this") : FString::Printf(TEXT("%s(%s)"), *OperatorStr, *OuterPath);
PathToMember = FString::Printf(TEXT("FUnconvertedWrapper__%s(%s).GetRef__%s()"), *FEmitHelper::GetCppName(PropertyOwnerAsBPGC), *ContainerStr
, *UnicodeToCPPIdentifier(Property->GetName(), false, nullptr));
}
else if (bNoExportProperty || Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPrivate) || (!bAllowProtected && Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierProtected)))
{
const UBoolProperty* BoolProperty = Cast<const UBoolProperty>(Property);
const bool bBietfield = BoolProperty && !BoolProperty->IsNativeBool();
const FString OperatorStr = (EPropertyAccessOperator::Dot == AccessOperator) ? TEXT("&") : TEXT("");
const FString ContainerStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("this") : OuterPath;
if (bBietfield)
{
const FString PropertyLocalName = FEmitHelper::GenerateGetPropertyByName(Context, Property);
const FString ValueStr = Context.ExportTextItem(Property, Property->ContainerPtrToValuePtr<uint8>(DataContainer, ArrayIndex));
Context.AddLine(FString::Printf(TEXT("(((UBoolProperty*)%s)->%s(%s(%s), %s, %d));")
, *PropertyLocalName
, GET_FUNCTION_NAME_STRING_CHECKED(UBoolProperty, SetPropertyValue_InContainer)
, *OperatorStr
, *ContainerStr
, *ValueStr
, ArrayIndex));
continue;
}
const FString GetPtrStr = bNoExportProperty
? FEmitHelper::AccessInaccessiblePropertyUsingOffset(Context, Property, ContainerStr, OperatorStr, ArrayIndex)
: FEmitHelper::AccessInaccessibleProperty(Context, Property, ContainerStr, OperatorStr, ArrayIndex, ENativizedTermUsage::UnspecifiedOrReference, nullptr);
PathToMember = Context.GenerateUniqueLocalName();
Context.AddLine(FString::Printf(TEXT("auto& %s = %s;"), *PathToMember, *GetPtrStr));
}
else
{
const FString AccessOperatorStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("")
: ((EPropertyAccessOperator::Pointer == AccessOperator) ? TEXT("->") : TEXT("."));
const bool bStaticArray = (Property->ArrayDim > 1);
const FString ArrayPost = bStaticArray ? FString::Printf(TEXT("[%d]"), ArrayIndex) : TEXT("");
PathToMember = FString::Printf(TEXT("%s%s%s%s"), *OuterPath, *AccessOperatorStr, *FEmitHelper::GetCppName(Property), *ArrayPost);
}
{
const uint8* ValuePtr = Property->ContainerPtrToValuePtr<uint8>(DataContainer, ArrayIndex);
const uint8* DefaultValuePtr = OptionalDefaultDataContainer ? Property->ContainerPtrToValuePtr<uint8>(OptionalDefaultDataContainer, ArrayIndex) : nullptr;
InnerGenerate(Context, Property, PathToMember, ValuePtr, DefaultValuePtr);
}
}
}
}
void FEmitDefaultValueHelper::GenerateGetDefaultValue(const UUserDefinedStruct* Struct, FEmitterLocalContext& Context)
{
check(Struct);
const FString StructName = FEmitHelper::GetCppName(Struct);
Context.Header.AddLine(FString::Printf(TEXT("static %s GetDefaultValue()"), *StructName));
Context.Header.AddLine(TEXT("{"));
Context.Header.IncreaseIndent();
Context.Header.AddLine(FString::Printf(TEXT("FStructOnScope StructOnScope(%s::StaticStruct());"), *StructName));
Context.Header.AddLine(FString::Printf(TEXT("%s& DefaultData__ = *((%s*)StructOnScope.GetStructMemory());"), *StructName, *StructName));
{
TGuardValue<FCodeText*> OriginalDefaultTarget(Context.DefaultTarget, &Context.Header);
FStructOnScope StructData(Struct);
FStructureEditorUtils::Fill_MakeStructureDefaultValue(Struct, StructData.GetStructMemory());
FStructOnScope RawDefaultStructOnScope(Struct);
for (auto Property : TFieldRange<const UProperty>(Struct))
{
OuterGenerate(Context, Property, TEXT("DefaultData__"), StructData.GetStructMemory(), RawDefaultStructOnScope.GetStructMemory(), EPropertyAccessOperator::Dot);
}
}
Context.Header.AddLine(TEXT("return DefaultData__;"));
Context.Header.DecreaseIndent();
Context.Header.AddLine(TEXT("}"));
}
void FEmitDefaultValueHelper::InnerGenerate(FEmitterLocalContext& Context, const UProperty* Property, const FString& PathToMember, const uint8* ValuePtr, const uint8* DefaultValuePtr, bool bWithoutFirstConstructionLine)
{
auto OneLineConstruction = [](FEmitterLocalContext& LocalContext, const UProperty* LocalProperty, const uint8* LocalValuePtr, FString& OutSingleLine, bool bGenerateEmptyStructConstructor) -> bool
{
bool bComplete = true;
FString ValueStr = HandleSpecialTypes(LocalContext, LocalProperty, LocalValuePtr);
if (ValueStr.IsEmpty())
{
ValueStr = LocalContext.ExportTextItem(LocalProperty, LocalValuePtr);
auto StructProperty = Cast<const UStructProperty>(LocalProperty);
if (ValueStr.IsEmpty() && StructProperty)
{
check(StructProperty->Struct);
if (bGenerateEmptyStructConstructor)
{
ValueStr = FString::Printf(TEXT("%s%s"), *FEmitHelper::GetCppName(StructProperty->Struct), FEmitHelper::EmptyDefaultConstructor(StructProperty->Struct)); //don;t override existing values
}
bComplete = false;
}
else if (ValueStr.IsEmpty())
{
UE_LOG(LogK2Compiler, Error, TEXT("FEmitDefaultValueHelper Cannot generate initialization: %s"), *LocalProperty->GetPathName());
}
}
OutSingleLine += ValueStr;
return bComplete;
};
const UStructProperty* StructProperty = Cast<const UStructProperty>(Property);
check(!StructProperty || StructProperty->Struct);
const UArrayProperty* ArrayProperty = Cast<const UArrayProperty>(Property);
check(!ArrayProperty || ArrayProperty->Inner);
const USetProperty* SetProperty = Cast<const USetProperty>(Property);
check(!SetProperty || SetProperty->ElementProp);
const UMapProperty* MapProperty = Cast<const UMapProperty>(Property);
check(!MapProperty || (MapProperty->KeyProp && MapProperty->ValueProp));
if (!bWithoutFirstConstructionLine)
{
FString ValueStr;
const bool bComplete = OneLineConstruction(Context, Property, ValuePtr, ValueStr, false);
if (!ValueStr.IsEmpty())
{
Context.AddLine(FString::Printf(TEXT("%s = %s;"), *PathToMember, *ValueStr));
}
// array initialization "array_var = TArray<..>()" is complete, but it still needs items.
if (bComplete && !ArrayProperty && !SetProperty && !MapProperty)
{
return;
}
}
if (StructProperty)
{
// Create default struct instance, only when DefaultValuePtr is null.
FStructOnScope DefaultStructOnScope(DefaultValuePtr ? nullptr : StructProperty->Struct);
for (auto LocalProperty : TFieldRange<const UProperty>(StructProperty->Struct))
{
OuterGenerate(Context, LocalProperty, PathToMember, ValuePtr
, (DefaultValuePtr ? DefaultValuePtr : DefaultStructOnScope.GetStructMemory())
, EPropertyAccessOperator::Dot);
}
}
if (ArrayProperty)
{
FScriptArrayHelper ScriptArrayHelper(ArrayProperty, ValuePtr);
if (ScriptArrayHelper.Num())
{
const UStructProperty* InnerStructProperty = Cast<const UStructProperty>(ArrayProperty->Inner);
const bool bInitializeWithoutScriptStruct = InnerStructProperty && InnerStructProperty->Struct
&& InnerStructProperty->Struct->IsNative()
&& (InnerStructProperty->Struct->StructFlags & STRUCT_NoExport);
const UScriptStruct* RegularInnerStruct = nullptr;
if(!bInitializeWithoutScriptStruct)
{
if (InnerStructProperty && !FEmitDefaultValueHelper::SpecialStructureConstructor(InnerStructProperty->Struct, nullptr, nullptr))
{
RegularInnerStruct = InnerStructProperty->Struct;
}
}
if(RegularInnerStruct)
{
Context.AddLine(FString::Printf(TEXT("%s.%s(%d);"), *PathToMember, TEXT("AddUninitialized"), ScriptArrayHelper.Num()));
Context.AddLine(FString::Printf(TEXT("%s->%s(%s.GetData(), %d);")
, *Context.FindGloballyMappedObject(RegularInnerStruct, UScriptStruct::StaticClass())
, GET_FUNCTION_NAME_STRING_CHECKED(UStruct, InitializeStruct)
, *PathToMember
, ScriptArrayHelper.Num()));
const FStructOnScope DefaultStruct(RegularInnerStruct);
for (int32 Index = 0; Index < ScriptArrayHelper.Num(); ++Index)
{
const FString ArrayElementRefName = Context.GenerateUniqueLocalName();
Context.AddLine(FString::Printf(TEXT("auto& %s = %s[%d];"), *ArrayElementRefName, *PathToMember, Index));
InnerGenerate(Context, ArrayProperty->Inner, ArrayElementRefName, ScriptArrayHelper.GetRawPtr(Index), nullptr, true);
}
}
else
{
Context.AddLine(FString::Printf(TEXT("%s.%s(%d);"), *PathToMember, TEXT("Reserve"), ScriptArrayHelper.Num()));
for (int32 Index = 0; Index < ScriptArrayHelper.Num(); ++Index)
{
const uint8* LocalValuePtr = ScriptArrayHelper.GetRawPtr(Index);
FString ValueStr;
bool bComplete = OneLineConstruction(Context, ArrayProperty->Inner, LocalValuePtr, ValueStr, bInitializeWithoutScriptStruct);
ensure(bComplete || bInitializeWithoutScriptStruct);
Context.AddLine(FString::Printf(TEXT("%s.Add(%s);"), *PathToMember, *ValueStr));
if (bInitializeWithoutScriptStruct && !bComplete)
{
InnerGenerate(Context, ArrayProperty->Inner, FString::Printf(TEXT("%s[%d]"), *PathToMember, Index), LocalValuePtr, nullptr, true);
}
}
}
}
}
else if(SetProperty)
{
FScriptSetHelper ScriptSetHelper(SetProperty, ValuePtr);
if (ScriptSetHelper.Num())
{
const UStructProperty* ElementStructProperty = Cast<const UStructProperty>(SetProperty->ElementProp);
Context.AddLine(FString::Printf(TEXT("%s.Reserve(%d);"), *PathToMember, ScriptSetHelper.Num()));
if(ElementStructProperty)
{
// declare a temporary scratch struct for the purpose of initialization. We create a scope so that
// we don't have to generate a new name for it every time we initialize a TSet:
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(TEXT("%s SetMemberScratchInitializer;"), *FEmitHelper::GetCppName(ElementStructProperty->Struct)));
int32 Size = ScriptSetHelper.Num();
for (int32 I = 0; Size; ++I)
{
if(ScriptSetHelper.IsValidIndex(I))
{
--Size;
// populate SetMemberScratchInitializer:
InnerGenerate(Context, SetProperty->ElementProp, TEXT("SetMemberScratchInitializer"), ScriptSetHelper.GetElementPtr(I), nullptr, true);
// Move into the TSet:
Context.AddLine(FString::Printf(TEXT("%s.Add(MoveTemp(SetMemberScratchInitializer));"), *PathToMember));
// reset SetMemberScratchInitializer:
Context.AddLine(FString::Printf(TEXT("SetMemberScratchInitializer = %s();"), *FEmitHelper::GetCppName(ElementStructProperty->Struct)));
}
}
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
else
{
int32 Size = ScriptSetHelper.Num();
for (int32 I = 0; Size; ++I)
{
if(ScriptSetHelper.IsValidIndex(I))
{
--Size;
FString ValueStr;
ensure(OneLineConstruction(Context, SetProperty->ElementProp, ScriptSetHelper.GetElementPtr(I), ValueStr, false));
Context.AddLine(FString::Printf(TEXT("%s.Add(%s);"), *PathToMember, *ValueStr));
}
}
}
}
}
else if(MapProperty)
{
FScriptMapHelper ScriptMapHelper(MapProperty, ValuePtr);
if (ScriptMapHelper.Num())
{
const UStructProperty* KeyStructProperty = Cast<const UStructProperty>(MapProperty->KeyProp);
const UStructProperty* ValueStructProperty = Cast<const UStructProperty>(MapProperty->ValueProp);
Context.AddLine(FString::Printf(TEXT("%s.Reserve(%d);"), *PathToMember, ScriptMapHelper.Num()));
// Split into 4 cases. They are all similar (add the Key/Value to the Map), but structs are significantly more complicated to initialize than primitives:
if(KeyStructProperty && ValueStructProperty)
{
// will add indentation and opening/closing brackets
// used to manage locals that were added via FEmitHelper::GenerateGetPropertyByName()
// (removes them from the cache so we don't try to use them outside of the scope)
FScopeBlock NestedScopeBlock(Context);
Context.AddLine(FString::Printf(TEXT("%s MapKeyScratchInitializer;"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
Context.AddLine(FString::Printf(TEXT("%s MapValueScratchInitializer;"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
int32 Size = ScriptMapHelper.Num();
for (int32 I = 0; Size; ++I)
{
if(ScriptMapHelper.IsValidIndex(I))
{
--Size;
// populate MapKeyScratchInitializer and MapValueScratchInitializer:
InnerGenerate(Context, MapProperty->KeyProp, TEXT("MapKeyScratchInitializer"), ScriptMapHelper.GetKeyPtr(I), nullptr, true);
InnerGenerate(Context, MapProperty->ValueProp, TEXT("MapValueScratchInitializer"), ScriptMapHelper.GetValuePtr(I), nullptr, true);
// Move into the TMap:
Context.AddLine(FString::Printf(TEXT("%s.Add(MoveTemp(MapKeyScratchInitializer), MoveTemp(MapValueScratchInitializer));"), *PathToMember));
// reset MapKeyScratchInitializer and MapValueScratchInitializer:
Context.AddLine(FString::Printf(TEXT("MapKeyScratchInitializer = %s();"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
Context.AddLine(FString::Printf(TEXT("MapValueScratchInitializer = %s();"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
}
}
}
else if( KeyStructProperty )
{
// will add indentation and opening/closing brackets
// used to manage locals that were added via FEmitHelper::GenerateGetPropertyByName()
// (removes them from the cache so we don't try to use them outside of the scope)
FScopeBlock NestedScopeBlock(Context);
Context.AddLine(FString::Printf(TEXT("%s MapKeyScratchInitializer;"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
int32 Size = ScriptMapHelper.Num();
for (int32 I = 0; Size; ++I)
{
if(ScriptMapHelper.IsValidIndex(I))
{
--Size;
// populate MapKeyScratchInitializer:
InnerGenerate(Context, MapProperty->KeyProp, TEXT("MapKeyScratchInitializer"), ScriptMapHelper.GetKeyPtr(I), nullptr, true);
FString ValueStr;
ensure(OneLineConstruction(Context, MapProperty->ValueProp, ScriptMapHelper.GetValuePtr(I), ValueStr, false));
// Move into the TMap:
Context.AddLine(FString::Printf(TEXT("%s.Add(MoveTemp(MapKeyScratchInitializer), %s);"), *PathToMember, *ValueStr));
// reset MapKeyScratchInitializer:
Context.AddLine(FString::Printf(TEXT("MapKeyScratchInitializer = %s();"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
}
}
}
else if( ValueStructProperty )
{
// will add indentation and opening/closing brackets
// used to manage locals that were added via FEmitHelper::GenerateGetPropertyByName()
// (removes them from the cache so we don't try to use them outside of the scope)
FScopeBlock NestedScopeBlock(Context);
Context.AddLine(FString::Printf(TEXT("%s MapValueScratchInitializer;"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
int32 Size = ScriptMapHelper.Num();
for (int32 I = 0; Size; ++I)
{
if(ScriptMapHelper.IsValidIndex(I))
{
--Size;
// populate MapValueScratchInitializer:
InnerGenerate(Context, MapProperty->ValueProp, TEXT("MapValueScratchInitializer"), ScriptMapHelper.GetValuePtr(I), nullptr, true);
FString KeyStr;
ensure(OneLineConstruction(Context, MapProperty->KeyProp, ScriptMapHelper.GetKeyPtr(I), KeyStr, false));
// Move into the TMap:
Context.AddLine(FString::Printf(TEXT("%s.Add(%s, MoveTemp(MapValueScratchInitializer));"), *PathToMember, *KeyStr));
// reset MapValueScratchInitializer:
Context.AddLine(FString::Printf(TEXT("MapValueScratchInitializer = %s();"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
}
}
}
else
{
int32 Size = ScriptMapHelper.Num();
for (int32 I = 0; Size; ++I)
{
if(ScriptMapHelper.IsValidIndex(I))
{
--Size;
FString KeyStr;
ensure(OneLineConstruction(Context, MapProperty->KeyProp, ScriptMapHelper.GetKeyPtr(I), KeyStr, false));
FString ValueStr;
ensure(OneLineConstruction(Context, MapProperty->ValueProp, ScriptMapHelper.GetValuePtr(I), ValueStr, false));
Context.AddLine(FString::Printf(TEXT("%s.Add(%s, %s);"), *PathToMember, *KeyStr, *ValueStr));
}
}
}
}
}
}
bool FEmitDefaultValueHelper::SpecialStructureConstructor(const UStruct* Struct, const uint8* ValuePtr, /*out*/ FString* OutResult)
{
check(ValuePtr || !OutResult);
if (FLatentActionInfo::StaticStruct() == Struct)
{
if (OutResult)
{
const FLatentActionInfo* LatentActionInfo = reinterpret_cast<const FLatentActionInfo*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FLatentActionInfo(%d, %d, TEXT(\"%s\"), this)")
, LatentActionInfo->Linkage
, LatentActionInfo->UUID
, *LatentActionInfo->ExecutionFunction.ToString().ReplaceCharWithEscapedChar());
}
return true;
}
if (TBaseStructure<FTransform>::Get() == Struct)
{
if (OutResult)
{
const FTransform* Transform = reinterpret_cast<const FTransform*>(ValuePtr);
const auto Rotation = Transform->GetRotation();
const auto Translation = Transform->GetTranslation();
const auto Scale = Transform->GetScale3D();
*OutResult = FString::Printf(TEXT("FTransform( FQuat(%s,%s,%s,%s), FVector(%s,%s,%s), FVector(%s,%s,%s) )"),
*FEmitHelper::FloatToString(Rotation.X), *FEmitHelper::FloatToString(Rotation.Y), *FEmitHelper::FloatToString(Rotation.Z), *FEmitHelper::FloatToString(Rotation.W),
*FEmitHelper::FloatToString(Translation.X), *FEmitHelper::FloatToString(Translation.Y), *FEmitHelper::FloatToString(Translation.Z),
*FEmitHelper::FloatToString(Scale.X), *FEmitHelper::FloatToString(Scale.Y), *FEmitHelper::FloatToString(Scale.Z));
}
return true;
}
if (TBaseStructure<FVector>::Get() == Struct)
{
if (OutResult)
{
const FVector* Vector = reinterpret_cast<const FVector*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FVector(%s, %s, %s)"), *FEmitHelper::FloatToString(Vector->X), *FEmitHelper::FloatToString(Vector->Y), *FEmitHelper::FloatToString(Vector->Z));
}
return true;
}
if (TBaseStructure<FGuid>::Get() == Struct)
{
if (OutResult)
{
const FGuid* Guid = reinterpret_cast<const FGuid*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FGuid(0x%08X, 0x%08X, 0x%08X, 0x%08X)"), Guid->A, Guid->B, Guid->C, Guid->D);
}
return true;
}
if (TBaseStructure<FRotator>::Get() == Struct)
{
if (OutResult)
{
const FRotator* Rotator = reinterpret_cast<const FRotator*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FRotator(%s, %s, %s)"), *FEmitHelper::FloatToString(Rotator->Pitch), *FEmitHelper::FloatToString(Rotator->Yaw), *FEmitHelper::FloatToString(Rotator->Roll));
}
return true;
}
if (TBaseStructure<FLinearColor>::Get() == Struct)
{
if (OutResult)
{
const FLinearColor* LinearColor = reinterpret_cast<const FLinearColor*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FLinearColor(%s, %s, %s, %s)"), *FEmitHelper::FloatToString(LinearColor->R), *FEmitHelper::FloatToString(LinearColor->G), *FEmitHelper::FloatToString(LinearColor->B), *FEmitHelper::FloatToString(LinearColor->A));
}
return true;
}
if (TBaseStructure<FColor>::Get() == Struct)
{
if (OutResult)
{
const FColor* Color = reinterpret_cast<const FColor*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FColor(%d, %d, %d, %d)"), Color->R, Color->G, Color->B, Color->A);
}
return true;
}
if (TBaseStructure<FVector2D>::Get() == Struct)
{
if (OutResult)
{
const FVector2D* Vector2D = reinterpret_cast<const FVector2D*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FVector2D(%s, %s)"), *FEmitHelper::FloatToString(Vector2D->X), *FEmitHelper::FloatToString(Vector2D->Y));
}
return true;
}
if (TBaseStructure<FBox2D>::Get() == Struct)
{
if (OutResult)
{
const FBox2D* Box2D = reinterpret_cast<const FBox2D*>(ValuePtr);
*OutResult = FString::Printf(TEXT("CreateFBox2D(FVector2D(%s, %s), FVector2D(%s, %s), %s)")
, *FEmitHelper::FloatToString(Box2D->Min.X)
, *FEmitHelper::FloatToString(Box2D->Min.Y)
, *FEmitHelper::FloatToString(Box2D->Max.X)
, *FEmitHelper::FloatToString(Box2D->Max.Y)
, Box2D->bIsValid ? TEXT("true") : TEXT("false"));
}
return true;
}
if (TBaseStructure<FFloatRangeBound>::Get() == Struct)
{
if (OutResult)
{
const FFloatRangeBound* FloatRangeBound = reinterpret_cast<const FFloatRangeBound*>(ValuePtr);
if (FloatRangeBound->IsExclusive())
{
*OutResult = FString::Printf(TEXT("FFloatRangeBound::%s(%s)"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Exclusive), *FEmitHelper::FloatToString(FloatRangeBound->GetValue()));
}
if (FloatRangeBound->IsInclusive())
{
*OutResult = FString::Printf(TEXT("FFloatRangeBound::%s(%s)"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Inclusive), *FEmitHelper::FloatToString(FloatRangeBound->GetValue()));
}
if (FloatRangeBound->IsOpen())
{
*OutResult = FString::Printf(TEXT("FFloatRangeBound::%s()"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Open));
}
}
return true;
}
if (TBaseStructure<FFloatRange>::Get() == Struct)
{
if (OutResult)
{
const FFloatRange* FloatRangeBound = reinterpret_cast<const FFloatRange*>(ValuePtr);
FString LowerBoundStr;
FFloatRangeBound LowerBound = FloatRangeBound->GetLowerBound();
SpecialStructureConstructor(TBaseStructure<FFloatRangeBound>::Get(), (uint8*)&LowerBound, &LowerBoundStr);
FString UpperBoundStr;
FFloatRangeBound UpperBound = FloatRangeBound->GetUpperBound();
SpecialStructureConstructor(TBaseStructure<FFloatRangeBound>::Get(), (uint8*)&UpperBound, &UpperBoundStr);
*OutResult = FString::Printf(TEXT("FFloatRange(%s, %s)"), *LowerBoundStr, *UpperBoundStr);
}
return true;
}
if (TBaseStructure<FInt32RangeBound>::Get() == Struct)
{
if (OutResult)
{
const FInt32RangeBound* RangeBound = reinterpret_cast<const FInt32RangeBound*>(ValuePtr);
if (RangeBound->IsExclusive())
{
*OutResult = FString::Printf(TEXT("FInt32RangeBound::%s(%d)"), GET_FUNCTION_NAME_STRING_CHECKED(FInt32RangeBound, Exclusive), RangeBound->GetValue());
}
if (RangeBound->IsInclusive())
{
*OutResult = FString::Printf(TEXT("FInt32RangeBound::%s(%d)"), GET_FUNCTION_NAME_STRING_CHECKED(FInt32RangeBound, Exclusive), RangeBound->GetValue());
}
if (RangeBound->IsOpen())
{
*OutResult = FString::Printf(TEXT("FInt32RangeBound::%s()"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Open));
}
}
return true;
}
if (TBaseStructure<FInt32Range>::Get() == Struct)
{
if (OutResult)
{
const FInt32Range* RangeBound = reinterpret_cast<const FInt32Range*>(ValuePtr);
FString LowerBoundStr;
FInt32RangeBound LowerBound = RangeBound->GetLowerBound();
SpecialStructureConstructor(TBaseStructure<FInt32RangeBound>::Get(), (uint8*)&LowerBound, &LowerBoundStr);
FString UpperBoundStr;
FInt32RangeBound UpperBound = RangeBound->GetUpperBound();
SpecialStructureConstructor(TBaseStructure<FInt32RangeBound>::Get(), (uint8*)&UpperBound, &UpperBoundStr);
*OutResult = FString::Printf(TEXT("FInt32Range(%s, %s)"), *LowerBoundStr, *UpperBoundStr);
}
return true;
}
if (TBaseStructure<FFloatInterval>::Get() == Struct)
{
if (OutResult)
{
const FFloatInterval* Interval = reinterpret_cast<const FFloatInterval*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FFloatInterval(%s, %s)"), *FEmitHelper::FloatToString(Interval->Min), *FEmitHelper::FloatToString(Interval->Max));
}
return true;
}
if (TBaseStructure<FInt32Interval>::Get() == Struct)
{
if (OutResult)
{
const FInt32Interval* Interval = reinterpret_cast<const FInt32Interval*>(ValuePtr);
*OutResult = FString::Printf(TEXT("FFloatInterval(%d, %d)"), Interval->Min, Interval->Max);
}
return true;
}
return false;
}
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();
}
struct FNonativeComponentData
{
const USCS_Node* SCSNode;
FString NativeVariablePropertyName;
UActorComponent* ComponentTemplate;
UObject* ObjectToCompare;
////
FString ParentVariableName;
bool bSetNativeCreationMethod;
/** Socket/Bone that Component might attach to */
FName AttachToName;
bool bIsRoot;
FNonativeComponentData()
: SCSNode(nullptr)
, ComponentTemplate(nullptr)
, ObjectToCompare(nullptr)
, bSetNativeCreationMethod(false)
, bIsRoot(false)
{
}
bool HandledAsSpecialProperty(FEmitterLocalContext& Context, const UProperty* Property)
{
// skip relative location and rotation. THey are ignored for root components created from scs (and they probably should be reset by scs editor).
if (bIsRoot && (Property->GetOuter() == USceneComponent::StaticClass()))
{
UProperty* RelativeLocationProperty = USceneComponent::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(USceneComponent, RelativeLocation));
UProperty* RelativeRotationProperty = USceneComponent::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(USceneComponent, RelativeRotation));
if ((Property == RelativeLocationProperty) || (Property == RelativeRotationProperty))
{
return true;
}
}
return false;
}
void EmitProperties(FEmitterLocalContext& Context)
{
ensure(!NativeVariablePropertyName.IsEmpty());
if (bSetNativeCreationMethod)
{
Context.AddLine(FString::Printf(TEXT("%s->%s = EComponentCreationMethod::Native;"), *NativeVariablePropertyName, GET_MEMBER_NAME_STRING_CHECKED(UActorComponent, CreationMethod)));
}
if (!ParentVariableName.IsEmpty())
{
const FString SocketName = (AttachToName == NAME_None) ? FString() : FString::Printf(TEXT(", TEXT(\"%s\")"), *AttachToName.ToString());
Context.AddLine(FString::Printf(TEXT("%s->%s(%s, FAttachmentTransformRules::KeepRelativeTransform %s);")
, *NativeVariablePropertyName
, GET_FUNCTION_NAME_STRING_CHECKED(USceneComponent, AttachToComponent)
, *ParentVariableName, *SocketName));
// AttachTo is called first in case some properties will be overridden.
}
bool bBodyInstanceIsAlreadyHandled = false;
UProperty* BodyInstanceProperty = UPrimitiveComponent::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UPrimitiveComponent, BodyInstance));
UPrimitiveComponent* PrimitiveComponent = Cast<UPrimitiveComponent>(ComponentTemplate);
if(PrimitiveComponent)
{
const FName CollisionProfileName = PrimitiveComponent->BodyInstance.GetCollisionProfileName();
UPrimitiveComponent* ComponentArchetype = Cast<UPrimitiveComponent>(ObjectToCompare);
const FName ComponentArchetypeCollisionProfileName = ComponentArchetype ? ComponentArchetype->BodyInstance.GetCollisionProfileName() : NAME_None;
if (CollisionProfileName != ComponentArchetypeCollisionProfileName)
{
FStructOnScope BodyInstanceToCompare(FBodyInstance::StaticStruct());
if (ComponentArchetype)
{
FBodyInstance::StaticStruct()->CopyScriptStruct(BodyInstanceToCompare.GetStructMemory(), &ComponentArchetype->BodyInstance);
}
((FBodyInstance*)BodyInstanceToCompare.GetStructMemory())->SetCollisionProfileName(CollisionProfileName);
const FString PathToMember = FString::Printf(TEXT("%s->BodyInstance"), *NativeVariablePropertyName);
Context.AddLine(FString::Printf(TEXT("%s.SetCollisionProfileName(FName(TEXT(\"%s\")));"), *PathToMember, *CollisionProfileName.ToString().ReplaceCharWithEscapedChar()));
FEmitDefaultValueHelper::InnerGenerate(Context, BodyInstanceProperty, PathToMember, (const uint8*)&PrimitiveComponent->BodyInstance, BodyInstanceToCompare.GetStructMemory());
bBodyInstanceIsAlreadyHandled = true;
}
}
UClass* ComponentClass = ComponentTemplate->GetClass();
for (auto Property : TFieldRange<const UProperty>(ComponentClass))
{
if (bBodyInstanceIsAlreadyHandled && (Property == BodyInstanceProperty))
{
continue;
}
if (HandledAsSpecialProperty(Context, Property))
{
continue;
}
FEmitDefaultValueHelper::OuterGenerate(Context, Property, NativeVariablePropertyName
, reinterpret_cast<const uint8*>(ComponentTemplate)
, reinterpret_cast<const uint8*>(ObjectToCompare)
, FEmitDefaultValueHelper::EPropertyAccessOperator::Pointer);
}
}
void EmitForcedPostLoad(FEmitterLocalContext& Context)
{
Context.AddLine(FString::Printf(TEXT("if(%s && !%s->%s())"), *NativeVariablePropertyName, *NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UActorComponent, IsTemplate)));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(TEXT("%s->%s(RF_NeedPostLoad |RF_NeedPostLoadSubobjects);"), *NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UActorComponent, SetFlags)));
Context.AddLine(FString::Printf(TEXT("%s->%s();"), *NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UActorComponent, ConditionalPostLoad)));
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
};
FString FEmitDefaultValueHelper::HandleNonNativeComponent(FEmitterLocalContext& Context, const USCS_Node* Node
, TSet<const UProperty*>& OutHandledProperties, TArray<FString>& NativeCreatedComponentProperties
, const USCS_Node* ParentNode, TArray<FNonativeComponentData>& ComponenntsToInit
, bool bBlockRecursion)
{
check(Node);
check(Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::CommonConstructor);
FString NativeVariablePropertyName;
UBlueprintGeneratedClass* BPGC = CastChecked<UBlueprintGeneratedClass>(Context.GetCurrentlyGeneratedClass());
if (UActorComponent* ComponentTemplate = Node->GetActualComponentTemplate(BPGC))
{
const FString VariableCleanName = Node->GetVariableName().ToString();
const UObjectProperty* VariableProperty = FindField<UObjectProperty>(BPGC, *VariableCleanName);
if (VariableProperty)
{
NativeVariablePropertyName = FEmitHelper::GetCppName(VariableProperty);
OutHandledProperties.Add(VariableProperty);
}
else
{
NativeVariablePropertyName = VariableCleanName;
}
//TODO: UGLY HACK UE-40026
if (bBlockRecursion && Context.CommonSubobjectsMap.Contains(ComponentTemplate))
{
return FString();
}
Context.AddCommonSubObject_InConstructor(ComponentTemplate, NativeVariablePropertyName);
if (ComponentTemplate->GetOuter() == BPGC)
{
FNonativeComponentData NonativeComponentData;
NonativeComponentData.SCSNode = Node;
NonativeComponentData.NativeVariablePropertyName = NativeVariablePropertyName;
NonativeComponentData.ComponentTemplate = ComponentTemplate;
USCS_Node* RootComponentNode = nullptr;
Node->GetSCS()->GetSceneRootComponentTemplate(&RootComponentNode);
NonativeComponentData.bIsRoot = RootComponentNode == Node;
UClass* ComponentClass = ComponentTemplate->GetClass();
check(ComponentClass != nullptr);
UObject* ObjectToCompare = ComponentClass->GetDefaultObject(false);
if (ComponentTemplate->HasAnyFlags(RF_InheritableComponentTemplate))
{
ObjectToCompare = Node->GetActualComponentTemplate(Cast<UBlueprintGeneratedClass>(BPGC->GetSuperClass()));
}
else
{
Context.AddLine(FString::Printf(TEXT("%s%s = CreateDefaultSubobject<%s>(TEXT(\"%s\"));")
, (VariableProperty == nullptr) ? TEXT("auto ") : TEXT("")
, *NativeVariablePropertyName
, *FEmitHelper::GetCppName(ComponentClass)
, *VariableCleanName));
NonativeComponentData.bSetNativeCreationMethod = true;
NativeCreatedComponentProperties.Add(NativeVariablePropertyName);
FString ParentVariableName;
if (ParentNode)
{
const FString CleanParentVariableName = ParentNode->GetVariableName().ToString();
const UObjectProperty* ParentVariableProperty = FindField<UObjectProperty>(BPGC, *CleanParentVariableName);
ParentVariableName = ParentVariableProperty ? FEmitHelper::GetCppName(ParentVariableProperty) : CleanParentVariableName;
}
else if (USceneComponent* ParentComponentTemplate = Node->GetParentComponentTemplate(CastChecked<UBlueprint>(BPGC->ClassGeneratedBy)))
{
ParentVariableName = Context.FindGloballyMappedObject(ParentComponentTemplate, USceneComponent::StaticClass());
}
NonativeComponentData.ParentVariableName = ParentVariableName;
NonativeComponentData.AttachToName = Node->AttachToName;
}
NonativeComponentData.ObjectToCompare = ObjectToCompare;
ComponenntsToInit.Add(NonativeComponentData);
}
}
// Recursively handle child nodes.
if (!bBlockRecursion)
{
for (auto ChildNode : Node->ChildNodes)
{
HandleNonNativeComponent(Context, ChildNode, OutHandledProperties, NativeCreatedComponentProperties, Node, ComponenntsToInit, bBlockRecursion);
}
}
return NativeVariablePropertyName;
}
struct FDependenciesHelper
{
public:
// Keep sync with FTypeSingletonCache::GenerateSingletonName
static FString GenerateZConstructor(UField* Item)
{
FString Result;
if (!ensure(Item))
{
return Result;
}
for (UObject* Outer = Item; Outer; Outer = Outer->GetOuter())
{
if (!Result.IsEmpty())
{
Result = TEXT("_") + Result;
}
if (Cast<UClass>(Outer) || Cast<UScriptStruct>(Outer))
{
FString OuterName = FEmitHelper::GetCppName(CastChecked<UField>(Outer), true);
Result = OuterName + Result;
// Structs can also have UPackage outer.
if (Cast<UClass>(Outer) || Cast<UPackage>(Outer->GetOuter()))
{
break;
}
}
else
{
Result = Outer->GetName() + Result;
}
}
// Can't use long package names in function names.
if (Result.StartsWith(TEXT("/Script/"), ESearchCase::CaseSensitive))
{
Result = FPackageName::GetShortName(Result);
}
const FString ClassString = Item->IsA<UClass>() ? TEXT("UClass") : TEXT("UScriptStruct");
return FString(TEXT("Z_Construct_")) + ClassString + TEXT("_") + Result + TEXT("()");
}
};
struct FFakeImportTableHelper
{
TSet<UObject*> SerializeBeforeSerializeClassDependencies;
TSet<UObject*> SerializeBeforeCreateCDODependencies;
FFakeImportTableHelper(UClass* SourceClass, UClass* OriginalClass, FEmitterLocalContext& Context)
{
if (ensure(SourceClass) && ensure(OriginalClass))
{
auto GatherDependencies = [&](UClass* InClass)
{
SerializeBeforeSerializeClassDependencies.Add(InClass->GetSuperClass());
for (const FImplementedInterface& ImplementedInterface : InClass->Interfaces)
{
SerializeBeforeSerializeClassDependencies.Add(ImplementedInterface.Class);
}
TArray<UObject*> ObjectsInsideClass;
GetObjectsWithOuter(InClass, ObjectsInsideClass, true);
for (UObject* Obj : ObjectsInsideClass)
{
UProperty* Property = Cast<UProperty>(Obj);
if (!Property)
{
continue;
}
const UProperty* OwnerProperty = Property->GetOwnerProperty();
if (!ensure(IsValid(OwnerProperty)))
{
continue;
}
// TODO:
// Let UDS_A contain UDS_B. Let UDS_B contain an array or a set of UDS_A. It causes a cyclic dependency.
// Should we try to fix it at this stage?
const bool bIsParam = (0 != (OwnerProperty->PropertyFlags & CPF_Parm)) && OwnerProperty->IsIn(InClass);
const bool bIsMemberVariable = (OwnerProperty->GetOuter() == InClass);
if (bIsParam || bIsMemberVariable) // Affects the class signature. It is necessary while ZCOnstructor/linking.
{
TArray<UObject*> LocalPreloadDependencies;
Property->GetPreloadDependencies(LocalPreloadDependencies);
for (UObject* Dependency : LocalPreloadDependencies)
{
const bool bDependencyMustBeSerializedBeforeClassIsLinked = Dependency
&& (Dependency->IsA<UScriptStruct>() || Dependency->IsA<UEnum>());
if (bDependencyMustBeSerializedBeforeClassIsLinked)
{
SerializeBeforeSerializeClassDependencies.Add(Dependency);
}
}
}
}
SerializeBeforeCreateCDODependencies.Add(InClass->GetSuperClass()->GetDefaultObject());
};
GatherDependencies(SourceClass);
GatherDependencies(OriginalClass);
auto GetClassesOfSubobjects = [&](TMap<UObject*, FString>& SubobjectsMap)
{
TArray<UObject*> Subobjects;
SubobjectsMap.GetKeys(Subobjects);
for (UObject* Subobject : Subobjects)
{
if (Subobject)
{
SerializeBeforeCreateCDODependencies.Add(Subobject->GetClass());
}
}
};
GetClassesOfSubobjects(Context.ClassSubobjectsMap);
GetClassesOfSubobjects(Context.CommonSubobjectsMap);
}
}
void FillDependencyData(const UObject* Asset, FCompactBlueprintDependencyData& CompactDataRef) const
{
ensure(Asset);
{
//Dynamic Class requires no non-native class, owner, archetype..
CompactDataRef.ClassDependency.bSerializationBeforeCreateDependency = false;
CompactDataRef.ClassDependency.bCreateBeforeCreateDependency = false;
const bool bDependencyNecessaryForLinking = SerializeBeforeSerializeClassDependencies.Contains(const_cast<UObject*>(Asset));
// Super Class, Interfaces, ScriptStructs, Enums..
CompactDataRef.ClassDependency.bSerializationBeforeSerializationDependency = bDependencyNecessaryForLinking;
// Everything else
CompactDataRef.ClassDependency.bCreateBeforeSerializationDependency = !bDependencyNecessaryForLinking;
}
{
//everything was created for class
CompactDataRef.CDODependency.bCreateBeforeCreateDependency = false;
// Classes of subobjects, created while CDO construction
CompactDataRef.CDODependency.bSerializationBeforeCreateDependency = SerializeBeforeCreateCDODependencies.Contains(const_cast<UObject*>(Asset));
// CDO is not serialized
CompactDataRef.CDODependency.bCreateBeforeSerializationDependency = false;
CompactDataRef.CDODependency.bSerializationBeforeSerializationDependency = false;
}
}
};
void FEmitDefaultValueHelper::AddStaticFunctionsForDependencies(FEmitterLocalContext& Context
, TSharedPtr<FGatherConvertedClassDependencies> ParentDependencies
, FCompilerNativizationOptions NativizationOptions)
{
// 1. GATHER UDS DEFAULT VALUE DEPENDENCIES
{
TSet<UObject*> References;
for (UUserDefinedStruct* UDS : Context.StructsWithDefaultValuesUsed)
{
FGatherConvertedClassDependencies::GatherAssetReferencedByUDSDefaultValue(References, UDS);
}
for (UObject* Obj : References)
{
Context.UsedObjectInCurrentClass.AddUnique(Obj);
}
}
// 2. ALL ASSETS TO LIST
TSet<const UObject*> AllDependenciesToHandle = Context.Dependencies.AllDependencies();
AllDependenciesToHandle.Append(Context.UsedObjectInCurrentClass);
AllDependenciesToHandle.Remove(nullptr);
// Special case, we don't need to load any dependencies from CoreUObject.
UPackage* CoreUObjectPackage = UProperty::StaticClass()->GetOutermost();
for (auto Iter = AllDependenciesToHandle.CreateIterator(); Iter; ++Iter)
{
if ((*Iter)->GetOutermost() == CoreUObjectPackage)
{
Iter.RemoveCurrent();
}
}
// HELPERS
auto SourceClass = Context.GetCurrentlyGeneratedClass();
auto OriginalClass = Context.Dependencies.FindOriginalClass(SourceClass);
const FString CppClassName = FEmitHelper::GetCppName(SourceClass);
FFakeImportTableHelper FakeImportTableHelper(SourceClass, OriginalClass, Context);
auto CreateAssetToLoadString = [&](const UObject* AssetObj) -> FString
{
UClass* AssetType = AssetObj->GetClass();
if (AssetType->IsChildOf<UUserDefinedEnum>())
{
AssetType = UEnum::StaticClass();
}
else if (AssetType->IsChildOf<UUserDefinedStruct>())
{
AssetType = UScriptStruct::StaticClass();
}
else if (AssetType->IsChildOf<UBlueprintGeneratedClass>() && Context.Dependencies.WillClassBeConverted(CastChecked<UBlueprintGeneratedClass>(AssetObj)))
{
AssetType = UDynamicClass::StaticClass();
}
const FString LongPackagePath = FPackageName::GetLongPackagePath(AssetObj->GetOutermost()->GetPathName());
return FString::Printf(TEXT("FBlueprintDependencyObjectRef(TEXT(\"%s\"), TEXT(\"%s\"), TEXT(\"%s\"), TEXT(\"%s\"), TEXT(\"%s\")),")
, *LongPackagePath
, *FPackageName::GetShortName(AssetObj->GetOutermost()->GetPathName())
, *AssetObj->GetName()
, *AssetType->GetOutermost()->GetPathName()
, *AssetType->GetName());
};
auto CreateDependencyRecord = [&](const UObject* InAsset, FString& OptionalComment) -> FCompactBlueprintDependencyData
{
ensure(InAsset);
if (InAsset && IsEditorOnlyObject(InAsset))
{
UE_LOG(LogK2Compiler, Warning, TEXT("Nativized %d depends on editor only asset: %s")
, (OriginalClass ? *OriginalClass->GetPathName() : *CppClassName)
,*InAsset->GetPathName());
OptionalComment = TEXT("Editor Only asset");
return FCompactBlueprintDependencyData{};
}
{
bool bNotForClient = false;
bool bNotForServer = false;
for (const UObject* Search = InAsset; Search && !Search->IsA(UPackage::StaticClass()); Search = Search->GetOuter())
{
bNotForClient = bNotForClient || !Search->NeedsLoadForClient();
bNotForServer = bNotForServer || !Search->NeedsLoadForServer();
}
if (bNotForServer && NativizationOptions.ServerOnlyPlatform)
{
OptionalComment = TEXT("Not for server");
return FCompactBlueprintDependencyData{};
}
if (bNotForClient && NativizationOptions.ClientOnlyPlatform)
{
OptionalComment = TEXT("Not for client");
return FCompactBlueprintDependencyData{};
}
}
FNativizationSummary::FDependencyRecord& DependencyRecord = FDependenciesGlobalMapHelper::FindDependencyRecord(InAsset);
ensure(DependencyRecord.Index >= 0);
if (DependencyRecord.NativeLine.IsEmpty())
{
DependencyRecord.NativeLine = CreateAssetToLoadString(InAsset);
}
FCompactBlueprintDependencyData Result;
Result.ObjectRefIndex = static_cast<int16>(DependencyRecord.Index);
FakeImportTableHelper.FillDependencyData(InAsset, Result);
return Result;
};
const bool bBootTimeEDL = false;
auto AddAssetArray = [&](const TArray<const UObject*>& Assets)
{
if (Assets.Num())
{
Context.AddLine(TEXT("const FCompactBlueprintDependencyData LocCompactBlueprintDependencyData[] ="));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
}
auto BlueprintDependencyTypeToString = [](FBlueprintDependencyType DependencyType) -> FString
{
return FString::Printf(TEXT("FBlueprintDependencyType(%s, %s, %s, %s)")
, DependencyType.bSerializationBeforeSerializationDependency ? TEXT("true") : TEXT("false")
, DependencyType.bCreateBeforeSerializationDependency ? TEXT("true") : TEXT("false")
, DependencyType.bSerializationBeforeCreateDependency ? TEXT("true") : TEXT("false")
, DependencyType.bCreateBeforeCreateDependency ? TEXT("true") : TEXT("false"));
};
for (const UObject* LocAsset : Assets)
{
FString OptionalComment;
const FCompactBlueprintDependencyData DependencyRecord = CreateDependencyRecord(LocAsset, OptionalComment);
Context.AddLine(FString::Printf(TEXT("{%d, %s, %s}, // %s %s ")
, DependencyRecord.ObjectRefIndex
, *BlueprintDependencyTypeToString(DependencyRecord.ClassDependency)
, *BlueprintDependencyTypeToString(DependencyRecord.CDODependency)
, *OptionalComment
, *LocAsset->GetFullName()));
}
if (Assets.Num())
{
Context.DecreaseIndent();
Context.AddLine(TEXT("};"));
Context.AddLine(TEXT("for(const FCompactBlueprintDependencyData CompactData : LocCompactBlueprintDependencyData)"));
Context.AddLine(TEXT("{"));
if (IsEventDrivenLoaderEnabledInCookedBuilds() && bBootTimeEDL)
{
Context.AddLine(TEXT("\tAssetsToLoad.Add(FBlueprintDependencyData("));
}
else
{
Context.AddLine(TEXT("\tAssetsToLoad.AddUnique(FBlueprintDependencyData("));
}
Context.AddLine(TEXT("\t\tF__NativeDependencies::Get(CompactData.ObjectRefIndex)"));
Context.AddLine(TEXT("\t\t,CompactData.ClassDependency"));
Context.AddLine(TEXT("\t\t,CompactData.CDODependency"));
Context.AddLine(TEXT("\t\t,CompactData.ObjectRefIndex"));
Context.AddLine(TEXT("\t));"));
Context.AddLine(TEXT("} "));
}
};
TSet<const UBlueprintGeneratedClass*> OtherBPGCs;
if (!IsEventDrivenLoaderEnabledInCookedBuilds() & !bBootTimeEDL)
{
for (const UObject* It : AllDependenciesToHandle)
{
if (const UBlueprintGeneratedClass* OtherBPGC = Cast<const UBlueprintGeneratedClass>(It))
{
const UBlueprint* BP = Cast<const UBlueprint>(OtherBPGC->ClassGeneratedBy);
if (Context.Dependencies.WillClassBeConverted(OtherBPGC) && BP && (BP->BlueprintType != EBlueprintType::BPTYPE_Interface))
{
OtherBPGCs.Add(OtherBPGC);
}
}
}
}
// 3. LIST OF UsedAssets
{
Context.AddLine(FString::Printf(TEXT("void %s::__StaticDependencies_DirectlyUsedAssets(TArray<FBlueprintDependencyData>& AssetsToLoad)"), *CppClassName));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
TArray<const UObject*> AssetsToAdd;
for (int32 UsedAssetIndex = 0; UsedAssetIndex < Context.UsedObjectInCurrentClass.Num(); ++UsedAssetIndex)
{
const UObject* LocAsset = Context.UsedObjectInCurrentClass[UsedAssetIndex];
ensure(AllDependenciesToHandle.Contains(LocAsset));
AssetsToAdd.Add(LocAsset);
AllDependenciesToHandle.Remove(LocAsset);
}
AddAssetArray(AssetsToAdd);
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
// 4. REMAINING DEPENDENCIES
{
Context.AddLine(FString::Printf(TEXT("void %s::__StaticDependenciesAssets(TArray<FBlueprintDependencyData>& AssetsToLoad)"), *CppClassName));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
if (!OtherBPGCs.Num() || (IsEventDrivenLoaderEnabledInCookedBuilds() && bBootTimeEDL))
{
Context.AddLine(FString(TEXT("__StaticDependencies_DirectlyUsedAssets(AssetsToLoad);")));
}
else
{
// To reduce the size of __StaticDependenciesAssets, all __StaticDependenciesAssets of listed BPs will be called.
FNativizationSummary::FDependencyRecord& DependencyRecord = FDependenciesGlobalMapHelper::FindDependencyRecord(OriginalClass);
ensure(DependencyRecord.Index >= 0);
if (DependencyRecord.NativeLine.IsEmpty())
{
DependencyRecord.NativeLine = CreateAssetToLoadString(OriginalClass);
}
Context.AddLine(FString::Printf(TEXT("const int16 __OwnIndex = %d;"), DependencyRecord.Index));
Context.AddLine(FString(TEXT("if(FBlueprintDependencyData::ContainsDependencyData(AssetsToLoad, __OwnIndex)) { return; }")));
Context.AddLine(TEXT("if(GEventDrivenLoaderEnabled && EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME){ __StaticDependencies_DirectlyUsedAssets(AssetsToLoad); }"));
Context.AddLine(TEXT("else"));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString(TEXT("const bool __FirstFunctionCall = !AssetsToLoad.Num();")));
Context.AddLine(FString(TEXT("TArray<FBlueprintDependencyData> Temp;")));
// Other __StaticDependenciesAssets fucntions should not see the assets added by __StaticDependencies_DirectlyUsedAssets
// But in the first function called the assets from __StaticDependencies_DirectlyUsedAssets must go first in unchanged order (to satisfy FConvertedBlueprintsDependencies::FillUsedAssetsInDynamicClass)
Context.AddLine(FString(TEXT("__StaticDependencies_DirectlyUsedAssets(__FirstFunctionCall ? AssetsToLoad : Temp);")));
Context.AddLine(FString(TEXT("TArray<FBlueprintDependencyData>& ArrayUnaffectedByDirectlyUsedAssets = __FirstFunctionCall ? Temp : AssetsToLoad;")));
Context.AddLine(FString(TEXT("ArrayUnaffectedByDirectlyUsedAssets.AddUnique(FBlueprintDependencyData(F__NativeDependencies::Get(__OwnIndex), {}, {}, __OwnIndex));")));
for (const UBlueprintGeneratedClass* OtherBPGC : OtherBPGCs)
{
Context.AddLine(FString::Printf(TEXT("%s::__StaticDependenciesAssets(ArrayUnaffectedByDirectlyUsedAssets);"), *FEmitHelper::GetCppName(OtherBPGC)));
}
Context.AddLine(FString(TEXT("FBlueprintDependencyData::AppendUniquely(AssetsToLoad, Temp);")));
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
if (IsEventDrivenLoaderEnabledInCookedBuilds() && bBootTimeEDL)
{
//TODO: remove stuff from CoreUObject
}
else
{
//WIthout EDL we don't need the native stuff.
for (auto Iter = AllDependenciesToHandle.CreateIterator(); Iter; ++Iter)
{
const UObject* ItObj = *Iter;
if (auto ObjAsClass = Cast<const UClass>(ItObj))
{
if (ObjAsClass->HasAnyClassFlags(CLASS_Native))
{
Iter.RemoveCurrent();
}
}
else if (ItObj && ItObj->IsA<UScriptStruct>() && !ItObj->IsA<UUserDefinedStruct>())
{
Iter.RemoveCurrent();
}
else if (ItObj && ItObj->IsA<UEnum>() && !ItObj->IsA<UUserDefinedEnum>())
{
Iter.RemoveCurrent();
}
}
}
AddAssetArray(AllDependenciesToHandle.Array());
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
}
void FEmitDefaultValueHelper::AddRegisterHelper(FEmitterLocalContext& Context)
{
auto SourceClass = Context.GetCurrentlyGeneratedClass();
auto OriginalClass = Context.Dependencies.FindOriginalClass(SourceClass);
const FString CppClassName = FEmitHelper::GetCppName(SourceClass);
const FString RegisterHelperName = FString::Printf(TEXT("FRegisterHelper__%s"), *CppClassName);
Context.AddLine(FString::Printf(TEXT("struct %s"), *RegisterHelperName));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(TEXT("%s()"), *RegisterHelperName));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(
TEXT("FConvertedBlueprintsDependencies::Get().RegisterConvertedClass(TEXT(\"%s\"), &%s::__StaticDependenciesAssets);")
, *OriginalClass->GetOutermost()->GetPathName()
, *CppClassName));
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
Context.AddLine(FString::Printf(TEXT("static %s Instance;"), *RegisterHelperName));
Context.DecreaseIndent();
Context.AddLine(TEXT("};"));
Context.AddLine(FString::Printf(TEXT("%s %s::Instance;"), *RegisterHelperName, *RegisterHelperName));
}
void FEmitDefaultValueHelper::GenerateCustomDynamicClassInitialization(FEmitterLocalContext& Context, TSharedPtr<FGatherConvertedClassDependencies> ParentDependencies)
{
auto BPGC = CastChecked<UBlueprintGeneratedClass>(Context.GetCurrentlyGeneratedClass());
const FString CppClassName = FEmitHelper::GetCppName(BPGC);
Context.AddLine(FString::Printf(TEXT("void %s::__CustomDynamicClassInitialization(UDynamicClass* InDynamicClass)"), *CppClassName));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields)));
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, MiscConvertedSubobjects)));
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, DynamicBindingObjects)));
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ComponentTemplates)));
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, Timelines)));
Context.AddLine(FString::Printf(TEXT("ensure(nullptr == InDynamicClass->%s);"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, AnimClassImplementation)));
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s();"), GET_FUNCTION_NAME_STRING_CHECKED(UDynamicClass, AssembleReferenceTokenStream)));
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass;
Context.ResetPropertiesForInaccessibleStructs();
if (Context.Dependencies.ConvertedEnum.Num())
{
Context.AddLine(TEXT("// List of all referenced converted enums"));
}
for (auto LocEnum : Context.Dependencies.ConvertedEnum)
{
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(LoadObject<UEnum>(nullptr, TEXT(\"%s\")));"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields), *(LocEnum->GetPathName().ReplaceCharWithEscapedChar())));
Context.EnumsInCurrentClass.Add(LocEnum);
}
if (Context.Dependencies.ConvertedClasses.Num())
{
Context.AddLine(TEXT("// List of all referenced converted classes"));
}
for (auto LocStruct : Context.Dependencies.ConvertedClasses)
{
UClass* ClassToLoad = Context.Dependencies.FindOriginalClass(LocStruct);
if (ensure(ClassToLoad))
{
if (ParentDependencies.IsValid() && ParentDependencies->ConvertedClasses.Contains(LocStruct))
{
continue;
}
FString ClassConstructor;
if (ClassToLoad->HasAnyClassFlags(CLASS_Interface))
{
const FString ClassZConstructor = FDependenciesHelper::GenerateZConstructor(ClassToLoad);
Context.AddLine(FString::Printf(TEXT("extern UClass* %s;"), *ClassZConstructor));
ClassConstructor = ClassZConstructor;
}
else
{
ClassConstructor = FString::Printf(TEXT("%s::StaticClass()"), *FEmitHelper::GetCppName(ClassToLoad));
}
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(%s);"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields), *ClassConstructor));
//Context.AddLine(FString::Printf(TEXT("InDynamicClass->ReferencedConvertedFields.Add(LoadObject<UClass>(nullptr, TEXT(\"%s\")));")
// , *(ClassToLoad->GetPathName().ReplaceCharWithEscapedChar())));
}
}
if (Context.Dependencies.ConvertedStructs.Num())
{
Context.AddLine(TEXT("// List of all referenced converted structures"));
}
for (auto LocStruct : Context.Dependencies.ConvertedStructs)
{
if (ParentDependencies.IsValid() && ParentDependencies->ConvertedStructs.Contains(LocStruct))
{
continue;
}
const FString StructConstructor = FDependenciesHelper::GenerateZConstructor(LocStruct);
Context.AddLine(FString::Printf(TEXT("extern UScriptStruct* %s;"), *StructConstructor));
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(%s);"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields), *StructConstructor));
}
TArray<UActorComponent*> ActorComponentTempatesOwnedByClass = BPGC->ComponentTemplates;
// Gather all CT from SCS and IH, the remaining ones are generated for class..
if (auto SCS = BPGC->SimpleConstructionScript)
{
// >>> This code should be removed, once UE-39168 is fixed
//TODO: it's an ugly workaround - template from DefaultSceneRootNode is unnecessarily cooked :(
UActorComponent* DefaultSceneRootComponentTemplate = SCS->GetDefaultSceneRootNode() ? SCS->GetDefaultSceneRootNode()->ComponentTemplate : nullptr;
if (DefaultSceneRootComponentTemplate)
{
ActorComponentTempatesOwnedByClass.Add(DefaultSceneRootComponentTemplate);
}
// <<< This code should be removed, once UE-39168 is fixed
for (auto Node : SCS->GetAllNodes())
{
ActorComponentTempatesOwnedByClass.RemoveSwap(Node->ComponentTemplate);
}
}
if (auto IH = BPGC->GetInheritableComponentHandler())
{
TArray<UActorComponent*> AllTemplates;
IH->GetAllTemplates(AllTemplates);
ActorComponentTempatesOwnedByClass.RemoveAllSwap([&](UActorComponent* Component) -> bool
{
return AllTemplates.Contains(Component);
});
}
Context.AddLine(TEXT("FConvertedBlueprintsDependencies::FillUsedAssetsInDynamicClass(InDynamicClass, &__StaticDependencies_DirectlyUsedAssets);"));
auto CreateAndInitializeClassSubobjects = [&](bool bCreate, bool bInitialize)
{
for (auto ComponentTemplate : ActorComponentTempatesOwnedByClass)
{
if (ComponentTemplate)
{
HandleClassSubobject(Context, ComponentTemplate, FEmitterLocalContext::EClassSubobjectList::ComponentTemplates, bCreate, bInitialize);
}
}
for (auto TimelineTemplate : BPGC->Timelines)
{
if (TimelineTemplate)
{
HandleClassSubobject(Context, TimelineTemplate, FEmitterLocalContext::EClassSubobjectList::Timelines, bCreate, bInitialize);
}
}
for (auto DynamicBindingObject : BPGC->DynamicBindingObjects)
{
if (DynamicBindingObject)
{
HandleClassSubobject(Context, DynamicBindingObject, FEmitterLocalContext::EClassSubobjectList::DynamicBindingObjects, bCreate, bInitialize);
}
}
FBackendHelperUMG::CreateClassSubobjects(Context, bCreate, bInitialize);
};
CreateAndInitializeClassSubobjects(true, false);
CreateAndInitializeClassSubobjects(false, true);
FBackendHelperAnim::CreateAnimClassData(Context);
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::Regular;
Context.ResetPropertiesForInaccessibleStructs();
FBackendHelperUMG::EmitWidgetInitializationFunctions(Context);
}
void FEmitDefaultValueHelper::GenerateConstructor(FEmitterLocalContext& Context)
{
UBlueprintGeneratedClass* BPGC = CastChecked<UBlueprintGeneratedClass>(Context.GetCurrentlyGeneratedClass());
const FString CppClassName = FEmitHelper::GetCppName(BPGC);
UClass* SuperClass = BPGC->GetSuperClass();
const bool bSuperHasObjectInitializerConstructor = SuperClass && SuperClass->HasMetaData(TEXT("ObjectInitializerConstructorDeclared"));
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::CommonConstructor;
Context.ResetPropertiesForInaccessibleStructs();
Context.AddLine(FString::Printf(TEXT("%s::%s(const FObjectInitializer& ObjectInitializer) : Super(%s)")
, *CppClassName
, *CppClassName
, bSuperHasObjectInitializerConstructor ? TEXT("ObjectInitializer") : TEXT("")));
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;
TArray<FNonativeComponentData> ComponentsToInit;
{
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)
{
// 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 (USCS_Node* Node : BPGCStack[i]->SimpleConstructionScript->GetRootNodes())
{
if (Node)
{
const FString NativeVariablePropertyName = HandleNonNativeComponent(Context, Node, HandledProperties, NativeCreatedComponentProperties, nullptr, ComponentsToInit, false);
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;
}
}
}
//TODO: UGLY HACK for "zombie" nodes - UE-40026
for (USCS_Node* Node : BPGCStack[i]->SimpleConstructionScript->GetAllNodes())
{
if (Node)
{
const bool bNodeWasProcessed = nullptr != ComponentsToInit.FindByPredicate([=](const FNonativeComponentData& InData) { return Node == InData.SCSNode; });
if (!bNodeWasProcessed)
{
HandleNonNativeComponent(Context, Node, HandledProperties, NativeCreatedComponentProperties, nullptr, ComponentsToInit, true);
}
}
}
}
}
for (auto& ComponentToInit : ComponentsToInit)
{
ComponentToInit.EmitProperties(Context);
if (Cast<UPrimitiveComponent>(ComponentToInit.ComponentTemplate))
{
Context.AddLine(FString::Printf(TEXT("if(!%s->%s())"), *ComponentToInit.NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UPrimitiveComponent, IsTemplate)));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(TEXT("%s->%s.%s(%s);")
, *ComponentToInit.NativeVariablePropertyName
, GET_MEMBER_NAME_STRING_CHECKED(UPrimitiveComponent, BodyInstance)
, GET_FUNCTION_NAME_STRING_CHECKED(FBodyInstance, FixupData)
, *ComponentToInit.NativeVariablePropertyName));
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
}
}
// 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))
{
if (!HandledProperties.Contains(Property))
{
const bool bNewProperty = Property->GetOwnerStruct() == BPGC;
OuterGenerate(Context, Property, TEXT(""), reinterpret_cast<const uint8*>(CDO), bNewProperty ? nullptr : reinterpret_cast<const uint8*>(ParentCDO), EPropertyAccessOperator::None, true);
}
}
}
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
// TODO: this mechanism could be required by other instanced subobjects.
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::Regular;
Context.ResetPropertiesForInaccessibleStructs();
Context.ResetPropertiesForInaccessibleStructs();
Context.AddLine(FString::Printf(TEXT("void %s::%s(FObjectInstancingGraph* OuterInstanceGraph)"), *CppClassName, GET_FUNCTION_NAME_STRING_CHECKED(UObject, PostLoadSubobjects)));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(TEXT("Super::%s(OuterInstanceGraph);"), GET_FUNCTION_NAME_STRING_CHECKED(UObject, PostLoadSubobjects)));
for (auto& ComponentToFix : NativeCreatedComponentProperties)
{
Context.AddLine(FString::Printf(TEXT("if(%s)"), *ComponentToFix));
Context.AddLine(TEXT("{"));
Context.IncreaseIndent();
Context.AddLine(FString::Printf(TEXT("%s->%s = EComponentCreationMethod::Native;"), *ComponentToFix, GET_MEMBER_NAME_STRING_CHECKED(UActorComponent, CreationMethod)));
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
Context.DecreaseIndent();
Context.AddLine(TEXT("}"));
}
FString FEmitDefaultValueHelper::HandleClassSubobject(FEmitterLocalContext& Context, UObject* Object, FEmitterLocalContext::EClassSubobjectList ListOfSubobjectsType, bool bCreate, bool bInitialize)
{
ensure(Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass);
FString LocalNativeName;
if (bCreate)
{
FString OuterStr = Context.FindGloballyMappedObject(Object->GetOuter());
if (OuterStr.IsEmpty())
{
OuterStr = HandleClassSubobject(Context, Object->GetOuter(), ListOfSubobjectsType, bCreate, bInitialize);
if (OuterStr.IsEmpty())
{
return FString();
}
const FString AlreadyCreatedObject = Context.FindGloballyMappedObject(Object);
if (!AlreadyCreatedObject.IsEmpty())
{
return AlreadyCreatedObject;
}
}
const bool bAddAsSubobjectOfClass = Object->GetOuter() == Context.GetCurrentlyGeneratedClass();
LocalNativeName = Context.GenerateUniqueLocalName();
Context.AddClassSubObject_InConstructor(Object, LocalNativeName);
UClass* ObjectClass = Object->GetClass();
const FString ActualClass = Context.FindGloballyMappedObject(ObjectClass, UClass::StaticClass());
const FString NativeType = FEmitHelper::GetCppName(Context.GetFirstNativeOrConvertedClass(ObjectClass));
if(!ObjectClass->IsNative())
{
// make sure CDO has been created for NativeType:
Context.AddLine(FString::Printf(TEXT("%s::StaticClass()->GetDefaultObject();"), *NativeType));
}
Context.AddLine(FString::Printf(
TEXT("auto %s = NewObject<%s>(%s, %s, TEXT(\"%s\"));")
, *LocalNativeName
, *NativeType
, bAddAsSubobjectOfClass ? TEXT("InDynamicClass") : *OuterStr
, *ActualClass
, *Object->GetName().ReplaceCharWithEscapedChar()));
if (bAddAsSubobjectOfClass)
{
Context.RegisterClassSubobject(Object, ListOfSubobjectsType);
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(%s);")
, Context.ClassSubobjectListName(ListOfSubobjectsType)
, *LocalNativeName));
}
}
if (bInitialize)
{
if (LocalNativeName.IsEmpty())
{
LocalNativeName = Context.FindGloballyMappedObject(Object);
}
ensure(!LocalNativeName.IsEmpty());
auto CDO = Object->GetClass()->GetDefaultObject(false);
for (auto Property : TFieldRange<const UProperty>(Object->GetClass()))
{
OuterGenerate(Context, Property, LocalNativeName
, reinterpret_cast<const uint8*>(Object)
, reinterpret_cast<const uint8*>(CDO)
, EPropertyAccessOperator::Pointer);
}
}
return LocalNativeName;
}
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>(%s(TEXT(\"%s\")));")
, *LocalNativeName
, *FEmitHelper::GetCppName(ObjectClass)
, GET_FUNCTION_NAME_STRING_CHECKED(UObject, GetDefaultSubobjectByName)
, *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->GetOuter());
if (OuterStr.IsEmpty())
{
ensure(false);
return FString();
}
const FString ActualClass = Context.FindGloballyMappedObject(ObjectClass, UClass::StaticClass());
const FString NativeType = FEmitHelper::GetCppName(Context.GetFirstNativeOrConvertedClass(ObjectClass));
if(!ObjectClass->IsNative())
{
// make sure CDO has been created for NativeType:
Context.AddLine(FString::Printf(TEXT("%s::StaticClass()->GetDefaultObject();"), *NativeType));
}
Context.AddLine(FString::Printf(
TEXT("auto %s = NewObject<%s>(%s, %s, TEXT(\"%s\"));")
, *LocalNativeName
, *NativeType
, *OuterStr
, *ActualClass
, *Object->GetName().ReplaceCharWithEscapedChar()));
}
return LocalNativeName;
}