You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2972003 on 2016/05/10 by Maciej.Mroz
Fixed build process for nativized Orion. When UBT commandline includes "-2015" switch, the Orion will be compiled with VS2015.
Change 2972004 on 2016/05/10 by Maciej.Mroz
Removed unnecessary comment
Change 2972177 on 2016/05/10 by Maciej.Mroz
Changed check() in UEdGraphSchema_K2::CreateSubstituteNode into ensure(). It fails for BP_GMM_Trainer asser (from Orion).
Change 2972313 on 2016/05/10 by Ben.Cosh
Adding support for the blueprint profiler execution path wire heat display which includes SlateCore support for color gradient splines.
#UEBP-103 - Execution path wire heat display
#Proj BlueprintProfiler, GraphEditor, Kismet, UnrealEd, SlateCore
#CodeReview Phillip.Kavan
Change 2974089 on 2016/05/11 by Maciej.Mroz
#jira UE-30557
NaN value is replaced by 0.0f while nativization.
Change 2974447 on 2016/05/11 by Maciej.Mroz
Fixed (in nativized code) strange C4883 error in VS2015 update 2.
Change 2974601 on 2016/05/11 by Mike.Beach
Fixing FText formatting warning, coming from the GameMode menu.
#jira UE-29901
Change 2974882 on 2016/05/11 by Dan.Oconnor
Fix for changes that only affected case being dropped by the blueprint editor (only effectied pins in graph view)
#jira UE-29750
Change 2977298 on 2016/05/13 by Ryan.Rauschkolb
Fixed Spelling error in Tooltip for "Save On Compile" in BP Editor
#jira UE-20392
Change 2977299 on 2016/05/13 by Ryan.Rauschkolb
Fixed Wrong Tooltip for "Keywords" and "Compact Node" in Macro Library Derived from an Actor
#jira UE-29894
Change 2977486 on 2016/05/13 by Ryan.Rauschkolb
Fixed "Asterisk" is misspelled as "Asterix" in a Blueprint Node
#jira UE-21579
Change 2977497 on 2016/05/13 by Mike.Beach
Clearing property nodes and cached read-addresses when changing the details view object (so any queued actions will not operate on invalid properties).
#jira UE-26392
Change 2977898 on 2016/05/14 by Maciej.Mroz
BP Nativization:
Name of native enum (converted from UDE) is mangled.
Change 2977915 on 2016/05/14 by Maciej.Mroz
Fixed UKismetNodeHelperLibrary::GetValidValue
Change 2978934 on 2016/05/16 by Maciej.Mroz
Blueprint nativiation fix:
Original owner od delegate is no longer added as "header dependency". THe delegates signatures are recreated in local scope anyway.
This change solves some circularly dependent headers errors (UHT error)
Change 2978985 on 2016/05/16 by Bob.Tellez
Duplicating CL#2969542 from //Fortnite/Main
#UE4 Fixed a crash that involved renaming SCS nodes during compile on load.
#JIRA FORT-23754
Change 2979069 on 2016/05/16 by Maciej.Mroz
#jira UE-28536 Attached Project Crashes on Attempting to Play in Standalone
A too strict check (in UEdGraphSchema_K2::ArePinTypesCompatible) is replaced by an error log.
Change 2980131 on 2016/05/17 by Ben.Cosh
This changes the macro displays in the execution graph to improve the logical layout of stats.
#Jira UEBP-192 - Update the macro appearance in the execution graph
#Proj Kismet, BlueprintProfiler
Change 2980483 on 2016/05/17 by Ryan.Rauschkolb
Fixed Copy/paste of Make Array loses type and values
#jira UE-16240
Change 2980764 on 2016/05/17 by Ryan.Rauschkolb
Added "Get All Actors With Tag" node
#jira UE-28769
Change 2982120 on 2016/05/18 by Mike.Beach
Preventing a crash that can happen when a UWorld TObjectIterator hits worlds that aren't in the engine's WorldList.
Change 2983265 on 2016/05/19 by Ben.Cosh
Fix for Blueprint profiler Min/Max stats considering both exclusive and inclusive timings
#Jira UE-31004 - The blueprint profiler displays different values for min and max stats when only a single sample is present.
#Proj Kismet
#lockdown nick.penwarden
[CL 2985633 by Dan Oconnor in Main branch]
1142 lines
45 KiB
C++
1142 lines
45 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "BlueprintCompilerCppBackendModulePrivatePCH.h"
|
|
#include "BlueprintCompilerCppBackendBase.h"
|
|
#include "BlueprintCompilerCppBackendUtils.h"
|
|
#include "BlueprintEditorUtils.h"
|
|
#include "Animation/AnimNodeBase.h"
|
|
|
|
TArray<class UFunction*> IBlueprintCompilerCppBackendModule::CollectBoundFunctions(class UBlueprint* BP)
|
|
{
|
|
// it would be clean to recover info about delegates from bytecode, but then the owner class is unknown, that's why we check the nodes.
|
|
TArray<class UFunction*> Result;
|
|
check(BP);
|
|
TArray<UEdGraph*> Graphs;
|
|
BP->GetAllGraphs(Graphs);
|
|
for (UEdGraph* Graph : Graphs)
|
|
{
|
|
if (!Graph)
|
|
{
|
|
continue;
|
|
}
|
|
for (UEdGraphNode* Node : Graph->Nodes)
|
|
{
|
|
FName FunctionName = NAME_None;
|
|
UClass* FunctionOwnerClass = nullptr;
|
|
if (UK2Node_CreateDelegate* CreateDelegate = Cast<UK2Node_CreateDelegate>(Node))
|
|
{
|
|
FunctionName = CreateDelegate->GetFunctionName();
|
|
FunctionOwnerClass = CreateDelegate->GetScopeClass(true);
|
|
}
|
|
else if (UK2Node_Event* EventNode = Cast<UK2Node_Event>(Node))
|
|
{
|
|
UEdGraphPin* DelegateOutPin = EventNode->FindPin(UK2Node_Event::DelegateOutputName);
|
|
if (DelegateOutPin && DelegateOutPin->LinkedTo.Num())
|
|
{
|
|
FunctionOwnerClass = BP->GeneratedClass;
|
|
FunctionName = EventNode->GetFunctionName();
|
|
}
|
|
}
|
|
|
|
FunctionOwnerClass = FunctionOwnerClass ? FunctionOwnerClass->GetAuthoritativeClass() : nullptr;
|
|
UFunction* Func = FunctionOwnerClass ? FunctionOwnerClass->FindFunctionByName(FunctionName) : nullptr;
|
|
Func = Func ? FEmitHelper::GetOriginalFunction(Func) : nullptr;
|
|
if (Func)
|
|
{
|
|
Result.Add(Func);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::EmitStructProperties(FEmitterLocalContext& EmitterContext, UStruct* SourceClass)
|
|
{
|
|
// Emit class variables
|
|
for (TFieldIterator<UProperty> It(SourceClass, EFieldIteratorFlags::ExcludeSuper); It; ++It)
|
|
{
|
|
UProperty* Property = *It;
|
|
check(Property);
|
|
FString PropertyMacro(TEXT("UPROPERTY("));
|
|
{
|
|
TArray<FString> Tags = FEmitHelper::ProperyFlagsToTags(Property->PropertyFlags, nullptr != Cast<UClass>(SourceClass));
|
|
Tags.Emplace(FEmitHelper::HandleRepNotifyFunc(Property));
|
|
Tags.Emplace(FEmitHelper::HandleMetaData(Property, false));
|
|
Tags.Remove(FString());
|
|
|
|
FString AllTags;
|
|
FEmitHelper::ArrayToString(Tags, AllTags, TEXT(", "));
|
|
PropertyMacro += AllTags;
|
|
}
|
|
PropertyMacro += TEXT(")");
|
|
EmitterContext.Header.AddLine(PropertyMacro);
|
|
|
|
const FString CppDeclaration = EmitterContext.ExportCppDeclaration(Property, EExportedDeclaration::Member, EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend | EPropertyExportCPPFlags::CPPF_NoConst);
|
|
EmitterContext.Header.AddLine(CppDeclaration + TEXT(";"));
|
|
}
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::DeclareDelegates(FEmitterLocalContext& EmitterContext, TIndirectArray<FKismetFunctionContext>& Functions)
|
|
{
|
|
// MC DELEGATE DECLARATION
|
|
{
|
|
FEmitHelper::EmitMulticastDelegateDeclarations(EmitterContext);
|
|
}
|
|
|
|
// GATHER ALL SC DELEGATES
|
|
{
|
|
TArray<UDelegateProperty*> Delegates;
|
|
for (TFieldIterator<UDelegateProperty> It(EmitterContext.GetCurrentlyGeneratedClass(), EFieldIteratorFlags::ExcludeSuper); It; ++It)
|
|
{
|
|
Delegates.Add(*It);
|
|
}
|
|
|
|
for (auto& FuncContext : Functions)
|
|
{
|
|
for (TFieldIterator<UDelegateProperty> It(FuncContext.Function, EFieldIteratorFlags::ExcludeSuper); It; ++It)
|
|
{
|
|
Delegates.Add(*It);
|
|
}
|
|
}
|
|
|
|
TArray<UFunction*> SCDelegateSignaturesWithoutType;
|
|
// Don't declare signatures, that are already declared in a native class
|
|
for (int32 I = 0; I < Delegates.Num();)
|
|
{
|
|
auto Delegate = Delegates[I];
|
|
auto DelegateSignature = Delegate ? Delegate->SignatureFunction : nullptr;
|
|
auto DelegateSignatureOwner = DelegateSignature ? DelegateSignature->GetOwnerStruct() : nullptr;
|
|
if (DelegateSignatureOwner && DelegateSignatureOwner->HasAnyInternalFlags(EInternalObjectFlags::Native))
|
|
{
|
|
if (DelegateSignature->HasAllFunctionFlags(FUNC_MulticastDelegate))
|
|
{
|
|
SCDelegateSignaturesWithoutType.AddUnique(DelegateSignature);
|
|
}
|
|
Delegates.RemoveAtSwap(I);
|
|
}
|
|
else
|
|
{
|
|
I++;
|
|
}
|
|
}
|
|
|
|
// remove duplicates, n^2, but n is small:
|
|
for (int32 I = 0; I < Delegates.Num(); ++I)
|
|
{
|
|
UFunction* TargetFn = Delegates[I]->SignatureFunction;
|
|
for (int32 J = I + 1; J < Delegates.Num();)
|
|
{
|
|
if (TargetFn == Delegates[J]->SignatureFunction)
|
|
{
|
|
// swap erase:
|
|
Delegates[J] = Delegates[Delegates.Num() - 1];
|
|
Delegates.RemoveAt(Delegates.Num() - 1);
|
|
}
|
|
else
|
|
{
|
|
J++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (UFunction* SCDelegateSignature : SCDelegateSignaturesWithoutType)
|
|
{
|
|
FString SCType = FString::Printf(TEXT("F__%s__SC"), *FEmitHelper::GetCppName(SCDelegateSignature));
|
|
FEmitHelper::EmitSinglecastDelegateDeclarations_Inner(EmitterContext, SCDelegateSignature, SCType);
|
|
EmitterContext.MCDelegateSignatureToSCDelegateType.Add(SCDelegateSignature, SCType);
|
|
}
|
|
|
|
FEmitHelper::EmitSinglecastDelegateDeclarations(EmitterContext, Delegates);
|
|
}
|
|
}
|
|
|
|
FString FBlueprintCompilerCppBackendBase::GenerateCodeFromClass(UClass* SourceClass, TIndirectArray<FKismetFunctionContext>& Functions, bool bGenerateStubsOnly, FString& OutCppBody)
|
|
{
|
|
CleanBackend();
|
|
for (auto& FunctionContext : Functions)
|
|
{
|
|
if (FunctionContext.bIsUbergraph)
|
|
{
|
|
UberGraphContext = &FunctionContext;
|
|
for (int32 ExecutionGroupIndex = 0; ExecutionGroupIndex < FunctionContext.UnsortedSeparateExecutionGroups.Num(); ++ExecutionGroupIndex)
|
|
{
|
|
TSet<UEdGraphNode*>& ExecutionGroup = FunctionContext.UnsortedSeparateExecutionGroups[ExecutionGroupIndex];
|
|
for (UEdGraphNode* LocNode : ExecutionGroup)
|
|
{
|
|
TArray<FBlueprintCompiledStatement*>* LocStatementsPtr = FunctionContext.StatementsPerNode.Find(LocNode);
|
|
if (ensure(LocStatementsPtr))
|
|
{
|
|
for (FBlueprintCompiledStatement* LocStatement : *LocStatementsPtr)
|
|
{
|
|
UberGraphStatementToExecutionGroup.Add(LocStatement, ExecutionGroupIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// use GetBaseFilename() so that we can coordinate #includes and filenames
|
|
auto CleanCppClassName = FEmitHelper::GetBaseFilename(SourceClass);
|
|
auto CppClassName = FEmitHelper::GetCppName(SourceClass);
|
|
|
|
FGatherConvertedClassDependencies Dependencies(SourceClass);
|
|
FEmitterLocalContext EmitterContext(Dependencies);
|
|
|
|
EmitFileBeginning(CleanCppClassName, EmitterContext);
|
|
|
|
// C4883 is a strange error (for big functions), introduced in VS2015 update 2
|
|
EmitterContext.Body.AddLine(TEXT("#ifdef _MSC_VER"));
|
|
EmitterContext.Body.AddLine(TEXT("#pragma warning (push)"));
|
|
EmitterContext.Body.AddLine(TEXT("#pragma warning (disable : 4883)"));
|
|
EmitterContext.Body.AddLine(TEXT("#endif"));
|
|
|
|
{
|
|
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
|
|
|
|
auto MarkAsNecessary = [](IBlueprintCompilerCppBackendModule& InBackEndModule, const FGatherConvertedClassDependencies InDependencies, UField* InField)
|
|
{
|
|
UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(InField);
|
|
UBlueprint* BP = (BPGC && !InDependencies.WillClassBeConverted(BPGC)) ? Cast<UBlueprint>(BPGC->ClassGeneratedBy) : nullptr;
|
|
if (BP)
|
|
{
|
|
InBackEndModule.OnIncludingUnconvertedBP().ExecuteIfBound(BP);
|
|
}
|
|
};
|
|
for (UField* Field : Dependencies.IncludeInHeader)
|
|
{
|
|
MarkAsNecessary(BackEndModule, Dependencies, Field);
|
|
}
|
|
for (UField* Field : Dependencies.DeclareInHeader)
|
|
{
|
|
MarkAsNecessary(BackEndModule, Dependencies, Field);
|
|
}
|
|
for (UField* Field : Dependencies.IncludeInBody)
|
|
{
|
|
MarkAsNecessary(BackEndModule, Dependencies, Field);
|
|
}
|
|
}
|
|
|
|
UClass* OriginalSourceClass = Dependencies.FindOriginalClass(SourceClass);
|
|
ensure(OriginalSourceClass != SourceClass);
|
|
|
|
// Class declaration
|
|
const bool bIsInterface = SourceClass->IsChildOf<UInterface>();
|
|
if (bIsInterface)
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("UINTERFACE(Blueprintable, %s)"), *FEmitHelper::ReplaceConvertedMetaData(OriginalSourceClass)));
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("class %s : public UInterface"), *FEmitHelper::GetCppName(SourceClass, true)));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("GENERATED_BODY()"));
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("};"));
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("class %s"), *CppClassName));
|
|
}
|
|
else
|
|
{
|
|
TArray<FString> AdditionalMD;
|
|
const FString ReplaceConvertedMD = FEmitHelper::GenerateReplaceConvertedMD(OriginalSourceClass);
|
|
if (!ReplaceConvertedMD.IsEmpty())
|
|
{
|
|
AdditionalMD.Add(ReplaceConvertedMD);
|
|
}
|
|
|
|
// AdditionalMD.Add(FString::Printf(TEXT("CustomDynamicClassInitialization=\"%s::__CustomDynamicClassInitialization\""), *CppClassName));
|
|
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("UCLASS(%s%s)")
|
|
, (!SourceClass->IsChildOf<UBlueprintFunctionLibrary>()) ? TEXT("Blueprintable, BlueprintType, ") : TEXT("")
|
|
, *FEmitHelper::HandleMetaData(nullptr, false, &AdditionalMD)));
|
|
|
|
UClass* SuperClass = SourceClass->GetSuperClass();
|
|
FString ClassDefinition = FString::Printf(TEXT("class %s : public %s"), *CppClassName, *FEmitHelper::GetCppName(SuperClass));
|
|
|
|
for (auto& ImplementedInterface : SourceClass->Interfaces)
|
|
{
|
|
if (ImplementedInterface.Class)
|
|
{
|
|
ClassDefinition += FString::Printf(TEXT(", public %s"), *FEmitHelper::GetCppName(ImplementedInterface.Class));
|
|
}
|
|
}
|
|
EmitterContext.Header.AddLine(ClassDefinition);
|
|
}
|
|
|
|
// Begin scope
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.AddLine(TEXT("public:"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("GENERATED_BODY()"));
|
|
|
|
DeclareDelegates(EmitterContext, Functions);
|
|
|
|
EmitStructProperties(EmitterContext, SourceClass);
|
|
|
|
// Emit function declarations and definitions (writes to header and body simultaneously)
|
|
if (!bIsInterface)
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("%s(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());"), *CppClassName));
|
|
EmitterContext.Header.AddLine(TEXT("virtual void PostLoadSubobjects(FObjectInstancingGraph* OuterInstanceGraph) override;"));
|
|
EmitterContext.Header.AddLine(TEXT("static void __StaticDependenciesAssets(TArray<FBlueprintDependencyData>& AssetsToLoad);"));
|
|
EmitterContext.Header.AddLine(TEXT("static void __CustomDynamicClassInitialization(UDynamicClass* InDynamicClass);"));
|
|
|
|
FEmitDefaultValueHelper::GenerateConstructor(EmitterContext);
|
|
}
|
|
|
|
// Create the state map
|
|
for (int32 i = 0; i < Functions.Num(); ++i)
|
|
{
|
|
StateMapPerFunction.Add(FFunctionLabelInfo());
|
|
FunctionIndexMap.Add(&Functions[i], i);
|
|
}
|
|
|
|
for (int32 i = 0; i < Functions.Num(); ++i)
|
|
{
|
|
if (Functions[i].IsValid())
|
|
{
|
|
ConstructFunction(Functions[i], EmitterContext, bGenerateStubsOnly);
|
|
}
|
|
}
|
|
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("public:"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
|
|
FBackendHelperUMG::WidgetFunctionsInHeader(EmitterContext);
|
|
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("};"));
|
|
|
|
FEmitHelper::EmitLifetimeReplicatedPropsImpl(EmitterContext);
|
|
|
|
EmitterContext.Body.AddLine(TEXT("#ifdef _MSC_VER"));
|
|
EmitterContext.Body.AddLine(TEXT("#pragma warning (pop)"));
|
|
EmitterContext.Body.AddLine(TEXT("#endif"));
|
|
|
|
CleanBackend();
|
|
|
|
OutCppBody = EmitterContext.Body.Result;
|
|
return EmitterContext.Header.Result;
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::DeclareLocalVariables(FEmitterLocalContext& EmitterContext, TArray<UProperty*>& LocalVariables)
|
|
{
|
|
for (int32 i = 0; i < LocalVariables.Num(); ++i)
|
|
{
|
|
UProperty* LocalVariable = LocalVariables[i];
|
|
|
|
const FString CppDeclaration = EmitterContext.ExportCppDeclaration(LocalVariable, EExportedDeclaration::Local, EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend);
|
|
EmitterContext.AddLine(CppDeclaration + TEXT("{};"));
|
|
}
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::ConstructFunction(FKismetFunctionContext& FunctionContext, FEmitterLocalContext& EmitterContext, bool bGenerateStubOnly)
|
|
{
|
|
if (FunctionContext.IsDelegateSignature())
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<UProperty*> LocalVariables;
|
|
TArray<UProperty*> ArgumentList;
|
|
// Split the function property list into arguments, a return value (if any), and local variable declarations
|
|
for (UProperty* Property : TFieldRange<UProperty>(FunctionContext.Function))
|
|
{
|
|
const bool bNeedLocalVariable = !Property->HasAnyPropertyFlags(CPF_Parm) || Property->HasAnyPropertyFlags(CPF_ReturnParm);
|
|
TArray<UProperty*>& PropertyDest = bNeedLocalVariable ? LocalVariables : ArgumentList;
|
|
PropertyDest.Add(Property);
|
|
}
|
|
|
|
static const FBoolConfigValueHelper UsePRAGMA_DISABLE_OPTIMIZATION(TEXT("BlueprintNativizationSettings"), TEXT("bUsePRAGMA_DISABLE_OPTIMIZATION"));
|
|
if (FunctionContext.bIsUbergraph && UsePRAGMA_DISABLE_OPTIMIZATION)
|
|
{
|
|
EmitterContext.AddLine(TEXT("PRAGMA_DISABLE_OPTIMIZATION"));
|
|
}
|
|
|
|
TArray<FString> BodyFunctionsDeclaration = ConstructFunctionDeclaration(EmitterContext, FunctionContext, ArgumentList);
|
|
ensure((BodyFunctionsDeclaration.Num() == FunctionContext.UnsortedSeparateExecutionGroups.Num())
|
|
|| ((1 == BodyFunctionsDeclaration.Num()) && (0 == FunctionContext.UnsortedSeparateExecutionGroups.Num())));
|
|
|
|
const bool bIsConstFunction = FunctionContext.Function->HasAllFunctionFlags(FUNC_Const);
|
|
if (bIsConstFunction)
|
|
{
|
|
ensure(0 == FunctionContext.UnsortedSeparateExecutionGroups.Num());
|
|
ensure(1 == BodyFunctionsDeclaration.Num());
|
|
const FString InnerImplementationFunctionName = FString::Printf(TEXT("%s_Inner_%d")
|
|
, *FEmitHelper::GetCppName(FunctionContext.Function)
|
|
, FEmitHelper::GetInheritenceLevel(FunctionContext.Function->GetOwnerStruct()));
|
|
|
|
const FString ReturnType = GenerateReturnType(EmitterContext, FunctionContext.Function);
|
|
const FString ArgList = GenerateArgList(EmitterContext, ArgumentList);
|
|
const FString ArgListNoTypes = GenerateArgList(EmitterContext, ArgumentList, true);
|
|
const FString ClassCppName = FEmitHelper::GetCppName(EmitterContext.GetCurrentlyGeneratedClass());
|
|
|
|
// Inner header declaration
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("%s %s%s;")
|
|
, *ReturnType, *InnerImplementationFunctionName, *ArgList));
|
|
|
|
// Function original declaration
|
|
EmitterContext.AddLine(BodyFunctionsDeclaration[0]);
|
|
//Original implementation
|
|
EmitterContext.AddLine(TEXT("{"));
|
|
EmitterContext.IncreaseIndent();
|
|
EmitterContext.AddLine(FString::Printf(TEXT("%sconst_cast<%s*>(this)->%s%s;")
|
|
, FunctionContext.Function->GetReturnProperty() ? TEXT("return ") : TEXT("")
|
|
, *ClassCppName
|
|
, *InnerImplementationFunctionName
|
|
, *ArgListNoTypes));
|
|
EmitterContext.DecreaseIndent();
|
|
EmitterContext.AddLine(TEXT("}"));
|
|
|
|
// Inner body declaration
|
|
EmitterContext.AddLine(FString::Printf(TEXT("%s %s::%s%s")
|
|
, *ReturnType, *ClassCppName, *InnerImplementationFunctionName, *ArgList));
|
|
|
|
}
|
|
const bool bManyExecutionGroups = FunctionContext.UnsortedSeparateExecutionGroups.Num() > 0;
|
|
for (int32 ExecutionGroupIndex = bManyExecutionGroups ? 0 : -1; ExecutionGroupIndex < FunctionContext.UnsortedSeparateExecutionGroups.Num(); ExecutionGroupIndex++)
|
|
{
|
|
if (!bIsConstFunction)
|
|
{
|
|
EmitterContext.AddLine(BodyFunctionsDeclaration[bManyExecutionGroups ? ExecutionGroupIndex : 0]);
|
|
}
|
|
// Start the body of the implementation
|
|
EmitterContext.AddLine(TEXT("{"));
|
|
EmitterContext.IncreaseIndent();
|
|
if (!bGenerateStubOnly)
|
|
{
|
|
for (UProperty* Property : ArgumentList)
|
|
{
|
|
if (FEmitHelper::PropertyForConstCast(Property))
|
|
{
|
|
const uint32 ExportFlags = EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend | EPropertyExportCPPFlags::CPPF_NoConst;
|
|
const FString ActualArg = EmitterContext.ExportCppDeclaration(Property, EExportedDeclaration::Parameter, ExportFlags, false);
|
|
const FString NoConstType = EmitterContext.ExportCppDeclaration(Property, EExportedDeclaration::Parameter, ExportFlags, true);
|
|
const FString TypeDefName = FString(TEXT("T")) + EmitterContext.GenerateUniqueLocalName();
|
|
EmitterContext.AddLine(FString::Printf(TEXT("typedef %s %s;"), *NoConstType, *TypeDefName));
|
|
EmitterContext.AddLine(FString::Printf(TEXT("%s = *const_cast<%s *>(&%s__const);"), *ActualArg, *TypeDefName, *FEmitHelper::GetCppName(Property)));
|
|
}
|
|
}
|
|
DeclareLocalVariables(EmitterContext, LocalVariables);
|
|
ConstructFunctionBody(EmitterContext, FunctionContext, bManyExecutionGroups ? ExecutionGroupIndex : -1);
|
|
}
|
|
|
|
if (UProperty* ReturnValue = FunctionContext.Function->GetReturnProperty())
|
|
{
|
|
EmitterContext.AddLine(FString::Printf(TEXT("return %s;"), *FEmitHelper::GetCppName(ReturnValue)));
|
|
}
|
|
|
|
EmitterContext.DecreaseIndent();
|
|
EmitterContext.AddLine(TEXT("}"));
|
|
}
|
|
|
|
if (FunctionContext.bIsUbergraph && UsePRAGMA_DISABLE_OPTIMIZATION)
|
|
{
|
|
EmitterContext.AddLine(TEXT("PRAGMA_ENABLE_OPTIMIZATION"));
|
|
}
|
|
}
|
|
|
|
TArray<FString> FBlueprintCompilerCppBackendBase::ConstructFunctionDeclaration(FEmitterLocalContext &EmitterContext, FKismetFunctionContext &FunctionContext, TArray<UProperty*> &ArgumentList)
|
|
{
|
|
FString FunctionHeaderName = FEmitHelper::GetCppName(FunctionContext.Function);
|
|
FString FunctionBodyName = FunctionHeaderName;
|
|
const bool bStaticFunction = FunctionContext.Function->HasAllFunctionFlags(FUNC_Static);
|
|
const bool bInInterface = FunctionContext.Function->GetOwnerClass()->IsChildOf<UInterface>();
|
|
bool bAddConst = false;
|
|
bool bIsOverride = false;
|
|
bool bIsVirtual = !bInInterface && !bStaticFunction && !FunctionContext.IsEventGraph();
|
|
|
|
FString MacroUFUNCTION;
|
|
{
|
|
UFunction* const Function = FunctionContext.Function;
|
|
UFunction* const OriginalFunction = FEmitHelper::GetOriginalFunction(Function);
|
|
TArray<FString> AdditionalMetaData;
|
|
TArray<FString> AdditionalTags;
|
|
bool bGenerateAsNativeEventImplementation = false;
|
|
const bool bNetImplementation = !bInInterface && Function->HasAllFunctionFlags(FUNC_Net) && !Function->HasAnyFunctionFlags(FUNC_NetResponse);
|
|
|
|
const UBlueprintGeneratedClass* const OriginalFuncOwnerAsBPGC = Cast<UBlueprintGeneratedClass>(OriginalFunction->GetOwnerClass());
|
|
const bool bBPInterfaceImplementation = OriginalFuncOwnerAsBPGC && OriginalFuncOwnerAsBPGC->IsChildOf<UInterface>();
|
|
|
|
if (bInInterface)
|
|
{
|
|
AdditionalTags.Emplace(TEXT("BlueprintImplementableEvent"));
|
|
}
|
|
else if (bNetImplementation)
|
|
{
|
|
FunctionBodyName = FunctionHeaderName + TEXT("_Implementation");
|
|
}
|
|
else if (FEmitHelper::ShouldHandleAsNativeEvent(Function))
|
|
{
|
|
bGenerateAsNativeEventImplementation = true;
|
|
FunctionBodyName = FunctionHeaderName = FEmitHelper::GetCppName(OriginalFunction) + TEXT("_Implementation");
|
|
bAddConst = OriginalFunction->HasAllFunctionFlags(FUNC_Const);
|
|
}
|
|
else if (FEmitHelper::ShouldHandleAsImplementableEvent(Function) || bBPInterfaceImplementation)
|
|
{
|
|
//The function "bpf__BIE__pf" should never be called directly. Only via function "BIE" with generated implementation.
|
|
bIsVirtual = false;
|
|
AdditionalMetaData.Emplace(TEXT("CppFromBpEvent"));
|
|
}
|
|
|
|
ensure(!bIsVirtual || Function->IsSignatureCompatibleWith(OriginalFunction));
|
|
bIsOverride = bGenerateAsNativeEventImplementation || (bIsVirtual && (Function != OriginalFunction));
|
|
|
|
auto PreliminaryConditionsToSkipMacroUFUNC = [](UFunction* InFunction) -> bool
|
|
{
|
|
check(InFunction);
|
|
return !FEmitHelper::ShouldHandleAsNativeEvent(InFunction)
|
|
&& !FEmitHelper::ShouldHandleAsImplementableEvent(InFunction)
|
|
&& !InFunction->GetOwnerClass()->IsChildOf<UInterface>()
|
|
&& !InFunction->HasAnyFunctionFlags(FUNC_Exec | FUNC_Static | FUNC_Native
|
|
| FUNC_Net | FUNC_NetServer | FUNC_NetClient | FUNC_NetMulticast | FUNC_NetReliable
|
|
| FUNC_BlueprintAuthorityOnly | FUNC_BlueprintCosmetic | FUNC_NetValidate
|
|
| FUNC_MulticastDelegate | FUNC_Delegate);
|
|
};
|
|
|
|
auto FunctionIsBoundToAnyDelegate = [](UFunction* InFunction)
|
|
{
|
|
check(InFunction);
|
|
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
|
|
auto& IsFunctionUsedInADelegate = BackEndModule.GetIsFunctionUsedInADelegateCallback();
|
|
return ensure(IsFunctionUsedInADelegate.IsBound()) ? IsFunctionUsedInADelegate.Execute(InFunction) : true;
|
|
};
|
|
|
|
auto IsFunctionUsedByReplication = [](UFunction* InFunction)
|
|
{
|
|
check(InFunction);
|
|
for (UProperty* Prop : TFieldRange<UProperty>(InFunction->GetOwnerClass(), EFieldIteratorFlags::ExcludeSuper))
|
|
{
|
|
if (Prop && FEmitHelper::HasAllFlags(Prop->PropertyFlags, CPF_Net | CPF_RepNotify) && (Prop->RepNotifyFunc == InFunction->GetFName()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
static const FBoolConfigValueHelper TryToSkipMacroUFUNC(TEXT("BlueprintNativizationSettings"), TEXT("bSkipUFUNCTION"));
|
|
const bool bTryToSkipMacroUFUNC = TryToSkipMacroUFUNC; // should be enabled only when all BP are compiled.
|
|
const bool bSkipMacro = bTryToSkipMacroUFUNC
|
|
&& !FunctionContext.bIsUbergraph // uber-graph is necessary for latent actions
|
|
&& PreliminaryConditionsToSkipMacroUFUNC(Function)
|
|
&& ((Function == OriginalFunction) || PreliminaryConditionsToSkipMacroUFUNC(OriginalFunction))
|
|
&& !IsFunctionUsedByReplication(Function)
|
|
&& !FunctionIsBoundToAnyDelegate(OriginalFunction);
|
|
|
|
if (bNetImplementation && bIsOverride)
|
|
{
|
|
FunctionHeaderName = FunctionBodyName;
|
|
}
|
|
else if (!bGenerateAsNativeEventImplementation && !bSkipMacro)
|
|
{
|
|
MacroUFUNCTION = FEmitHelper::EmitUFuntion(Function, AdditionalTags, AdditionalMetaData);
|
|
}
|
|
}
|
|
|
|
const bool bManyExecutionGroups = FunctionContext.UnsortedSeparateExecutionGroups.Num() > 0;
|
|
|
|
TArray<FString> Result;
|
|
for (int32 ExecutionGroupIndex = bManyExecutionGroups ? 0 : -1; ExecutionGroupIndex < FunctionContext.UnsortedSeparateExecutionGroups.Num(); ExecutionGroupIndex++)
|
|
{
|
|
bool bNeedMacro = true;
|
|
if (bManyExecutionGroups)
|
|
{
|
|
bNeedMacro = false;
|
|
for (UEdGraphNode* NodeIt : FunctionContext.UnsortedSeparateExecutionGroups[ExecutionGroupIndex])
|
|
{
|
|
UK2Node_CallFunction* CallFuncNode = Cast<UK2Node_CallFunction>(NodeIt);
|
|
if (CallFuncNode && CallFuncNode->IsLatentFunction())
|
|
{
|
|
bNeedMacro = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!MacroUFUNCTION.IsEmpty() && bNeedMacro)
|
|
{
|
|
const FString OldExecutionFunctionName = UEdGraphSchema_K2::FN_ExecuteUbergraphBase.ToString() + TEXT("_") + FunctionContext.Blueprint->GetName();
|
|
const FString NewExecutionFunctionName = OldExecutionFunctionName + FString::Printf(TEXT("_%d"), ExecutionGroupIndex);
|
|
const FString LocMacroUFUNCTION = bManyExecutionGroups
|
|
? MacroUFUNCTION.Replace(*OldExecutionFunctionName, *NewExecutionFunctionName)
|
|
: MacroUFUNCTION;
|
|
EmitterContext.Header.AddLine(LocMacroUFUNCTION);
|
|
}
|
|
|
|
const FString ReturnType = GenerateReturnType(EmitterContext, FunctionContext.Function);
|
|
const FString ArgList = GenerateArgList(EmitterContext, ArgumentList);
|
|
const FString FunctionNamePostfix = (-1 == ExecutionGroupIndex) ? FString() : FString::Printf(TEXT("_%d"), ExecutionGroupIndex);
|
|
|
|
Result.Add(FString::Printf(TEXT("%s %s::%s%s%s%s")
|
|
, *ReturnType
|
|
, *FEmitHelper::GetCppName(EmitterContext.GetCurrentlyGeneratedClass())
|
|
, *FunctionBodyName
|
|
, *FunctionNamePostfix
|
|
, *ArgList
|
|
, bAddConst ? TEXT(" const") : TEXT("")));
|
|
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("%s%s%s %s%s%s%s%s;")
|
|
, bStaticFunction ? TEXT("static ") : TEXT("")
|
|
, bIsVirtual ? TEXT("virtual ") : TEXT("")
|
|
, *ReturnType
|
|
, *FunctionHeaderName
|
|
, *FunctionNamePostfix
|
|
, *ArgList
|
|
, bAddConst ? TEXT(" const") : TEXT("")
|
|
, bIsOverride ? TEXT(" override") : TEXT("")));
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
FString FBlueprintCompilerCppBackendBase::GenerateArgList(const FEmitterLocalContext &EmitterContext, const TArray<UProperty*> &ArgumentList, bool bOnlyParamName)
|
|
{
|
|
FString ArgListStr = TEXT("(");
|
|
for (int32 i = 0; i < ArgumentList.Num(); ++i)
|
|
{
|
|
UProperty* ArgProperty = ArgumentList[i];
|
|
|
|
if (i > 0)
|
|
{
|
|
ArgListStr += TEXT(", ");
|
|
}
|
|
|
|
const FString NamePostFix = FEmitHelper::PropertyForConstCast(ArgProperty) ? TEXT("__const") : TEXT("");
|
|
if (bOnlyParamName)
|
|
{
|
|
ArgListStr += FEmitHelper::GetCppName(ArgProperty) + NamePostFix;
|
|
}
|
|
else
|
|
{
|
|
if (ArgProperty->HasAnyPropertyFlags(CPF_OutParm))
|
|
{
|
|
ArgListStr += TEXT("/*out*/ ");
|
|
}
|
|
ArgListStr += EmitterContext.ExportCppDeclaration(ArgProperty
|
|
, EExportedDeclaration::Parameter
|
|
, EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend | EPropertyExportCPPFlags::CPPF_ArgumentOrReturnValue
|
|
, false
|
|
, NamePostFix);
|
|
}
|
|
}
|
|
ArgListStr += TEXT(")");
|
|
return ArgListStr;
|
|
}
|
|
|
|
FString FBlueprintCompilerCppBackendBase::GenerateReturnType(const FEmitterLocalContext &EmitterContext, const UFunction* Function)
|
|
{
|
|
UProperty* ReturnValue = Function->GetReturnProperty();
|
|
if (ReturnValue)
|
|
{
|
|
const uint32 LocalExportCPPFlags = EPropertyExportCPPFlags::CPPF_CustomTypeName
|
|
| EPropertyExportCPPFlags::CPPF_NoConst
|
|
| EPropertyExportCPPFlags::CPPF_NoRef
|
|
| EPropertyExportCPPFlags::CPPF_NoStaticArray
|
|
| EPropertyExportCPPFlags::CPPF_BlueprintCppBackend
|
|
| EPropertyExportCPPFlags::CPPF_ArgumentOrReturnValue;
|
|
return EmitterContext.ExportCppDeclaration(ReturnValue, EExportedDeclaration::Parameter, LocalExportCPPFlags, true);
|
|
}
|
|
return TEXT("void");
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::ConstructFunctionBody(FEmitterLocalContext& EmitterContext, FKismetFunctionContext &FunctionContext, int32 ExecutionGroup)
|
|
{
|
|
if (FunctionContext.UnsortedSeparateExecutionGroups.Num() && (ExecutionGroup < 0))
|
|
{
|
|
// uso only for latent actions..
|
|
return;
|
|
}
|
|
|
|
// Run thru code looking only at things marked as jump targets, to make sure the jump targets are ordered in order of appearance in the linear execution list
|
|
// Emit code in the order specified by the linear execution list (the first node is always the entry point for the function)
|
|
for (int32 NodeIndex = 0; NodeIndex < FunctionContext.LinearExecutionList.Num(); ++NodeIndex)
|
|
{
|
|
UEdGraphNode* StatementNode = FunctionContext.LinearExecutionList[NodeIndex];
|
|
TArray<FBlueprintCompiledStatement*>* StatementList = FunctionContext.StatementsPerNode.Find(StatementNode);
|
|
|
|
if (StatementList != NULL)
|
|
{
|
|
for (int32 StatementIndex = 0; StatementIndex < StatementList->Num(); ++StatementIndex)
|
|
{
|
|
FBlueprintCompiledStatement& Statement = *((*StatementList)[StatementIndex]);
|
|
|
|
if (Statement.bIsJumpTarget)
|
|
{
|
|
// Just making sure we number them in order of appearance, so jump statements don't influence the order
|
|
const int32 StateNum = StatementToStateIndex(FunctionContext, &Statement);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
InnerFunctionImplementation(FunctionContext, EmitterContext, ExecutionGroup);
|
|
}
|
|
|
|
FString FBlueprintCompilerCppBackendBase::GenerateCodeFromEnum(UUserDefinedEnum* SourceEnum)
|
|
{
|
|
check(SourceEnum);
|
|
FCodeText Header;
|
|
Header.AddLine(TEXT("#pragma once"));
|
|
const FString EnumCppName = *FEmitHelper::GetCppName(SourceEnum);
|
|
// use GetBaseFilename() so that we can coordinate #includes and filenames
|
|
Header.AddLine(FString::Printf(TEXT("#include \"%s.generated.h\""), *FEmitHelper::GetBaseFilename(SourceEnum)));
|
|
Header.AddLine(FString::Printf(TEXT("UENUM(BlueprintType, %s )"), *FEmitHelper::ReplaceConvertedMetaData(SourceEnum)));
|
|
Header.AddLine(FString::Printf(TEXT("enum class %s : uint8"), *EnumCppName));
|
|
Header.AddLine(TEXT("{"));
|
|
Header.IncreaseIndent();
|
|
|
|
auto EnumItemName = [&](int32 InIndex)
|
|
{
|
|
const int32 ElemValue = SourceEnum->GetValueByIndex(InIndex);
|
|
if (ElemValue == SourceEnum->GetMaxEnumValue())
|
|
{
|
|
return FString::Printf(TEXT("%s_MAX"), *EnumCppName);
|
|
}
|
|
return SourceEnum->GetEnumName(InIndex);
|
|
};
|
|
|
|
for (int32 Index = 0; Index < SourceEnum->NumEnums(); ++Index)
|
|
{
|
|
const FString ElemName = EnumItemName(Index);
|
|
const int32 ElemValue = SourceEnum->GetValueByIndex(Index);
|
|
|
|
const FString& DisplayNameMD = SourceEnum->GetMetaData(TEXT("DisplayName"), ElemValue);// TODO: value or index?
|
|
const FString Meta = DisplayNameMD.IsEmpty() ? FString() : FString::Printf(TEXT("UMETA(DisplayName = \"%s\")"), *DisplayNameMD.ReplaceCharWithEscapedChar());
|
|
Header.AddLine(FString::Printf(TEXT("%s = %d %s,"), *ElemName, ElemValue, *Meta));
|
|
}
|
|
|
|
Header.DecreaseIndent();
|
|
Header.AddLine(TEXT("};"));
|
|
|
|
Header.AddLine(FString::Printf(TEXT("inline FString %s__GetUserFriendlyName(int32 InValue)"), *EnumCppName));
|
|
Header.AddLine(TEXT("{"));
|
|
Header.IncreaseIndent();
|
|
|
|
Header.AddLine(TEXT("FText Text;"));
|
|
Header.AddLine(FString::Printf(TEXT("const auto EnumValue = static_cast<%s>(InValue);"), *EnumCppName));
|
|
Header.AddLine(TEXT("switch(EnumValue)"));
|
|
Header.AddLine(TEXT("{"));
|
|
Header.IncreaseIndent();
|
|
for (int32 Index = 0; Index < SourceEnum->NumEnums(); ++Index)
|
|
{
|
|
const FString ElemName = EnumItemName(Index);
|
|
FString DisplayNameStr;
|
|
FTextStringHelper::WriteToString(DisplayNameStr, SourceEnum->GetEnumText(Index));
|
|
Header.AddLine(FString::Printf(TEXT("case %s::%s: FTextStringHelper::ReadFromString(TEXT(\"%s\"), Text); break;"), *EnumCppName, *ElemName, *DisplayNameStr.ReplaceCharWithEscapedChar()));
|
|
}
|
|
|
|
Header.AddLine(TEXT("default: ensure(false);"));
|
|
Header.DecreaseIndent();
|
|
Header.AddLine(TEXT("};"));
|
|
|
|
Header.AddLine(TEXT("return Text.ToString();"));
|
|
Header.DecreaseIndent();
|
|
Header.AddLine(TEXT("};"));
|
|
|
|
return Header.Result;
|
|
}
|
|
|
|
FString FBlueprintCompilerCppBackendBase::GenerateCodeFromStruct(UUserDefinedStruct* SourceStruct)
|
|
{
|
|
check(SourceStruct);
|
|
FGatherConvertedClassDependencies Dependencies(SourceStruct);
|
|
FEmitterLocalContext EmitterContext(Dependencies);
|
|
// use GetBaseFilename() so that we can coordinate #includes and filenames
|
|
EmitFileBeginning(FEmitHelper::GetBaseFilename(SourceStruct), EmitterContext, true, true);
|
|
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("USTRUCT(BlueprintType, %s)"), *FEmitHelper::ReplaceConvertedMetaData(SourceStruct)));
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("struct %s"), *FEmitHelper::GetCppName(SourceStruct)));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.AddLine(TEXT("public:"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("GENERATED_BODY()"));
|
|
EmitStructProperties(EmitterContext, SourceStruct);
|
|
|
|
FEmitDefaultValueHelper::GenerateGetDefaultValue(SourceStruct, EmitterContext);
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("};"));
|
|
|
|
return EmitterContext.Header.Result;
|
|
}
|
|
|
|
FString FBlueprintCompilerCppBackendBase::GenerateWrapperForClass(UClass* SourceClass)
|
|
{
|
|
FGatherConvertedClassDependencies Dependencies(SourceClass);
|
|
FEmitterLocalContext EmitterContext(Dependencies);
|
|
|
|
UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(SourceClass);
|
|
|
|
TArray<UFunction*> FunctionsToGenerate;
|
|
for (auto Func : TFieldRange<UFunction>(SourceClass, EFieldIteratorFlags::ExcludeSuper))
|
|
{
|
|
if (!Func->HasAnyFunctionFlags(FUNC_BlueprintCallable | FUNC_BlueprintPure))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (BPGC && (Func == BPGC->UberGraphFunction))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Exclude native events.. Unexpected.
|
|
// Exclude delegate signatures.
|
|
static const FName __UCSName(TEXT("UserConstructionScript"));
|
|
if (__UCSName == Func->GetFName())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FunctionsToGenerate.Add(Func);
|
|
}
|
|
|
|
TArray<UMulticastDelegateProperty*> MCDelegateProperties;
|
|
for (auto MCDelegateProp : TFieldRange<UMulticastDelegateProperty>(SourceClass, EFieldIteratorFlags::ExcludeSuper))
|
|
{
|
|
MCDelegateProperties.Add(MCDelegateProp);
|
|
}
|
|
|
|
const bool bGenerateAnyMCDelegateProperty = (0 != MCDelegateProperties.Num());
|
|
|
|
FString ParentStruct;
|
|
UClass* SuperClassToUse = SourceClass->GetSuperClass();
|
|
{
|
|
static const FBoolConfigValueHelper DontNativizeDataOnlyBP(TEXT("BlueprintNativizationSettings"), TEXT("bDontNativizeDataOnlyBP"));
|
|
if (DontNativizeDataOnlyBP)
|
|
{
|
|
// Find first Native or Converted Or Not Data Only class
|
|
for (; SuperClassToUse; SuperClassToUse = SuperClassToUse->GetSuperClass())
|
|
{
|
|
if (SuperClassToUse->HasAnyClassFlags(CLASS_Native))
|
|
{
|
|
break;
|
|
}
|
|
UBlueprintGeneratedClass* SuperBPGC = Cast<UBlueprintGeneratedClass>(SuperClassToUse);
|
|
if (SuperBPGC && Dependencies.WillClassBeConverted(SuperBPGC))
|
|
{
|
|
break;
|
|
}
|
|
else if (SuperBPGC)
|
|
{
|
|
UBlueprint* SuperBP = Cast<UBlueprint>(SuperBPGC->ClassGeneratedBy);
|
|
if (!ensure(SuperBP) || !FBlueprintEditorUtils::IsDataOnlyBlueprint(SuperBP))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UBlueprintGeneratedClass* SuperBPGC = Cast<UBlueprintGeneratedClass>(SuperClassToUse);
|
|
if (SuperBPGC && !Dependencies.WillClassBeConverted(SuperBPGC))
|
|
{
|
|
ParentStruct = FString::Printf(TEXT("FUnconvertedWrapper__%s"), *FEmitHelper::GetCppName(SuperBPGC));
|
|
}
|
|
else
|
|
{
|
|
ParentStruct = FString::Printf(TEXT("FUnconvertedWrapper<%s>"), *FEmitHelper::GetCppName(SuperClassToUse));
|
|
}
|
|
}
|
|
|
|
// Include standard stuff
|
|
EmitFileBeginning(FEmitHelper::GetBaseFilename(SourceClass), EmitterContext, bGenerateAnyMCDelegateProperty, true, true, SuperClassToUse);
|
|
|
|
// DELEGATES
|
|
const FString DelegatesClassName = FString::Printf(TEXT("U__Delegates__%s"), *FEmitHelper::GetCppName(SourceClass));
|
|
auto GenerateMulticastDelegateTypeName = [](UMulticastDelegateProperty* MCDelegateProp) -> FString
|
|
{
|
|
return FString::Printf(TEXT("F__MulticastDelegate__%s"), *FEmitHelper::GetCppName(MCDelegateProp));
|
|
};
|
|
if (bGenerateAnyMCDelegateProperty)
|
|
{
|
|
EmitterContext.Header.AddLine(TEXT("UCLASS()"));
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("class %s : public UObject"), *DelegatesClassName));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.AddLine(TEXT("public:"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("GENERATED_BODY()"));
|
|
for (auto MCDelegateProp : MCDelegateProperties)
|
|
{
|
|
FString ParamNumberStr, Parameters;
|
|
FEmitHelper::ParseDelegateDetails(EmitterContext, MCDelegateProp->SignatureFunction, Parameters, ParamNumberStr);
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("DECLARE_DYNAMIC_MULTICAST_DELEGATE%s(%s%s);"), *ParamNumberStr, *GenerateMulticastDelegateTypeName(MCDelegateProp), *Parameters));
|
|
}
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("};"));
|
|
}
|
|
|
|
// Begin the struct
|
|
const FString WrapperName = FString::Printf(TEXT("FUnconvertedWrapper__%s"), *FEmitHelper::GetCppName(SourceClass));
|
|
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("struct %s : public %s"), *WrapperName, *ParentStruct));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
|
|
// Constructor
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("%s(const %s* __InObject) : %s(__InObject){}"), *WrapperName, *FEmitHelper::GetCppName(EmitterContext.GetFirstNativeOrConvertedClass(SourceClass)), *ParentStruct));
|
|
|
|
static const FBoolConfigValueHelper UseStaticVariables(TEXT("BlueprintNativizationSettings"), TEXT("bUseStaticVariablesInWrappers"));
|
|
const bool bUseStaticVariables = UseStaticVariables;
|
|
|
|
// PROPERTIES:
|
|
for (auto Property : TFieldRange<UProperty>(SourceClass, EFieldIteratorFlags::ExcludeSuper))
|
|
{
|
|
if (BPGC && (Property == BPGC->UberGraphFramePointerProperty))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Cast<UAnimBlueprintGeneratedClass>(BPGC))
|
|
{
|
|
// Dont generate Getters for inner properties
|
|
UStructProperty* StructProperty = Cast<UStructProperty>(Property);
|
|
UScriptStruct* InnerStruct = StructProperty ? StructProperty->Struct : nullptr;
|
|
if (InnerStruct && InnerStruct->IsChildOf(FAnimNode_Base::StaticStruct()))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//TODO: check if the property is really used?
|
|
const FString TypeDeclaration = Property->IsA<UMulticastDelegateProperty>()
|
|
? FString::Printf(TEXT("%s::%s"), *DelegatesClassName, *GenerateMulticastDelegateTypeName(CastChecked<UMulticastDelegateProperty>(Property)))
|
|
: EmitterContext.ExportCppDeclaration(Property, EExportedDeclaration::Parameter, EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend | EPropertyExportCPPFlags::CPPF_NoRef, true);
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("FORCENOINLINE %s& GetRef__%s()"), *TypeDeclaration, *UnicodeToCPPIdentifier(Property->GetName(), false, nullptr)));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
if (bUseStaticVariables)
|
|
{
|
|
EmitterContext.Header.AddLine(TEXT("static TWeakObjectPtr<UProperty> __PropertyPtr{};"));
|
|
EmitterContext.Header.AddLine(TEXT("const UProperty* __Property = __PropertyPtr.Get();"));
|
|
EmitterContext.Header.AddLine(TEXT("if (nullptr == __Property)"));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("__Property = GetClass()->FindPropertyByName(FName(TEXT(\"%s\")));"), *Property->GetName()));
|
|
EmitterContext.Header.AddLine(TEXT("check(__Property);"));
|
|
EmitterContext.Header.AddLine(TEXT("__PropertyPtr = __Property;"));
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("}"));
|
|
}
|
|
else
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("const UProperty* __Property = GetClass()->FindPropertyByName(FName(TEXT(\"%s\")));"), *Property->GetName()));
|
|
}
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("return *(__Property->ContainerPtrToValuePtr<%s>(__Object));"), *TypeDeclaration));
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("}"));
|
|
}
|
|
|
|
// FUNCTIONS:
|
|
for (auto Func : FunctionsToGenerate)
|
|
{
|
|
TArray<FString> FuncParameters;
|
|
const FString ParamNameInStructPostfix(TEXT("_"));
|
|
const FString FuncCppName = FEmitHelper::GetCppName(Func);
|
|
FString DelareFunction = FString::Printf(TEXT("FORCENOINLINE void %s("), *FuncCppName);
|
|
FString RawParameterList;
|
|
{
|
|
bool bFirst = true;
|
|
for (TFieldIterator<UProperty> It(Func); It; ++It)
|
|
{
|
|
UProperty* Property = *It;
|
|
if (!Property || !Property->HasAnyPropertyFlags(CPF_Parm))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!bFirst)
|
|
{
|
|
DelareFunction += TEXT(", ");
|
|
RawParameterList += TEXT(", ");
|
|
}
|
|
else
|
|
{
|
|
bFirst = false;
|
|
}
|
|
|
|
if (Property->HasAnyPropertyFlags(CPF_OutParm))
|
|
{
|
|
DelareFunction += TEXT("/*out*/ ");
|
|
}
|
|
|
|
DelareFunction += EmitterContext.ExportCppDeclaration(Property, EExportedDeclaration::Parameter, EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend);
|
|
FString ParamAsStructMember;
|
|
|
|
{ // copied from FNativeClassHeaderGenerator::ExportEventParm
|
|
bool bEmitConst = Property->HasAnyPropertyFlags(CPF_ConstParm) && Property->IsA<UObjectProperty>();
|
|
const bool bIsConstParam = (Property->IsA(UInterfaceProperty::StaticClass()) && !Property->HasAllPropertyFlags(CPF_OutParm));
|
|
const bool bIsOnConstClass = (Property->IsA(UObjectProperty::StaticClass()) && ((UObjectProperty*)Property)->PropertyClass != NULL && ((UObjectProperty*)Property)->PropertyClass->HasAnyClassFlags(CLASS_Const));
|
|
if (bIsConstParam || bIsOnConstClass)
|
|
{
|
|
bEmitConst = false; // ExportCppDeclaration will do it for us
|
|
}
|
|
|
|
if (bEmitConst)
|
|
{
|
|
ParamAsStructMember = TEXT("const ");
|
|
}
|
|
}
|
|
ParamAsStructMember += EmitterContext.ExportCppDeclaration(Property, EExportedDeclaration::Local, EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend, false, ParamNameInStructPostfix);
|
|
FuncParameters.Emplace(ParamAsStructMember);
|
|
RawParameterList += UnicodeToCPPIdentifier(Property->GetName(), Property->HasAnyPropertyFlags(CPF_Deprecated), TEXT("bpp__"));
|
|
}
|
|
}
|
|
DelareFunction += TEXT(")");
|
|
EmitterContext.Header.AddLine(DelareFunction);
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
|
|
if (bUseStaticVariables)
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("static const FName __FunctionName(TEXT(\"%s\"));"), *Func->GetName()));
|
|
}
|
|
const FString FuncNameStr = bUseStaticVariables ? TEXT("__FunctionName") : FString::Printf(TEXT("FName(TEXT(\"%s\"))"), *Func->GetName());
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("UFunction* __Function = __Object->FindFunctionChecked(%s);"), *FuncNameStr));
|
|
|
|
const FString FuncParametersStructName = FuncParameters.Num() ? (FuncCppName + TEXT("_Parameters")) : FString();
|
|
if (FuncParameters.Num())
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("struct %s"), *FuncParametersStructName));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
for (FString& Param : FuncParameters)
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("%s;"), *Param));
|
|
}
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("};"));
|
|
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("%s __Parameters { %s };"), *FuncParametersStructName, *RawParameterList));
|
|
}
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("__Object->ProcessEvent(__Function, %s);"), FuncParameters.Num() ? TEXT("&__Parameters") : TEXT("nullptr")));
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("}"));
|
|
}
|
|
|
|
// close struct
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("};"));
|
|
|
|
return EmitterContext.Header.Result;
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::EmitFileBeginning(const FString& CleanName, FEmitterLocalContext& EmitterContext, bool bIncludeGeneratedH, bool bIncludeCodeHelpersInHeader, bool bFullyIncludedDeclaration, UField* AdditionalFieldToIncludeInHeader)
|
|
{
|
|
EmitterContext.Header.AddLine(TEXT("#pragma once"));
|
|
|
|
auto EmitIncludeHeader = [&](FCodeText& Dst, const TCHAR* Message, bool bAddDotH)
|
|
{
|
|
Dst.AddLine(FString::Printf(TEXT("#include \"%s%s\""), Message, bAddDotH ? TEXT(".h") : TEXT("")));
|
|
};
|
|
const FString PCHFilename = FEmitHelper::GetPCHFilename();
|
|
if (!PCHFilename.IsEmpty())
|
|
{
|
|
EmitIncludeHeader(EmitterContext.Body, *PCHFilename, false);
|
|
}
|
|
else
|
|
{
|
|
// Used when generated code is not in a separate module
|
|
const FString MainHeaderFilename = FEmitHelper::GetGameMainHeaderFilename();
|
|
if (!MainHeaderFilename.IsEmpty())
|
|
{
|
|
EmitIncludeHeader(EmitterContext.Body, *MainHeaderFilename, false);
|
|
}
|
|
}
|
|
EmitIncludeHeader(EmitterContext.Body, *CleanName, true);
|
|
EmitIncludeHeader(bIncludeCodeHelpersInHeader ? EmitterContext.Header : EmitterContext.Body, TEXT("GeneratedCodeHelpers"), true);
|
|
EmitIncludeHeader(EmitterContext.Header, TEXT("Blueprint/BlueprintSupport"), true);
|
|
|
|
FBackendHelperUMG::AdditionalHeaderIncludeForWidget(EmitterContext);
|
|
FBackendHelperAnim::AddHeaders(EmitterContext);
|
|
|
|
TSet<FString> AlreadyIncluded;
|
|
AlreadyIncluded.Add(CleanName);
|
|
auto EmitInner = [&](FCodeText& Dst, const TSet<UField*>& Src, const TSet<UField*>& Declarations)
|
|
{
|
|
auto EngineSourceDir = FPaths::EngineSourceDir();
|
|
auto GameSourceDir = FPaths::GameSourceDir();
|
|
|
|
for (UField* Field : Src)
|
|
{
|
|
if (!Field)
|
|
{
|
|
continue;
|
|
}
|
|
const bool bWantedType = Field->IsA<UBlueprintGeneratedClass>() || Field->IsA<UUserDefinedEnum>() || Field->IsA<UUserDefinedStruct>();
|
|
|
|
// Wanted no-native type, that will be converted
|
|
if (bWantedType)
|
|
{
|
|
// @TODO: Need to query if this asset will actually be converted
|
|
|
|
const FString Name = Field->GetName();
|
|
bool bAlreadyIncluded = false;
|
|
AlreadyIncluded.Add(Name, &bAlreadyIncluded);
|
|
if (!bAlreadyIncluded)
|
|
{
|
|
const FString GeneratedFilename = FEmitHelper::GetBaseFilename(Field);
|
|
EmitIncludeHeader(Dst, *GeneratedFilename, true);
|
|
}
|
|
}
|
|
// headers for native items
|
|
else
|
|
{
|
|
FString PackPath;
|
|
if (FSourceCodeNavigation::FindClassHeaderPath(Field, PackPath))
|
|
{
|
|
if (!PackPath.RemoveFromStart(EngineSourceDir))
|
|
{
|
|
if (!PackPath.RemoveFromStart(GameSourceDir))
|
|
{
|
|
PackPath = FPaths::GetCleanFilename(PackPath);
|
|
}
|
|
}
|
|
bool bAlreadyIncluded = false;
|
|
AlreadyIncluded.Add(PackPath, &bAlreadyIncluded);
|
|
if (!bAlreadyIncluded)
|
|
{
|
|
EmitIncludeHeader(Dst, *PackPath, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto Type : Declarations)
|
|
{
|
|
if (auto ForwardDeclaredType = Cast<UClass>(Type))
|
|
{
|
|
Dst.AddLine(FString::Printf(TEXT("class %s;"), *FEmitHelper::GetCppName(ForwardDeclaredType)));
|
|
}
|
|
}
|
|
};
|
|
|
|
TSet<UField*> IncludeInBody = EmitterContext.Dependencies.IncludeInBody;
|
|
TSet<UField*> IncludeInHeader = EmitterContext.Dependencies.IncludeInHeader;
|
|
if (AdditionalFieldToIncludeInHeader)
|
|
{
|
|
IncludeInHeader.Add(AdditionalFieldToIncludeInHeader);
|
|
}
|
|
EmitInner(EmitterContext.Header, IncludeInHeader, bFullyIncludedDeclaration ? TSet<UField*>() : EmitterContext.Dependencies.DeclareInHeader);
|
|
if (bFullyIncludedDeclaration)
|
|
{
|
|
EmitInner(EmitterContext.Header, EmitterContext.Dependencies.DeclareInHeader, TSet<UField*>());
|
|
}
|
|
else
|
|
{
|
|
IncludeInBody.Append(EmitterContext.Dependencies.DeclareInHeader);
|
|
}
|
|
EmitInner(EmitterContext.Body, IncludeInBody, TSet<UField*>());
|
|
|
|
if (bIncludeGeneratedH)
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("#include \"%s.generated.h\""), *CleanName));
|
|
}
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::CleanBackend()
|
|
{
|
|
StateMapPerFunction.Empty();
|
|
FunctionIndexMap.Empty();
|
|
UberGraphContext = nullptr;
|
|
UberGraphStatementToExecutionGroup.Empty();
|
|
} |