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

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

Change 3256692 on 2017/01/13 by Ben.Cosh

	This updates the blueprint variable config option tooltip to be more accurate when describing the config to override the value in.
	#Jira UE-40334 - Blueprint variables report the wrong config file in the UI when using the set from config option
	#Proj Kismet

Change 3258553 on 2017/01/16 by Phillip.Kavan

	[UE-40131] Revised fix that will work for "inclusive" BP nativization with data-only BPs.

	- Mirrored from //UE4/Release-4.15 (CL# 3258541).

	#jira UE-40131

Change 3258958 on 2017/01/16 by Mike.Beach

	Adopted fix from UDN - resolves problems with Blueprints which inherit from UDataAsset. Compiling/Reinstancing them was leaving the data asset packages empty.

Change 3259363 on 2017/01/16 by Mike.Beach

	Revising fix in UBlueprint::BeginCacheForCookedPlatformData(), saving off nativization data if the -nativizeAssets param is present (not just if it was enabled in packaging settings).

	#jira UE-40620

Change 3260722 on 2017/01/17 by Maciej.Mroz

	Exclude client-only BPGC from Server.

	#jira UE-39728

Change 3261420 on 2017/01/17 by Dan.Oconnor

	Final prototype of the GMinimalCompileOnLoad pass

Change 3262208 on 2017/01/18 by Dan.Oconnor

	SA fix

Change 3263168 on 2017/01/18 by Dan.Oconnor

	Tweak to minimal compile on load pass, doing node refreshing early because node refresh can trigger CDO generation (which cannot happen while reinstancing, unless the new class is ready)

Change 3263844 on 2017/01/19 by Maciej.Mroz

	Error when CDO is created for an incomplete dynamic class

Change 3264647 on 2017/01/19 by Mike.Beach

	PR #3146: Only ensure if ptr != nullptr (Contributed by projectgheist )

	#jira UE-40846

Change 3264818 on 2017/01/19 by Dan.Oconnor

	Undo overzealous use of GMinimalCompileOnLoad

Change 3265075 on 2017/01/19 by Dan.Oconnor

	Make sure we use the authoritative class for the component template, fixes reinst issue in GMinimalCompileOnLoad pass

Change 3265085 on 2017/01/19 by Mike.Beach

	Mirroring CL 3260397

	Making it so CreateEvent nodes are searchable using the function name they are bound to (as requested by Jeff Farris). To find this data across your content library, in unopened Blueprints, you'll have to resave those Blueprints (so the new data is available in the asset registry).

Change 3272401 on 2017/01/25 by Mike.Beach

	Fixed a bug where modifying certain timeline settings would not dirty the Blueprint.

	PR #3137: UE-40674: Mark TimeLine BP as modified (Contributed by projectgheist)

	#jira UE-40680, UE-40674

Change 3273050 on 2017/01/26 by Maciej.Mroz

	#jira ODIN-4913, UE-40974
	merged cl3268193 from Odin branch

	Nativization:
	- added ConstructTInlineValue function
	- TInlineValue structures will be initialized using UScriptStruct::InitializeStruct, instead of callind default constructor ( default constructor is unrealible)

Change 3273052 on 2017/01/26 by Maciej.Mroz

	#jira UE-40917
	merged cl#3270456 from Odin branch

	Nativization:
	Actor template from ChildActorComponent is a subobject of nativized class.

Change 3275646 on 2017/01/27 by Dan.Oconnor

	Fix obscure issue when reinstancing interface types

Change 3275811 on 2017/01/27 by Dan.Oconnor

	ImplementsGetWorld now properly inherited for blueprint types that derive from a blueprint type that derives from a native type that "ImplementsGetWorld"

Change 3275922 on 2017/01/27 by Dan.Oconnor

	Preserve trashed properties' names, don't force regen nodes when using the new compile manager (it has already regen'd nodes)

Change 3276037 on 2017/01/27 by Dan.Oconnor

	Uses Authoritative class for this type check, added const version of GetAuthoritativeClass

Change 3276109 on 2017/01/27 by Phillip.Kavan

	[UE-40894] Fix data loss issues with non-native Blueprint classes that override inherited component default values from a nativized parent Blueprint class hierarchy.

	change summary:
	- modified FBlueprintEditorUtils::BuildComponentInstancingData() to accept an additional parameter indicating whether or not to use the CDO or the template's Archetype as the basis for building the delta property list.
	- revised UBlueprint::BeginCacheForCookedPlatformData() to first generate "override" data for ICH records that override SCS nodes from parent classes that are targeted for nativization. this also makes the optimized component instancing feature compatible with nativization as well (should that ever become useful).
	- revised UBlueprintGeneratedClass::CheckAndApplyComponentTemplateOverrides() to utilize the additional delta property list data that's now generated at cook time along with a delta serialization pass against the ICH template that's serializes changed properties to a cached data block at load time. this cached "override" data is then applied to instanced subobjects inherited from nativized parent Blueprint classes using a minimal binary serialization pass.

	#jira UE-40894

Change 3277671 on 2017/01/30 by Mike.Beach

	Mirroring CL 3276602.

	Refactoring FBlueprintCompilerCppBackend::SortNodesInUberGraphExecutionGroup() a bit. Catching cases that weren't acounted for - detecting cyclical logic now when we've pulled a node/statement out of order, and other nodes need to fall through to that logic (not relying on a goto).

	#jira UE-41188, UE-41189, UE-41186, UE-41037

Change 3278454 on 2017/01/30 by Mike.Beach

	Mirroring CL 3278054

	Upgrading USimpleConstructionScript::FixupSceneNodeHierarchy() so that the SCS detects orphaned components, and adds them to the hierarchy (notifying users of the issue with a warning).

	#jira UE-41247

Change 3278814 on 2017/01/31 by Maciej.Mroz

	fixed CIS warning
	fixed FBlueprintNativeCodeGenModule::FindReplacedNameAndOuter

Change 3279398 on 2017/01/31 by Mike.Beach

	Back out changelist 3278454 - causing unforseen issues, restructuring the component hierarchy.

Change 3282200 on 2017/02/01 by Dan.Oconnor

	Moved ForceLoad helpers into UBlueprint so that new compile on load path can prod the linker into loading stuff before flushing the compilation queue

Change 3282269 on 2017/02/01 by Mike.Beach

	ClassGeneratedBy not dependable in cooked builds, must rely on class flags to determine if this is a Blueprint class.

Change 3284455 on 2017/02/02 by Dan.Oconnor

	Reinstancer optimizations and skipping loader reset when using the new compile manager

Change 3284463 on 2017/02/02 by Dan.Oconnor

	Handling missing GeneratedClass

Change 3284476 on 2017/02/02 by Dan.Oconnor

	Flush compilation manager when we would normally do a bytecode pass, this guarantees clients of LoadObject that we won't reinstance a pointer on load (giving them back a stale object)

Change 3284523 on 2017/02/02 by Dan.Oconnor

	Optimize FBlueprintUnloader::ReplaceStaleRefs, add option to skip reinstancing when recompiling bytecode, refactored bytecode recompilation options into a flag parameter

Change 3285731 on 2017/02/03 by Dan.Oconnor

	Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints)

	Auto resolved, with merging:
	K2Node_CreateDelegate.h/cpp, KismetCompiler.cpp, WidgetBlueprintCompiler.cpp, Kismet2.cpp, KismetReinstanceUtilities.cpp/h, KismetEditorUtils.h, BlueprintSupport.cpp, Class.cpp, LinkerLoad.cpp, Obj.cpp, Class.h, Object.h, BlueprintGeneratedClass.cpp, DefaultEngine.ini,

	Manually resolved:
	BlueprintEditorUtils.cpp, TestRunner.Automation.Tests.cs, OrionAutobuyCardStatEntry.cpp/h, OrionStateWidget_DraftLobby.cpp

	All else resolved safely (no merging)

Change 3286019 on 2017/02/03 by Dan.Oconnor

	Add assertion that was added in main

Change 3286031 on 2017/02/03 by Dan.Oconnor

	Build fix, missing include

Change 3286056 on 2017/02/03 by Mike.Beach

	Corrected fix (from CL 3278454, which had to be backed out) - USimpleConstructionScript::FixupSceneNodeHierarchy() so that the SCS detects orphaned components, and adds them to the hierarchy (notifying users of the issue with a warning).

	#jira UE-41247

Change 3286302 on 2017/02/03 by Dan.Oconnor

	Allow reinstancing to run very early or in other situation where GEditor is not yet set up

Change 3286386 on 2017/02/03 by Dan.Oconnor

	GMinimalCompileOnLoad checks no longer needed with my local changes to BlueprintCompilationManager

Change 3286391 on 2017/02/03 by Dan.Oconnor

	Missed old comment

Change 3286614 on 2017/02/03 by Dan.Oconnor

	GMinimalCompileOnLoad logic no longer needed

Change 3286655 on 2017/02/03 by Dan.Oconnor

	Refactor FKismetEditorUtilities::CompileBlueprint flags into EBlueprintCompileOptions

Change 3286662 on 2017/02/03 by Dan.Oconnor

	Build fix, missed file

Change 3288770 on 2017/02/06 by Mike.Beach

	Attempt to fix up CIS warnings from CL 3286056.

Change 3289236 on 2017/02/06 by Mike.Beach

	Missed fix for CIS warning.

Change 3289276 on 2017/02/06 by Dan.Oconnor

	Duplication of CDO is unnecessary
	#fyi Maciej.Mroz

Change 3289334 on 2017/02/06 by Dan.Oconnor

	Add option to skip all reinstancing logic when running CompileBlueprint - used locally by the blueprint compilation manager. Fixed Typo.

Change 3289443 on 2017/02/06 by Dan.Oconnor

	Further cleanup of GMinimalCompileOnLoad abuse. added flag to skip reinstancing and "stub on failure" compile pass

Change 3290509 on 2017/02/07 by Dan.Oconnor

	Addressed this with a change to the blueprint compiler manager - no modification needed

Change 3290534 on 2017/02/07 by Dan.Oconnor

	Remove old forward declare and outdated comment

Change 3291446 on 2017/02/07 by Dan.Oconnor

	This CPFUO call is unused with my latest iteration on the compiler manager

Change 3291516 on 2017/02/07 by Dan.Oconnor

	Running compile manager earlier means we don't need to worry about this weird (and costly) call

Change 3291534 on 2017/02/07 by Dan.Oconnor

	This compile children call is no longer running with latest improvents to the compile manager

Change 3292587 on 2017/02/08 by Maciej.Mroz

	#jira UE-41694
	Mirroring cl#3292273 from Odin branch

Change 3293344 on 2017/02/08 by Dan.Oconnor

	Iteration on blueprint compile manager - fortnite loads without issue. For now I do the three compile passes that are done by the existing load paths. Timing is actually quite reasonable. Centralizing reinstancing has sped things up.

	#fyi Maciej.Mroz

Change 3293565 on 2017/02/08 by Dan.Oconnor

	Back out changelist 3293344 -  wrong CL

Change 3293567 on 2017/02/08 by Dan.Oconnor

	Iteration on blueprint compile manager - fortnite loads without issue. For now I do the three compile passes that are done by the existing load paths. Timing is actually quite reasonable. Centralizing reinstancing has sped things up.

	#fyi Maciej.Mroz

Change 3294334 on 2017/02/09 by Phillip.Kavan

	[UE-41246] Fix misaligned memory access of noexport struct properties leading to incorrectly initialized values.

	- Mirrored from //Odin/Main/... (CL# 3284308).

	#jira UE-41246

Change 3294343 on 2017/02/09 by Phillip.Kavan

	[UE-41246] Add a whitelist mechanism to handle native noexport types that can support direct field access in nativized Blueprint code.

	- Mirrored from //Odin/Main/... (CL# 3285789).

	#jira UE-41246

Change 3295913 on 2017/02/09 by Dan.Oconnor

	Back out reinstancer optimization, failing to find a reference and it's causing a crash when a component is renamed
	#jira UE-41843

Change 3297279 on 2017/02/10 by Dan.Oconnor

	SA fix

Change 3297587 on 2017/02/10 by Mike.Beach

	Temporarily disabling a spurious warning that gets triggered in Fortnite, when correctly excluding client-only Blueprints.

	#jira UE-41880

Change 3298078 on 2017/02/10 by Dan.Oconnor

	Try to suppress confused SA warning

[CL 3298251 by Mike Beach in Main branch]
2017-02-10 19:50:34 -05:00

1951 lines
78 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 bInaccessibleScriptStructProperty = PropertyOwnerAsScriptStruct
&& !FStructAccessHelper::CanEmitDirectFieldAccess(PropertyOwnerAsScriptStruct)
// && !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 (bInaccessibleScriptStructProperty || 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 = 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 InlineValueStruct = [&](UScriptStruct* OuterStruct, const uint8* LocalValuePtr) -> UScriptStruct*
{
UScriptStruct* InnerStruct = FBackendHelperUMG::InlineValueStruct(OuterStruct, LocalValuePtr);
if (InnerStruct)
{
Context.StructsUsedAsInlineValues.Add(InnerStruct);
}
return InnerStruct;
};
auto InlineValueData = [](UScriptStruct* OuterStruct, const uint8* LocalValuePtr) -> const uint8*{ return FBackendHelperUMG::InlineValueData(OuterStruct, LocalValuePtr); };
auto IsTInlineStruct = [](UScriptStruct* OuterStruct) -> bool { return FBackendHelperUMG::IsTInlineStruct(OuterStruct); };
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())
{
const UStructProperty* StructProperty = Cast<const UStructProperty>(LocalProperty);
UScriptStruct* InnerInlineStruct = InlineValueStruct(StructProperty ? StructProperty->Struct : nullptr, LocalValuePtr);
if (StructProperty && StructProperty->Struct && InnerInlineStruct)
{
check(InnerInlineStruct);
FString StructConstructor;
bComplete = SpecialStructureConstructor(InnerInlineStruct, InlineValueData(StructProperty->Struct, LocalValuePtr), &StructConstructor);
ValueStr = bComplete
? FString::Printf(TEXT("%s(%s)"), *FEmitHelper::GetCppName(StructProperty->Struct), *StructConstructor)
: FString::Printf(TEXT("ConstructTInlineValue<%s>(%s::StaticStruct())"), *FEmitHelper::GetCppName(StructProperty->Struct), *FEmitHelper::GetCppName(InnerInlineStruct));
}
else
{
ValueStr = LocalContext.ExportTextItem(LocalProperty, LocalValuePtr);
}
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;
};
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 && !Property->IsA<UArrayProperty>() && !Property->IsA<USetProperty>() && !Property->IsA<UMapProperty>())
{
return;
}
}
if (const UStructProperty* StructProperty = Cast<const UStructProperty>(Property))
{
check(StructProperty->Struct);
UScriptStruct* InnerInlineStruct = InlineValueStruct(StructProperty->Struct, ValuePtr);
UScriptStruct* ActualStruct = InnerInlineStruct ? InnerInlineStruct : StructProperty->Struct;
const uint8* ActualValuePtr = InnerInlineStruct ? InlineValueData(StructProperty->Struct, ValuePtr) : ValuePtr;
const uint8* ActualDefaultValuePtr = InnerInlineStruct ? InlineValueData(StructProperty->Struct, DefaultValuePtr) : DefaultValuePtr;
// Create default struct instance, only when DefaultValuePtr is null.
FStructOnScope DefaultStructOnScope(ActualDefaultValuePtr ? nullptr : ActualStruct);
const FString ActualPathToMember = InnerInlineStruct ? FString::Printf(TEXT("((%s*)%s.GetPtr())"), *FEmitHelper::GetCppName(InnerInlineStruct), *PathToMember) : PathToMember;
for (auto LocalProperty : TFieldRange<const UProperty>(ActualStruct))
{
OuterGenerate(Context, LocalProperty, ActualPathToMember, ActualValuePtr
, (ActualDefaultValuePtr ? ActualDefaultValuePtr : DefaultStructOnScope.GetStructMemory())
, InnerInlineStruct ? EPropertyAccessOperator::Pointer : EPropertyAccessOperator::Dot);
}
}
enum class EStructConstructionType
{
InitializeStruct,
EmptyConstructor,
Custom,
};
auto StructConstruction = [IsTInlineStruct](const UStructProperty* InnerStructProperty) -> EStructConstructionType
{
//TODO: if the struct has a custom ExportTextItem, that support PPF_ExportCpp, then ELocalConstructionType::Custom should be returned
//For regular native structs default constructor is not relaible, so we need to use InitializeStruct
const bool bUDS = InnerStructProperty && (nullptr != Cast<UUserDefinedStruct>(InnerStructProperty->Struct));
const bool bSpecialNativeStruct = InnerStructProperty && InnerStructProperty->Struct
&& InnerStructProperty->Struct->IsNative()
&& ((0 != (InnerStructProperty->Struct->StructFlags & STRUCT_NoExport)) || IsTInlineStruct(InnerStructProperty->Struct));
const bool bInitializeWithoutScriptStruct = bUDS || bSpecialNativeStruct;
if (!bInitializeWithoutScriptStruct)
{
if (InnerStructProperty && !FEmitDefaultValueHelper::SpecialStructureConstructor(InnerStructProperty->Struct, nullptr, nullptr))
{
return EStructConstructionType::InitializeStruct;
}
}
return bInitializeWithoutScriptStruct ? EStructConstructionType::EmptyConstructor : EStructConstructionType::Custom;
};
auto CreateElementSimple = [OneLineConstruction](FEmitterLocalContext& LocalContext, const UProperty* LocalProperty, const uint8* LocalValuePtr) -> FString
{
FString ValueStr;
const bool bComplete = OneLineConstruction(LocalContext, LocalProperty, LocalValuePtr, ValueStr, true);
ensure(!ValueStr.IsEmpty());
if (!bComplete)
{
const FString ElemLocName = LocalContext.GenerateUniqueLocalName();
LocalContext.AddLine(FString::Printf(TEXT("auto %s = %s;"), *ElemLocName, *ValueStr));
InnerGenerate(LocalContext, LocalProperty, ElemLocName, LocalValuePtr, nullptr, /*bWithoutFirstConstructionLine=*/true);
ValueStr = ElemLocName;
}
return ValueStr;
};
if (const UArrayProperty* ArrayProperty = Cast<const UArrayProperty>(Property))
{
check(ArrayProperty->Inner);
FScriptArrayHelper ScriptArrayHelper(ArrayProperty, ValuePtr);
if (ScriptArrayHelper.Num())
{
const UStructProperty* StructProperty = Cast<const UStructProperty>(ArrayProperty->Inner);
const EStructConstructionType Construction = StructConstruction(StructProperty);
if(EStructConstructionType::InitializeStruct == Construction)
{
const UScriptStruct* InnerStruct = StructProperty ? StructProperty->Struct : nullptr;
ensure(InnerStruct);
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(InnerStruct, UScriptStruct::StaticClass())
, GET_FUNCTION_NAME_STRING_CHECKED(UStruct, InitializeStruct)
, *PathToMember
, ScriptArrayHelper.Num()));
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));
// This is a Regular Struct (no special constructor), so we don't need to call constructor
InnerGenerate(Context, ArrayProperty->Inner, ArrayElementRefName, ScriptArrayHelper.GetRawPtr(Index), nullptr, /*bWithoutFirstConstructionLine=*/ 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, true);
Context.AddLine(FString::Printf(TEXT("%s.Add(%s);"), *PathToMember, *ValueStr));
if (!bComplete)
{
// The constructor was already called
InnerGenerate(Context, ArrayProperty->Inner, FString::Printf(TEXT("%s[%d]"), *PathToMember, Index), LocalValuePtr, nullptr, /*bWithoutFirstConstructionLine=*/ true);
}
}
}
}
}
else if(const USetProperty* SetProperty = Cast<const USetProperty>(Property))
{
check(SetProperty->ElementProp);
FScriptSetHelper ScriptSetHelper(SetProperty, ValuePtr);
if (ScriptSetHelper.Num())
{
auto ForEachElementInSet = [&](TFunctionRef<void(int32)> Process)
{
int32 Size = ScriptSetHelper.Num();
for (int32 I = 0; Size; ++I)
{
if (ScriptSetHelper.IsValidIndex(I))
{
--Size;
Process(I);
}
}
};
Context.AddLine(FString::Printf(TEXT("%s.Reserve(%d);"), *PathToMember, ScriptSetHelper.Num()));
const UStructProperty* StructProperty = Cast<const UStructProperty>(SetProperty->ElementProp);
const EStructConstructionType Construction = StructConstruction(StructProperty);
if (EStructConstructionType::InitializeStruct == Construction)
{
const UScriptStruct* InnerStruct = StructProperty ? StructProperty->Struct : nullptr;
ensure(InnerStruct);
const FString SetHelperName = Context.GenerateUniqueLocalName();
const FString PropertyLocalName = FEmitHelper::GenerateGetPropertyByName(Context, SetProperty);
const FString StructCppName = FEmitHelper::GetCppName(InnerStruct);
Context.AddLine(FString::Printf(TEXT("FScriptSetHelper %s(CastChecked<USetProperty>(%s), &%s);"), *SetHelperName, *PropertyLocalName, *PathToMember));
ForEachElementInSet([&](int32 Index)
{
const FString ElementName = Context.GenerateUniqueLocalName();
Context.AddLine(FString::Printf(TEXT("%s& %s = *(%s*)%s.GetElementPtr(%s.AddDefaultValue_Invalid_NeedsRehash());")
, *StructCppName, *ElementName, *StructCppName, *SetHelperName, *SetHelperName));
InnerGenerate(Context, StructProperty, ElementName, ScriptSetHelper.GetElementPtr(Index), nullptr, /*bWithoutFirstConstructionLine=*/true);
});
Context.AddLine(FString::Printf(TEXT("%s.Rehash();"), *SetHelperName));
}
else
{
ForEachElementInSet([&](int32 Index)
{
const FString Element = CreateElementSimple(Context, SetProperty->ElementProp, ScriptSetHelper.GetElementPtr(Index));
Context.AddLine(FString::Printf(TEXT("%s.Add(%s);"), *PathToMember, *Element));
});
}
}
}
else if(const UMapProperty* MapProperty = Cast<const UMapProperty>(Property))
{
check(MapProperty->KeyProp && MapProperty->ValueProp);
FScriptMapHelper ScriptMapHelper(MapProperty, ValuePtr);
if (ScriptMapHelper.Num())
{
auto ForEachPairInMap = [&](TFunctionRef<void(int32)> Process)
{
int32 Size = ScriptMapHelper.Num();
for (int32 I = 0; Size; ++I)
{
if (ScriptMapHelper.IsValidIndex(I))
{
--Size;
Process(I);
}
}
};
Context.AddLine(FString::Printf(TEXT("%s.Reserve(%d);"), *PathToMember, ScriptMapHelper.Num()));
const UStructProperty* KeyStructProperty = Cast<const UStructProperty>(MapProperty->KeyProp);
const EStructConstructionType KeyConstruction = StructConstruction(KeyStructProperty);
const UStructProperty* ValueStructProperty = Cast<const UStructProperty>(MapProperty->ValueProp);
const EStructConstructionType ValueConstruction = StructConstruction(ValueStructProperty);
if ((EStructConstructionType::InitializeStruct == KeyConstruction) || (EStructConstructionType::InitializeStruct == ValueConstruction))
{
const FString MapHelperName = Context.GenerateUniqueLocalName();
const FString PropertyLocalName = FEmitHelper::GenerateGetPropertyByName(Context, MapProperty);
Context.AddLine(FString::Printf(TEXT("FScriptMapHelper %s(CastChecked<UMapProperty>(%s), &%s);"), *MapHelperName, *PropertyLocalName, *PathToMember));
const uint32 ElementTypeCppExportFlags = EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend | EPropertyExportCPPFlags::CPPF_NoConst;
const FString ElementTypeStr = Context.ExportCppDeclaration(MapProperty, EExportedDeclaration::Member, ElementTypeCppExportFlags, FEmitterLocalContext::EPropertyNameInDeclaration::Skip).TrimTrailing()
+ TEXT("::ElementType");
ForEachPairInMap([&](int32 Index)
{
const FString PairName = Context.GenerateUniqueLocalName();
Context.AddLine(FString::Printf(TEXT("%s& %s = *(%s*)%s.GetPairPtr(%s.AddDefaultValue_Invalid_NeedsRehash());")
, *ElementTypeStr, *PairName, *ElementTypeStr, *MapHelperName, *MapHelperName));
{
bool bKeyComplete = false;
const FString KeyPath = FString::Printf(TEXT("%s.Key"), *PairName);
if (EStructConstructionType::Custom == KeyConstruction)
{
FString KeyStr;
bKeyComplete = OneLineConstruction(Context, MapProperty->KeyProp, ScriptMapHelper.GetKeyPtr(Index), KeyStr, /*bGenerateEmptyStructConstructor=*/ false);
if (!KeyStr.IsEmpty())
{
Context.AddLine(FString::Printf(TEXT("%s = %s;"), *KeyPath, *KeyStr));
}
}
if (!bKeyComplete)
{
InnerGenerate(Context, MapProperty->KeyProp, KeyPath, ScriptMapHelper.GetKeyPtr(Index), nullptr, /*bWithoutFirstConstructionLine=*/true);
}
}
{
bool bValueComplete = false;
const FString ValuePath = FString::Printf(TEXT("%s.Value"), *PairName);
if (EStructConstructionType::Custom == ValueConstruction)
{
FString ValueStr;
bValueComplete = OneLineConstruction(Context, MapProperty->ValueProp, ScriptMapHelper.GetKeyPtr(Index), ValueStr, /*bGenerateEmptyStructConstructor=*/ false);
if (!ValueStr.IsEmpty())
{
Context.AddLine(FString::Printf(TEXT("%s = %s;"), *ValuePath, *ValueStr));
}
}
if (!bValueComplete)
{
InnerGenerate(Context, MapProperty->ValueProp, ValuePath, ScriptMapHelper.GetValuePtr(Index), nullptr, /*bWithoutFirstConstructionLine=*/true);
}
}
});
Context.AddLine(FString::Printf(TEXT("%s.Rehash();"), *MapHelperName));
}
else
{
ForEachPairInMap([&](int32 Index)
{
const FString KeyStr = CreateElementSimple(Context, MapProperty->KeyProp, ScriptMapHelper.GetKeyPtr(Index));
const FString ValueStr = CreateElementSimple(Context, MapProperty->ValueProp, ScriptMapHelper.GetValuePtr(Index));
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 (FBackendHelperUMG::SpecialStructureConstructorUMG(Struct, ValuePtr, OutResult))
{
return true;
}
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;
}
}
auto BPGC = Context.GetCurrentlyGeneratedClass();
const bool bCreatingSubObjectsOfClass = (Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass);
{
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;
}
}
if (!bCreatingSubObjectsOfClass && Object->IsIn(BPGC))
{
UChildActorComponent* OuterCAC = Cast<UChildActorComponent>(Object->GetOuter());
if (OuterCAC && OuterCAC->IsIn(BPGC) && OuterCAC->GetChildActorTemplate() == Object)
{
Context.TemplateFromSubobjectsOfClass.AddUnique(Object);
const FString MappedObject = Context.FindGloballyMappedObject(Object, ObjectClassToUse);
if (!MappedObject.IsEmpty())
{
return MappedObject;
}
}
}
}
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;
const bool bEnableBootTimeEDLOptimization = IsEventDrivenLoaderEnabledInCookedBuilds() && bBootTimeEDL;
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 (bEnableBootTimeEDLOptimization)
{
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 (!bEnableBootTimeEDLOptimization)
{
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() || bEnableBootTimeEDLOptimization)
{
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 (bEnableBootTimeEDLOptimization)
{
//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));
}
ensure(0 == Context.MiscConvertedSubobjects.Num());
for (UObject* LocalTemplate : Context.TemplateFromSubobjectsOfClass)
{
HandleClassSubobject(Context, LocalTemplate, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, true, true, true);
}
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, bool bForceSubobjectOfClass)
{
ensure(Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass);
FString LocalNativeName;
if (bCreate)
{
const bool bAddAsSubobjectOfClass = bForceSubobjectOfClass || (Object->GetOuter() == Context.GetCurrentlyGeneratedClass());
FString OuterStr;
if (bAddAsSubobjectOfClass)
{
OuterStr = TEXT("InDynamicClass");
}
else
{
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;
}
}
}
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
, *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;
}