You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change3228496on 2016/12/09 by Ben.Cosh This change adds extra information to component template arrays so that the component class can be determined in builds that strip out objects of certain class types such as the editor dedicated server build. #Jira UE-38842 - "LogBlueprint:Error: [Compiler BP_Skybox_World_RandomTrees_01] Error Can't connect pins ReturnValue and Target" after entering a lobby in a synced server #Proj KismetCompiler, BlueprintGraph, UnrealEd, Core, Engine, Kismet, BlueprintCompilerCppBackend Change 3232435 on 2016/12/13 by Ben.Cosh Fix for a bug introduced in CL3228496that caused component templates to fail to be identified by name and resulted in blueprint compilation issues for add component nodes. #Jira UE-39623 - Unknown template referenced by Add Component Node #Proj BlueprintGraph, Engine Change 3234581 on 2016/12/14 by Mike.Beach Backing out fix for UE-38842 (CL 3228496/3232435/3232564) - mapping UBlueprintGeneratedClass's ComponentTemplates array to a new format was causing issues with deferred dependency loading during serialization (trying to extract type information from a placeholder object). We're opting for a smaller/simpler solution to UE-38842, which will be to store the component information on the node itself (not with the templates). #jira UE-39707 Change 3236615 on 2016/12/15 by Maciej.Mroz Nativization: Fixed getter fuctions in FUnconvertedWrapper, the returned ref won;t be const. Change 3236967 on 2016/12/15 by Dan.Oconnor Test data showing an error for jira issue UE-39808 Change 3237021 on 2016/12/15 by Dan.Oconnor UE31622 test data Change 3237046 on 2016/12/15 by Dan.Oconnor UE-14123 test data Change 3239289 on 2016/12/17 by Phillip.Kavan [UE-38999] Dump component tree node hierarchy to the output log on error state during widget generation. change summary: - added FOnTableViewBadState delegate parameter to SSCSEditorDragDropTree - added SSCSEditor::DumpTree() as the FOnTableViewBadState delegate implementatioon for the STableView widget (to provide us with more info on future occurrences) #jira UE-38999 Change 3239448 on 2016/12/19 by Maciej.Mroz #jira UE-39794 New way of collecting dependencies assets. Only directly used assets are listed. It will be used in projects with EDL enabled, once the "EDL boot time" is enabled. Nativized projects with EDL disabled use the new mechanism as well. They use new __StaticDependenciesAssets functions generator. __StaticDependenciesAssets calls recursively __StaticDependenciesAssets of all Blueprints, that the current BP depends on. It reduces the size of __StaticDependenciesAssets. Notice, that at the moment, this change should no affect any projects with EDL enabled (because the EDL boot time doesn't work yet). Change 3239778 on 2016/12/19 by Phillip.Kavan [UE-39854] Fix nativized assets build error when there are no native code dependencies. change summary: - modified FDependenciesGlobalMapHelper::EmitBodyCode() to emit a NULL entry when there are no other entries to emit, in order to avoid a zero-length array initialization error at compile time. #jira UE-39854 Change 3239965 on 2016/12/19 by Phillip.Kavan [UE-39733] Fix incorrect graph pin value display names for user-defined enum types. change summary: - switched UEnum::GetDisplayNameText() to be a virtual API - added a UUserDefinedEnum::GetDisplayNameText() override to call FEnumEditorUtils::GetEnumeratorDisplayName() #jira UE-39733 Change 3240422 on 2016/12/19 by Dan.Oconnor Remove useless counter. ensureMsgf only fires once Change 3242313 on 2016/12/21 by Phillip.Kavan [UE-35418] The Actor details view will now refresh property mappings for the current Actor instance selection after a bytecode-only recompile of its Blueprint class as a dependency during reinstancing. change summary: - Modified FBlueprintCompileReinstancer::ReinstanceObjects() to invoke SelectActor() on the current editor selection after a bytecode-only recompile of its class as a dependent BP (i.e. when reinstancing/finalization is not actually incurred). This is meant to be consistent with how the refresh for the current Actor selection is handled by the reinstancer in the other cases. #jira UE-35418 Change 3242409 on 2016/12/21 by Dan.Oconnor PR #2995: Arbitrary base logarithm blueprint node (Contributed by Valkrysa) #jira UE-39169 Change 3243207 on 2016/12/22 by Phillip.Kavan [UE-39816] Renaming interface input/output parameters will no longer cause broken pin links at interface function call sites in Blueprints that are currently loaded. change summary: - modified FBasePinChangeHelper::Broadcast() to consider pin changes sourced from an interface Blueprint - also revised non-interface function call site check code a bit to try and avoid doing some unnecessary work when possible, since this code runs through every call site node #jira UE-39816 Change 3243210 on 2016/12/22 by Phillip.Kavan [UE-39944] Extend the GetClassDefaults node to include output pin exceptions for TSet/TMap properties (i.e. mirror safeguards already in place for TArray). change summary: - deprecated 'bExcludeObjectArrays' and replaced with 'bExcludeObjectContainers' (not using a redirect because I need the old property serialized on load for backwards-compatibility) - modified FClassDefaultsOptionalPinManager's ctor to consider both flags - modified FClassDefaultsOptionalPinManager::CanTreatPropertyAsOptional() to also test for TSet/TMap types, and exclude them if they contain a non-class UObject property as the inner type (same as we do for TArray) - modified UK2Node_GetClassDefaults::ValidateNodeDuringCompilation() to also test for TSet/TMap types, and emit a warning for existing pin connections that would have otherwise failed for newly-placed nodes (same as TArray) - modified UK2Node_GetClassDefaults::ExpandNode() to generate the necessary script needed to create a copy of TSet/TMap property value outputs (same as we do for TArray types) #jira UE-39944 Change 3243373 on 2016/12/23 by Maciej.Mroz #jira UE-39794 -Nativized build with EDL enabled use new dependency-gathering system. It shrinks size of the .exe file. This code will change once the boot time EDL is enabled. -In nativized code, ZCOnstructors are not called. They are replaced by ::StaticClass(). Static class doesn't call noting recursively. We still need to fix interfaces (they have no StaticClass). - Workaround for UE-40026 - Various minior improvements. Change 3244038 on 2016/12/28 by Phillip.Kavan [UE-34488] Child Blueprint assets are now marked as dirty if its parent Blueprint incurs a strucutural change. change summary: - modified FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified() to call MarkPackageDirty() after a skeleton-only compile pass on a child Blueprint. notes: - this only fixes the issue for child Blueprints that are loaded when the change to the parent Blueprint occurs. unloaded child Blueprints would still need a redirector to fix this up on load. #jira UE-34488 Change 3244087 on 2016/12/28 by Phillip.Kavan CIS warning fix (C6236). Change 3244388 on 2016/12/30 by Phillip.Kavan [UE-39816] Fix broken pin links caused by renaming interface function input/output parameters prior to compiling the interface, but after renaming the function itself. change summary: - modified FBlueprintEditorUtils::FindFunctionInImplementedInterfaces() to prefer the skeleton class when searching, as that is always up-to-date. - modified FBlueprintEditorUtils::RenameGraph() to update *all* function call site nodes, rather than just those within the current Blueprint class scope. #jira UE-39816 Change 3245322 on 2017/01/03 by Maciej.Mroz #jira UE-40125 Fixed a crash when implementing a native interface in a BP Change 3245667 on 2017/01/03 by Mike.Beach Mirroring CL 3245664 - Making it so level script bound nodes are fixed up on load (in case the level instances were saved out without the bindings). #jira UE-39950 Change 3247675 on 2017/01/05 by Maciej.Mroz BP is not DataOnly, when it overrides an inherited component related to UE-40131 Change 3247985 on 2017/01/05 by Maciej.Mroz NativizationSummary object is always present. #jira UE-40035 Change 3249423 on 2017/01/06 by Mike.Beach Mirroring CL 3248792 from Orion. Fix to keep placeholder classes from being needlessly created (when the object they represent already exists) - instead, attempt to lookup and find the existing import objects (which used to be set, but could be cleared during async loading by FLinkerManager::DissociateImportsAndForcedExports()). #jira OR-34038 Change 3249568 on 2017/01/06 by Mike.Beach Updating the UBlueprintThumbnailRenderer API so it can be sub-classed by plugin/projects. PR #2899: Expose UBlueprintThumbnailRenderer in the API (Contributed by e-agaubatz) #jira UE-38004 Change 3251903 on 2017/01/10 by Phillip.Kavan [UE-31640] Function inputs are now exposed as variable "Get" nodes via the right-click context menu in a Blueprint function graph context. Note: Changes are based on shelved CL# 2452724, original proof-of-concept code (credit: Michael.Noland). #jira UE-31640 #fyi Michael.Noland Change 3252119 on 2017/01/10 by Phillip.Kavan CIS fix (shadowed variable). Change 3252744 on 2017/01/10 by Dan.Oconnor Add GMinimalCompileOnLoad path for postponing compile on load until all data has been loaded - long term this will be used to improve bp compiler infrastructure (performance, correctness, succinctness) Change 3252968 on 2017/01/10 by Phillip.Kavan [UE-36798] Fix for an infinite loop case in the math expression parser. #jira UE-36798 Change 3253153 on 2017/01/10 by Dan.Oconnor Fixed construction scripts not being loaded before instancing occurs when using new lighter compile path Change 3253171 on 2017/01/10 by Mike.Beach Mirrored CL 3253147. Properly fills out FPropertyChangedEvent's MemberProperty field, for the property editor's NotifyHook. As its comment implies, "MemberProperty" is meant to represent the outermost property (in scenarios, like with nested struct properties). It was not working this way, and was instead set to the same nested property. Change 3253220 on 2017/01/10 by Dan.Oconnor These pins should infer together #jira UE-40427 Change 3253223 on 2017/01/10 by Phillip.Kavan [UE-35050] Fix a crash that occurs on an attempt to rename the category of an implemented interface function inherited from a native parent class in the My Blueprint panel. change summary: - modified FBlueprintEditorUtils::FindFunctionInImplementedInterfaces() to accept an additional input parameter so that callers can opt-in to searching all inherited interface classes - modified SMyBlueprint::CanRequestRenameOnActionNode() to prevent rename on implemented interface functions inherited from a native parent class #jira UE-35050 Change 3253259 on 2017/01/10 by Dan.Oconnor Quick cleanup, this stuff is in progress Change 3253983 on 2017/01/11 by Phillip.Kavan [UE-35629] Disable external curve asset creation when editing a local variable's default value in a Blueprint graph. change summary: - modified FCurveStructCustomization::CustomizeChildren() to set the "create" button visibility to "collapsed" when the Owner is NULL (as the default asset path is inferred from the Owner's package). #jira UE-35629 Change 3254024 on 2017/01/11 by Phillip.Kavan [UE-40131] Non-native child BPs can now properly override a nativized parent BP's components in a cooked build with exclusive Blueprint class nativiation. change summary: - added UBlueprintGeneratedClass::CheckAndApplyComponentTemplateOverrides() - switched FComponentKey::OwnerClass type from UBlueprintGeneratedClass to UClass to allow the reference to be loaded when it's a nativized BP class. - modified AActor::PostLoadSubobjects() to apply ICH template overrides to inherited component subobjects on load (only in a cooked build) - modified AActor::PostSpawnInitialize() to also apply ICH template overrides to inherited component subobjects on new Actor spawns (only in a cooked build) - modified FBlueprintEditorUtils::IsDataOnlyBlueprint() to additionally reject BPs that have a non-empty ICH (from Maciej) - added UBlueprint::bHasNativizedParent to cache this as an indicator for cooked builds to allow Actor instances to avoid incurring additional overhead for this at load time in the general case #jira UE-40131 Change 3254176 on 2017/01/11 by Mike.Beach Mirroring CL 3245838 to unblock the Odin build. Downgrading new error to warning until we can address it in content (and fully understand why it is triggering). #jira UE-40470 Change 3254391 on 2017/01/11 by Phillip.Kavan [UE-40131] CIS fix (non-unity). Change 3254442 on 2017/01/11 by Mike.Beach Mirroring CL 3245069 (from RobM), which is described as a "temp fix". #jira UE-40399 Change 3254599 on 2017/01/11 by Mike.Beach Updating some of our GetRedirectPinNames() functions - making local copy of an array element instead of a reference. We're adding to the array right after, and the ref can become invalid (if the array is grown and reallocated). Came to us via UDN. Change 3254624 on 2017/01/11 by Mike.Beach Backing out CL 3247675, as it removed the "data-only" status from certain Blueprints that, IMO, are still considered "data-only". This would have reprecussions to editor (compile-on) load times, and affect how these Blueprints are presented to the user. #fyi Maciej.Mroz, Phillip.Kavan Change3254671on 2017/01/11 by Mike.Beach Resolving CIS warning - USE_EVENT_DRIVEN_ASYNC_LOAD was replaced with GEventDrivenLoaderEnabled. [CL 3255791 by Mike Beach in Main branch]
1305 lines
52 KiB
C++
1305 lines
52 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "BlueprintCompilerCppBackendBase.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "Engine/BlueprintGeneratedClass.h"
|
|
#include "Misc/Paths.h"
|
|
#include "UObject/Interface.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
|
#include "Animation/AnimBlueprintGeneratedClass.h"
|
|
#include "Engine/UserDefinedEnum.h"
|
|
#include "Engine/UserDefinedStruct.h"
|
|
#include "K2Node_Event.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "K2Node_CreateDelegate.h"
|
|
#include "SourceCodeNavigation.h"
|
|
#include "IBlueprintCompilerCppBackendModule.h"
|
|
#include "BlueprintCompilerCppBackendGatherDependencies.h"
|
|
#include "BlueprintCompilerCppBackendUtils.h"
|
|
#include "Kismet2/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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 UniqeSCDelegateIndex = 0;
|
|
for (UFunction* SCDelegateSignature : SCDelegateSignaturesWithoutType)
|
|
{
|
|
FString SCType = FString::Printf(TEXT("F__%s__SC_%d"), *FEmitHelper::GetCppName(SCDelegateSignature), UniqeSCDelegateIndex);
|
|
UniqeSCDelegateIndex++;
|
|
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, FCompilerNativizationOptions NativizationOptions, 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);
|
|
|
|
UClass* OriginalSourceClass = Dependencies.FindOriginalClass(SourceClass);
|
|
ensure(OriginalSourceClass != SourceClass);
|
|
|
|
FNativizationSummaryHelper::RegisterClass(OriginalSourceClass);
|
|
|
|
EmitFileBeginning(CleanCppClassName, EmitterContext);
|
|
|
|
const bool bHasStaticSearchableValues = FBackendHelperStaticSearchableValues::HasSearchableValues(SourceClass);
|
|
|
|
{
|
|
// C4883 is a strange error (for big functions), introduced in VS2015 update 2
|
|
FDisableUnwantedWarningOnScope DisableUnwantedWarningOnScope(EmitterContext.Body);
|
|
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
if (bHasStaticSearchableValues)
|
|
{
|
|
AdditionalMD.Add(FBackendHelperStaticSearchableValues::GenerateClassMetaData(SourceClass));
|
|
}
|
|
|
|
// AdditionalMD.Add(FString::Printf(TEXT("CustomDynamicClassInitialization=\"%s::__CustomDynamicClassInitialization\""), *CppClassName));
|
|
const FString DefinedConfigName = (OriginalSourceClass->ClassConfigName == NAME_None) ? FString() : FString::Printf(TEXT("config=%s, "), *OriginalSourceClass->ClassConfigName.ToString());
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("UCLASS(%s%s%s)")
|
|
, *DefinedConfigName
|
|
, (!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);
|
|
|
|
{
|
|
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
|
|
TSharedPtr<FNativizationSummary> NativizationSummary = BackEndModule.NativizationSummary();
|
|
if (NativizationSummary.IsValid())
|
|
{
|
|
for (TFieldIterator<UProperty> It(SourceClass, EFieldIteratorFlags::ExcludeSuper); It; ++It)
|
|
{
|
|
UProperty* Property = *It;
|
|
if (Property && Property->HasAllPropertyFlags(CPF_Transient | CPF_DuplicateTransient))
|
|
{
|
|
NativizationSummary->MemberVariablesFromGraph++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<FGatherConvertedClassDependencies> ParentDependencies;
|
|
// Emit function declarations and definitions (writes to header and body simultaneously)
|
|
if (!bIsInterface)
|
|
{
|
|
UBlueprintGeneratedClass* BPGC = CastChecked<UBlueprintGeneratedClass>(EmitterContext.GetCurrentlyGeneratedClass());
|
|
UBlueprintGeneratedClass* ParentBPGC = Cast<UBlueprintGeneratedClass>(BPGC->GetSuperClass());
|
|
ParentDependencies = TSharedPtr<FGatherConvertedClassDependencies>(ParentBPGC ? new FGatherConvertedClassDependencies(ParentBPGC) : nullptr);
|
|
|
|
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 __CustomDynamicClassInitialization(UDynamicClass* InDynamicClass);"));
|
|
|
|
EmitterContext.Header.AddLine(TEXT("static void __StaticDependenciesAssets(TArray<FBlueprintDependencyData>& AssetsToLoad);"));
|
|
EmitterContext.Header.AddLine(TEXT("static void __StaticDependencies_DirectlyUsedAssets(TArray<FBlueprintDependencyData>& AssetsToLoad);"));
|
|
if (bHasStaticSearchableValues)
|
|
{
|
|
FBackendHelperStaticSearchableValues::EmitFunctionDeclaration(EmitterContext);
|
|
FBackendHelperStaticSearchableValues::EmitFunctionDefinition(EmitterContext);
|
|
}
|
|
FEmitDefaultValueHelper::GenerateConstructor(EmitterContext);
|
|
FEmitDefaultValueHelper::GenerateCustomDynamicClassInitialization(EmitterContext, ParentDependencies);
|
|
}
|
|
|
|
// 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("};"));
|
|
|
|
if (!bIsInterface)
|
|
{
|
|
// must be called after GenerateConstructor and GenerateCustomDynamicClassInitialization and other functions implementation
|
|
// now we knows which assets are directly used in source code
|
|
FEmitDefaultValueHelper::AddStaticFunctionsForDependencies(EmitterContext, ParentDependencies, NativizationOptions);
|
|
FEmitDefaultValueHelper::AddRegisterHelper(EmitterContext);
|
|
}
|
|
|
|
FEmitHelper::EmitLifetimeReplicatedPropsImpl(EmitterContext);
|
|
}
|
|
CleanBackend();
|
|
|
|
OutCppBody = EmitterContext.Body.Result;
|
|
return EmitterContext.Header.Result;
|
|
}
|
|
|
|
static void PropertiesUsedByStatement(FBlueprintCompiledStatement* Statement, TSet<UProperty*>& Properties)
|
|
{
|
|
if (Statement)
|
|
{
|
|
for (FBPTerminal* Terminal : Statement->RHS)
|
|
{
|
|
if (Terminal)
|
|
{
|
|
Properties.Add(Terminal->AssociatedVarProperty);
|
|
PropertiesUsedByStatement(Terminal->InlineGeneratedParameter, Properties);
|
|
}
|
|
}
|
|
|
|
if (Statement->FunctionContext)
|
|
{
|
|
Properties.Add(Statement->FunctionContext->AssociatedVarProperty);
|
|
PropertiesUsedByStatement(Statement->FunctionContext->InlineGeneratedParameter, Properties);
|
|
}
|
|
|
|
if (Statement->LHS)
|
|
{
|
|
Properties.Add(Statement->LHS->AssociatedVarProperty);
|
|
PropertiesUsedByStatement(Statement->LHS->InlineGeneratedParameter, Properties);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Emits local variable declarations for a function */
|
|
static void DeclareLocalVariables(FEmitterLocalContext& EmitterContext, TArray<UProperty*>& LocalVariables, FKismetFunctionContext& FunctionContext, int32 ExecutionGroup)
|
|
{
|
|
const bool bUseExecutionGroup = ExecutionGroup >= 0;
|
|
TSet<UProperty*> PropertiesUsedByCurrentExecutionGroup;
|
|
if (bUseExecutionGroup)
|
|
{
|
|
for (UEdGraphNode* Node : FunctionContext.UnsortedSeparateExecutionGroups[ExecutionGroup])
|
|
{
|
|
TArray<FBlueprintCompiledStatement*>* StatementList = FunctionContext.StatementsPerNode.Find(Node);
|
|
if (StatementList)
|
|
{
|
|
for (FBlueprintCompiledStatement* Statement : *StatementList)
|
|
{
|
|
PropertiesUsedByStatement(Statement, PropertiesUsedByCurrentExecutionGroup);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int32 i = 0; i < LocalVariables.Num(); ++i)
|
|
{
|
|
UProperty* LocalVariable = LocalVariables[i];
|
|
if (!bUseExecutionGroup || PropertiesUsedByCurrentExecutionGroup.Contains(LocalVariable))
|
|
{
|
|
const FString CppDeclaration = EmitterContext.ExportCppDeclaration(LocalVariable, EExportedDeclaration::Local, EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend | EPropertyExportCPPFlags::CPPF_NoConst);
|
|
UStructProperty* StructProperty = Cast<UStructProperty>(LocalVariable);
|
|
const TCHAR* EmptyDefaultConstructor = FEmitHelper::EmptyDefaultConstructor(StructProperty ? StructProperty->Struct : nullptr);
|
|
EmitterContext.AddLine(CppDeclaration + EmptyDefaultConstructor + 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);
|
|
const bool bUseInnerFunctionImplementation = bIsConstFunction && !FunctionContext.Function->HasAnyFunctionFlags(FUNC_Static);
|
|
if (bUseInnerFunctionImplementation)
|
|
{
|
|
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 (!bUseInnerFunctionImplementation)
|
|
{
|
|
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 | EPropertyExportCPPFlags::CPPF_NoRef;
|
|
const FString NoConstNoRefType = EmitterContext.ExportCppDeclaration(Property, EExportedDeclaration::Parameter, ExportFlags, FEmitterLocalContext::EPropertyNameInDeclaration::Skip);
|
|
const FString TypeDefName = FString(TEXT("T")) + EmitterContext.GenerateUniqueLocalName();
|
|
EmitterContext.AddLine(FString::Printf(TEXT("typedef %s %s;"), *NoConstNoRefType, *TypeDefName));
|
|
|
|
const FString ParamName = FEmitHelper::GetCppName(Property);
|
|
EmitterContext.AddLine(FString::Printf(TEXT("%s& %s = *const_cast<%s *>(&%s__const);"), *TypeDefName, *ParamName, *TypeDefName, *ParamName));
|
|
}
|
|
}
|
|
const int32 ExecutionGroup = bManyExecutionGroups ? ExecutionGroupIndex : -1;
|
|
DeclareLocalVariables(EmitterContext, LocalVariables, FunctionContext, ExecutionGroup);
|
|
ConstructFunctionBody(EmitterContext, FunctionContext, ExecutionGroup);
|
|
}
|
|
|
|
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
|
|
, FEmitterLocalContext::EPropertyNameInDeclaration::Regular
|
|
, 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, FEmitterLocalContext::EPropertyNameInDeclaration::Skip);
|
|
}
|
|
return TEXT("void");
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::ConstructFunctionBody(FEmitterLocalContext& EmitterContext, FKismetFunctionContext &FunctionContext, int32 ExecutionGroup)
|
|
{
|
|
if (FunctionContext.UnsortedSeparateExecutionGroups.Num() && (ExecutionGroup < 0))
|
|
{
|
|
// use 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bIsFunctionNotReducible = InnerFunctionImplementation(FunctionContext, EmitterContext, ExecutionGroup);
|
|
if (!bIsFunctionNotReducible)
|
|
{
|
|
FNativizationSummaryHelper::ReducibleFunciton(EmitterContext.Dependencies.FindOriginalClass(EmitterContext.GetCurrentlyGeneratedClass()));
|
|
}
|
|
}
|
|
|
|
void FBlueprintCompilerCppBackendBase::GenerateCodeFromEnum(UUserDefinedEnum* SourceEnum, FString& OutHeaderCode, FString& OutCPPCode)
|
|
{
|
|
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 int64 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 ElemCppName = EnumItemName(Index);
|
|
const int64 ElemValue = SourceEnum->GetValueByIndex(Index);
|
|
|
|
const FString& DisplayNameMD = SourceEnum->GetMetaData(TEXT("DisplayName"), ElemValue);// TODO: value or index?
|
|
const FString MetaDisplayName = DisplayNameMD.IsEmpty() ? FString() : FString::Printf(TEXT("DisplayName = \"%s\","), *DisplayNameMD.ReplaceCharWithEscapedChar());
|
|
const FString MetaOverrideName = FString::Printf(TEXT("OverrideName = \"%s\""), *SourceEnum->GetNameByIndex(Index).ToString());
|
|
Header.AddLine(FString::Printf(TEXT("%s = %lld UMETA(%s%s),"), *ElemCppName, ElemValue, *MetaDisplayName, *MetaOverrideName));
|
|
}
|
|
|
|
Header.DecreaseIndent();
|
|
Header.AddLine(TEXT("};"));
|
|
|
|
Header.AddLine(FString::Printf(TEXT("FText %s__GetUserFriendlyName(int32 InValue);"), *EnumCppName));
|
|
|
|
OutHeaderCode = MoveTemp(Header.Result);
|
|
|
|
FCodeText Body;
|
|
|
|
const FString PCHFilename = FEmitHelper::GetPCHFilename();
|
|
if (!PCHFilename.IsEmpty())
|
|
{
|
|
Body.AddLine(FString::Printf(TEXT("#include \"%s\""), *PCHFilename));
|
|
}
|
|
else
|
|
{
|
|
// Used when generated code is not in a separate module
|
|
const FString MainHeaderFilename = FEmitHelper::GetGameMainHeaderFilename();
|
|
if (!MainHeaderFilename.IsEmpty())
|
|
{
|
|
Body.AddLine(FString::Printf(TEXT("#include \"%s\""), *MainHeaderFilename));
|
|
}
|
|
}
|
|
|
|
Body.AddLine(FString::Printf(TEXT("#include \"%s.h\""), *FEmitHelper::GetBaseFilename(SourceEnum)));
|
|
|
|
// generate implementation of GetUserFriendlyName:
|
|
Body.AddLine(FString::Printf(TEXT("FText %s__GetUserFriendlyName(int32 InValue)"), *EnumCppName, *EnumCppName));
|
|
Body.AddLine(TEXT("{"));
|
|
Body.IncreaseIndent();
|
|
|
|
Body.AddLine(TEXT("FText Text;"));
|
|
Body.AddLine(FString::Printf(TEXT("const auto EnumValue = static_cast<%s>(InValue);"), *EnumCppName));
|
|
Body.AddLine(TEXT("switch(EnumValue)"));
|
|
Body.AddLine(TEXT("{"));
|
|
Body.IncreaseIndent();
|
|
for (int32 Index = 0; Index < SourceEnum->NumEnums(); ++Index)
|
|
{
|
|
const FString ElemName = EnumItemName(Index);
|
|
FString DisplayNameStr;
|
|
FTextStringHelper::WriteToString(DisplayNameStr, SourceEnum->GetEnumText(Index));
|
|
Body.AddLine(FString::Printf(TEXT("case %s::%s: FTextStringHelper::%s(TEXT(\"%s\"), Text); break;")
|
|
, *EnumCppName, *ElemName
|
|
, GET_FUNCTION_NAME_STRING_CHECKED(FTextStringHelper, ReadFromString)
|
|
, *DisplayNameStr.ReplaceCharWithEscapedChar()));
|
|
}
|
|
|
|
Body.AddLine(TEXT("default: ensure(false);"));
|
|
Body.DecreaseIndent();
|
|
Body.AddLine(TEXT("};"));
|
|
|
|
Body.AddLine(TEXT("return Text;"));
|
|
Body.DecreaseIndent();
|
|
Body.AddLine(TEXT("};"));
|
|
|
|
OutCPPCode = MoveTemp(Body.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);
|
|
|
|
const FString CppStructName = FEmitHelper::GetCppName(SourceStruct);
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("USTRUCT(BlueprintType, %s)"), *FEmitHelper::ReplaceConvertedMetaData(SourceStruct)));
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("struct %s"), *CppStructName));
|
|
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.AddLine(FString::Printf(TEXT("bool operator== (const %s& __Other) const"), *CppStructName));
|
|
EmitterContext.Header.AddLine(TEXT("{"));
|
|
EmitterContext.Header.IncreaseIndent();
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("return %s::StaticStruct()->%s(this, &__Other, 0);"), *CppStructName, GET_FUNCTION_NAME_STRING_CHECKED(UScriptStruct, CompareScriptStruct)));
|
|
EmitterContext.Header.DecreaseIndent();
|
|
EmitterContext.Header.AddLine(TEXT("};"));
|
|
|
|
// Provide GetTypeHash if the struct is hashable:
|
|
if( FBlueprintEditorUtils::StructHasGetTypeHash( SourceStruct ) )
|
|
{
|
|
EmitterContext.Header.AddLine(FString::Printf(TEXT("friend uint32 GetTypeHash(const %s& __Other) { return UUserDefinedStruct::GetUserDefinedStructTypeHash( &__Other, %s::StaticStruct()); }"), *CppStructName, *CppStructName));
|
|
}
|
|
|
|
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 | EPropertyExportCPPFlags::CPPF_NoConst
|
|
, FEmitterLocalContext::EPropertyNameInDeclaration::Skip);
|
|
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()->%s(FName(TEXT(\"%s\")));"), GET_FUNCTION_NAME_STRING_CHECKED(UClass, FindPropertyByName), *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()->%s(FName(TEXT(\"%s\")));"), GET_FUNCTION_NAME_STRING_CHECKED(UClass, FindPropertyByName), *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
|
|
, FEmitterLocalContext::EPropertyNameInDeclaration::Regular
|
|
, 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->%s(%s);"), GET_FUNCTION_NAME_STRING_CHECKED(UObject, FindFunctionChecked), *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->%s(__Function, %s);"), GET_FUNCTION_NAME_STRING_CHECKED(UObject, ProcessEvent), 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->GetPathName();
|
|
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();
|
|
}
|