Files
UnrealEngineUWP/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.h
marc audy 4320117e97 Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework [at] 4343008)
#lockdown Nick.Penwarden
#rb
#rnx

============================
  MAJOR FEATURES & CHANGES
============================

Change 4289396 by Marc.Audy

	Fix get data table row result pin not properly updating if it was split.
	#jira UE-62896

Change 4289407 by Marc.Audy

	Don't allow set member input pin to be split at all
	#jira UE-62924

Change 4291871 by Mieszko.Zielinski

	Exporting EnvQueryGenerator_SimpleGrid with AIMODULE_API #UE4

	#jira UE-62697

Change 4292078 by Marc.Audy

	Fix GetDataTableRow title not updating when changing type

	#jira UE-62896

Change 4292770 by Dan.Oconnor

	Interpreter Optimization 7 of N: CPF_ZeroConstructor assigned for struct properties that that are User Defined Structs

Change 4293207 by Dan.Oconnor

	Interpreter Optimization 8 of N: CPF_IsPlainOldData and CPF_NoDestructor assigned for struct properties that that are User Defined Structs

Change 4297023 by Marc.Audy

	Remove dumb returns

Change 4298303 by Dan.Oconnor

	Fix for issues compiling blueprints after changing a UserDefinedStructure

	#jira UE-63002

Change 4301603 by Phillip.Kavan

	Fix incorrect code generation at cook time when Blueprint nativization is enabled for objects instanced through the editor and assigned to an instanced object reference property.

	Change summary:
	- Modified FEmitDefaultValueHelper::HandleSpecialTypes() to emit runtime code to look for an existing instance of a default subobject only if the 'RF_DefaultSubObject' flag is set on the source instance. This works around instances that return 'true' for IsDefaultSubobject() but were not created with CreateDefaultSubobject() (e.g. - instanced in the editor UI).

	#jira UE-62883

Change 4302720 by Dan.Oconnor

	Prevent user from entering 'None' as parameter name on a blueprint function

	#jira UE-63033

Change 4303695 by Fred.Kimberley

	Don't allow the user to attempt to import a csv unless they've specified the rowstruct for datatables or the interpolation for curve tables.

	#jira UE-62856

Change 4304633 by Ben.Zeigler

	#jira UE-63173 Fix crash ending PIE while a level is waiting to load, SetShouldForceUnloadStreamingLevels now actually force unloads levels

Change 4313208 by Dan.Oconnor

	Warn users when the outputs of a node that is pruned are used

	#jira UE-44518

Change 4313339 by Dan.Oconnor

	Interpreter Optimization 9 of N: Mark ubergraph function as final so that calls to it don't need to search the target object

Change 4313652 by Dan.Oconnor

	Don't warn when a non-pure node that is connected only via its exec is orphaned - fixes overzealous warnings EngineTest

	#jira None

Change 4313661 by Dan.Oconnor

	Comment typo fixes

Change 4313729 by Dan.Oconnor

	Avoid crashing when running the help command for the GenerateBlueprintAPICommandlet

	#jira UE-63283

Change 4318175 by Marc.Audy

	PR #5024: Require BlueprintAssignable delegate parameters to be supported by blueprints (Contributed by slonopotamus)
	#jira UE-635250

Change 4318302 by Marc.Audy

	Use define instead of duplicating text

Change 4318681 by Fred.Kimberley

	Don't allow users to try to rename rows to emtpy strings, whitespace only, or names that have invalid characters.

	#jira UE-63058

Change 4318683 by Fred.Kimberley

	Don't reorder the rowmap when removing a row.

	#jira UE-62754

Change 4318686 by Fred.Kimberley

	Get rid of the failed to import message when cancelling the import of a csv or json file.

	#jira UE-62625

Change 4318689 by Fred.Kimberley

	Back out changelist 4281972

Change 4318713 by Marc.Audy

	Validate CommutativeAssociativeBinaryOperator metadata in UHT and also at the K2Node level in case the function changes to an invalid construct later
	#jira UE-60186

Change 4319363 by Dan.Oconnor

	Warnings and errors generated by macro expansion now include a link back to the macro instance that created the suspect nodes

	#jira None

Change 4319559 by Dan.Oconnor

	PR #5028: UE-63225: Use correct source BP when retrieving the tooltip text (Contributed by projectgheist)

	#jira UE-63225

Change 4323740 by Marc.Audy

	Don't crash if an ICH is (somewhat surprisingly) missing
	#jira UE-63305

Change 4328877 by mason.seay

	BP for pin bug

Change 4336999 by Fred.Kimberley

	Unshelved from pending changelist '4299645':

	PR #5003: Changed SendGameplayEvent()'s category from "Abilities" to "Ability" for consistency (Contributed by rlabrecque)


	#jira UE-63067

Change 4337645 by Marc.Audy

	PR #5049: Make GetPlayerControllerID pure (Contributed by KristofMorva)
	#jira UE-63472

Change 4338211 by Phillip.Kavan

	Fix missing backend support for initializing a container variable (including unconnected function output pins) to a default (literal) value term in nativized Blueprint C++ code.

	Change summary:
	- Added a 'CoerceProperty' argument to FBlueprintCompilerCppBackEnd::TermToText() and FEmitHelper::LiteralTerm(). This is passed through as the LHS term's associated property when compiling 'KCST_Assignment' statements.
	- Modified FEmitHelper::LiteralTerm() to emit an initialization list to C++. This occurs when the RHS of a variable assignment statement is a literal container term, which includes the initialization of K2Node_FunctionResult pins to a default (empty) value.
	- Added FBlueprintCompilerCppBackEnd::TermToText() and FEmitHelper::LiteralTerm() methods that accept a struct value for specifying arguments rather than increasing the number of parameters with default values. Modified the implementations to match.

	#jira UE-48397

Change 4338908 by Marc.Audy

	Don't check when fetching world settings from IsPaused as it is already set up to handle it coming back null
	#jira UE-63583

Change 4339262 by Marc.Audy

	Add comments to ERedirectType

Change 4339718 by Mieszko.Zielinski

	Clearing editor world's EnvironmentQueryManager instace's LocalContextMap whenever a BP finished compiling #UE4

	#jira UE-63481

Change 4340548 by Marc.Audy

	ChildActor's need to be spawned from the ChildActorTemplate otherwise any non-replicated  customized properties will be incorrect on the Client.
	#jira UE-63427

Change 4340756 by Marc.Audy

	Don't allow a new pin to be matched twice
	#jira UE-63504

Change 4342704 by mason.seay

	Updated Pin Testing blueprints

Change 4319930 by Marc.Audy

	Mark FPartyState as a BlueprintType

Change 4329176 by Dan.Oconnor

	Add optional message identifier to tokenized message, use message identifier to facilitate suppressing a new warning in the blueprint compiler, remove the unused (and unusable) FTokenizedMiscData

	#jira None

Change 4337859 by Fred.Kimberley

	Fixing misc restricted gameplay tag UI bugs

	#jira UE-63600, UE-63602, UE-63603

#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-SOURCE: CL 4344176 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 4344180 by marc audy in Dev-VR branch]
2018-09-05 09:57:16 -04:00

1890 lines
53 KiB
C++

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ErrorException.h"
#include "UObject/Package.h"
#include "UObject/MetaData.h"
#include "UObject/TextProperty.h"
#include "UObject/EnumProperty.h"
#include "UnrealHeaderToolGlobals.h"
#include "ClassMaps.h"
#include "Templates/UniqueObj.h"
#include "Templates/UniquePtr.h"
class UEnum;
class UScriptStruct;
class UProperty;
class FUnrealSourceFile;
class UObject;
class UField;
class UMetaData;
class FHeaderParser;
extern class FCompilerMetadataManager GScriptHelper;
/*-----------------------------------------------------------------------------
FPropertyBase.
-----------------------------------------------------------------------------*/
enum EFunctionExportFlags
{
FUNCEXPORT_Final =0x00000001, // function declaration included "final" keyword. Used to differentiate between functions that have FUNC_Final only because they're private
// =0x00000002,
// =0x00000004,
FUNCEXPORT_RequiredAPI =0x00000008, // Function should be exported as a public API function
FUNCEXPORT_Inline =0x00000010, // export as an inline static C++ function
FUNCEXPORT_CppStatic =0x00000020, // Export as a real C++ static function, causing thunks to call via ClassName::FuncName instead of this->FuncName
FUNCEXPORT_CustomThunk =0x00000040, // Export no thunk function; the user will manually define a custom one
// =0x00000080,
// =0x00000100,
};
enum EPropertyHeaderExportFlags
{
PROPEXPORT_Public =0x00000001, // property should be exported as public
PROPEXPORT_Private =0x00000002, // property should be exported as private
PROPEXPORT_Protected =0x00000004, // property should be exported as protected
};
struct EPointerType
{
enum Type
{
None,
Native
};
};
struct EArrayType
{
enum Type
{
None,
Static,
Dynamic,
Set
};
};
struct ERefQualifier
{
enum Type
{
None,
ConstRef,
NonConstRef
};
};
enum class EIntType
{
None,
Sized, // e.g. int32, int16
Unsized // e.g. int, unsigned int
};
#ifndef CASE_TEXT
#define CASE_TEXT(txt) case txt: return TEXT(#txt)
#endif
/**
* Basic information describing a type.
*/
class FPropertyBase : public TSharedFromThis<FPropertyBase>
{
public:
// Variables.
EPropertyType Type;
EArrayType::Type ArrayType;
EPropertyFlags PropertyFlags;
EPropertyFlags ImpliedPropertyFlags;
ERefQualifier::Type RefQualifier; // This is needed because of legacy stuff - FString mangles the flags for reasons that have become lost in time but we need this info for testing for invalid replicated function signatures.
TSharedPtr<FPropertyBase> MapKeyProp;
/**
* A mask of EPropertyHeaderExportFlags which are used for modifying how this property is exported to the native class header
*/
uint32 PropertyExportFlags;
union
{
class UEnum* Enum;
class UClass* PropertyClass;
class UScriptStruct* Struct;
class UFunction* Function;
#if PLATFORM_64BITS
int64 StringSize;
#else
int32 StringSize;
#endif
};
UClass* MetaClass;
FName DelegateName;
UClass* DelegateSignatureOwnerClass;
FName RepNotifyName;
/** Raw string (not type-checked) used for specifying special text when exporting a property to the *Classes.h file */
FString ExportInfo;
/** Map of key value pairs that will be added to the package's UMetaData for this property */
TMap<FName, FString> MetaData;
EPointerType::Type PointerType;
EIntType IntType;
public:
/** @name Constructors */
//@{
explicit FPropertyBase(EPropertyType InType)
: Type (InType)
, ArrayType (EArrayType::None)
, PropertyFlags (CPF_None)
, ImpliedPropertyFlags(CPF_None)
, RefQualifier (ERefQualifier::None)
, PropertyExportFlags (PROPEXPORT_Public)
, StringSize (0)
, MetaClass (nullptr)
, DelegateName (NAME_None)
, DelegateSignatureOwnerClass(nullptr)
, RepNotifyName (NAME_None)
, PointerType (EPointerType::None)
, IntType (GetSizedIntTypeFromPropertyType(InType))
{
}
explicit FPropertyBase(EPropertyType InType, EIntType InIntType)
: Type (InType)
, ArrayType (EArrayType::None)
, PropertyFlags (CPF_None)
, ImpliedPropertyFlags(CPF_None)
, RefQualifier (ERefQualifier::None)
, PropertyExportFlags (PROPEXPORT_Public)
, StringSize (0)
, MetaClass (nullptr)
, DelegateName (NAME_None)
, DelegateSignatureOwnerClass(nullptr)
, RepNotifyName (NAME_None)
, PointerType (EPointerType::None)
, IntType (InIntType)
{
}
explicit FPropertyBase(UEnum* InEnum, EPropertyType InType)
: Type (InType)
, ArrayType (EArrayType::None)
, PropertyFlags (CPF_None)
, ImpliedPropertyFlags(CPF_None)
, RefQualifier (ERefQualifier::None)
, PropertyExportFlags (PROPEXPORT_Public)
, Enum (InEnum)
, MetaClass (nullptr)
, DelegateName (NAME_None)
, DelegateSignatureOwnerClass(nullptr)
, RepNotifyName (NAME_None)
, PointerType (EPointerType::None)
, IntType (GetSizedIntTypeFromPropertyType(InType))
{
}
explicit FPropertyBase(UClass* InClass, bool bIsWeak = false, bool bWeakIsAuto = false, bool bIsLazy = false, bool bIsSoft = false)
: Type (CPT_ObjectReference)
, ArrayType (EArrayType::None)
, PropertyFlags (CPF_None)
, ImpliedPropertyFlags(CPF_None)
, RefQualifier (ERefQualifier::None)
, PropertyExportFlags (PROPEXPORT_Public)
, PropertyClass (InClass)
, MetaClass (nullptr)
, DelegateName (NAME_None)
, DelegateSignatureOwnerClass(nullptr)
, RepNotifyName (NAME_None)
, PointerType (EPointerType::None)
, IntType (EIntType::None)
{
// if this is an interface class, we use the UInterfaceProperty class instead of UObjectProperty
if ( InClass->HasAnyClassFlags(CLASS_Interface) )
{
Type = CPT_Interface;
}
if (bIsLazy)
{
Type = CPT_LazyObjectReference;
}
else if (bIsSoft)
{
Type = CPT_SoftObjectReference;
}
else if (bIsWeak)
{
Type = CPT_WeakObjectReference;
if (bWeakIsAuto)
{
PropertyFlags |= CPF_AutoWeak;
}
}
}
explicit FPropertyBase(UScriptStruct* InStruct)
: Type (CPT_Struct)
, ArrayType (EArrayType::None)
, PropertyFlags (CPF_None)
, ImpliedPropertyFlags(CPF_None)
, RefQualifier (ERefQualifier::None)
, PropertyExportFlags (PROPEXPORT_Public)
, Struct (InStruct)
, MetaClass (NULL)
, DelegateName (NAME_None)
, DelegateSignatureOwnerClass(nullptr)
, RepNotifyName (NAME_None)
, PointerType (EPointerType::None)
, IntType (EIntType::None)
{
}
explicit FPropertyBase(UProperty* Property)
: PropertyExportFlags(PROPEXPORT_Public)
, DelegateName (NAME_None)
, DelegateSignatureOwnerClass(nullptr)
, RepNotifyName (NAME_None)
, IntType (EIntType::None)
{
checkSlow(Property);
EArrayType::Type ArrType = EArrayType::None;
EPropertyFlags PropagateFlags = CPF_None;
UClass* ClassOfProperty = Property->GetClass();
if( ClassOfProperty==UArrayProperty::StaticClass() )
{
ArrType = EArrayType::Dynamic;
// if we're an array, save up Parm flags so we can propagate them.
// below the array will be assigned the inner property flags. This allows propagation of Parm flags (out, optional..)
PropagateFlags = Property->PropertyFlags & CPF_ParmFlags;
Property = CastChecked<UArrayProperty>(Property)->Inner;
ClassOfProperty = Property->GetClass();
}
if( ClassOfProperty==UByteProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Byte);
Enum = Cast<UByteProperty>(Property)->Enum;
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UEnumProperty::StaticClass() )
{
UEnumProperty* EnumProp = Cast<UEnumProperty>(Property);
UNumericProperty* UnderlyingProp = EnumProp->GetUnderlyingProperty();
*this = FPropertyBase(
UnderlyingProp->IsA<UInt8Property>() ? CPT_Int8
: UnderlyingProp->IsA<UInt16Property>() ? CPT_Int16
: UnderlyingProp->IsA<UIntProperty>() ? CPT_Int
: UnderlyingProp->IsA<UInt64Property>() ? CPT_Int64
: UnderlyingProp->IsA<UByteProperty>() ? CPT_Byte
: UnderlyingProp->IsA<UUInt16Property>() ? CPT_UInt16
: UnderlyingProp->IsA<UUInt32Property>() ? CPT_UInt32
: UnderlyingProp->IsA<UUInt64Property>() ? CPT_UInt64
: CPT_None
);
check(this->Type != CPT_None);
Enum = EnumProp->Enum;
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UInt8Property::StaticClass() )
{
*this = FPropertyBase(CPT_Int8);
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UInt16Property::StaticClass() )
{
*this = FPropertyBase(CPT_Int16);
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UIntProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Int);
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UInt64Property::StaticClass() )
{
*this = FPropertyBase(CPT_Int64);
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UUInt16Property::StaticClass() )
{
*this = FPropertyBase(CPT_UInt16);
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UUInt32Property::StaticClass() )
{
*this = FPropertyBase(CPT_UInt32);
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UUInt64Property::StaticClass() )
{
*this = FPropertyBase(CPT_UInt64);
IntType = EIntType::Sized;
}
else if( ClassOfProperty==UBoolProperty::StaticClass() )
{
UBoolProperty* BoolProperty = Cast<UBoolProperty>(Property);
if (BoolProperty->IsNativeBool())
{
*this = FPropertyBase(CPT_Bool);
}
else
{
switch( BoolProperty->ElementSize )
{
case sizeof(uint8):
*this = FPropertyBase(CPT_Bool8);
break;
case sizeof(uint16):
*this = FPropertyBase(CPT_Bool16);
break;
case sizeof(uint32):
*this = FPropertyBase(CPT_Bool32);
break;
case sizeof(uint64):
*this = FPropertyBase(CPT_Bool64);
break;
}
}
}
else if( ClassOfProperty==UFloatProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Float);
}
else if( ClassOfProperty==UDoubleProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Double);
}
else if( ClassOfProperty==UClassProperty::StaticClass() )
{
*this = FPropertyBase(CPT_ObjectReference);
PropertyClass = Cast<UClassProperty>(Property)->PropertyClass;
MetaClass = Cast<UClassProperty>(Property)->MetaClass;
}
else if( ClassOfProperty==UObjectProperty::StaticClass() )
{
*this = FPropertyBase(CPT_ObjectReference);
PropertyClass = Cast<UObjectProperty>(Property)->PropertyClass;
}
else if( ClassOfProperty==UWeakObjectProperty::StaticClass() )
{
*this = FPropertyBase(CPT_WeakObjectReference);
PropertyClass = Cast<UWeakObjectProperty>(Property)->PropertyClass;
}
else if( ClassOfProperty==ULazyObjectProperty::StaticClass() )
{
*this = FPropertyBase(CPT_LazyObjectReference);
PropertyClass = Cast<ULazyObjectProperty>(Property)->PropertyClass;
}
else if( ClassOfProperty==USoftClassProperty::StaticClass() )
{
*this = FPropertyBase(CPT_SoftObjectReference);
PropertyClass = Cast<USoftClassProperty>(Property)->PropertyClass;
MetaClass = Cast<USoftClassProperty>(Property)->MetaClass;
}
else if( ClassOfProperty==USoftObjectProperty::StaticClass() )
{
*this = FPropertyBase(CPT_SoftObjectReference);
PropertyClass = Cast<USoftObjectProperty>(Property)->PropertyClass;
}
else if( ClassOfProperty==UNameProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Name);
}
else if( ClassOfProperty==UStrProperty::StaticClass() )
{
*this = FPropertyBase(CPT_String);
}
else if( ClassOfProperty==UTextProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Text);
}
else if( ClassOfProperty==UStructProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Struct);
Struct = Cast<UStructProperty>(Property)->Struct;
}
else if( ClassOfProperty==UDelegateProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Delegate);
Function = Cast<UDelegateProperty>(Property)->SignatureFunction;
}
else if( ClassOfProperty==UMulticastDelegateProperty::StaticClass() )
{
*this = FPropertyBase(CPT_MulticastDelegate);
// @todo delegate: Any other setup for calling multi-cast delegates from script needed?
Function = Cast<UMulticastDelegateProperty>(Property)->SignatureFunction;
}
else if ( ClassOfProperty==UInterfaceProperty::StaticClass() )
{
*this = FPropertyBase(CPT_Interface);
PropertyClass = Cast<UInterfaceProperty>(Property)->InterfaceClass;
}
else
{
UE_LOG(LogCompile, Fatal, TEXT("Unknown property type '%s'"), *Property->GetFullName() );
}
ArrayType = ArrType;
PropertyFlags = Property->PropertyFlags | PropagateFlags;
ImpliedPropertyFlags = CPF_None;
RefQualifier = ERefQualifier::None;
PointerType = EPointerType::None;
}
//@}
/** @name Functions */
//@{
/**
* Returns whether this token represents an object reference
*/
bool IsObject() const
{
return Type == CPT_ObjectReference || Type == CPT_Interface || Type == CPT_WeakObjectReference || Type == CPT_LazyObjectReference || Type == CPT_SoftObjectReference;
}
bool IsContainer() const
{
return (ArrayType != EArrayType::None || MapKeyProp.IsValid());
}
/**
* Determines whether this token's type is compatible with another token's type.
*
* @param Other the token to check against this one.
* Given the following example expressions, VarA is Other and VarB is 'this':
* VarA = VarB;
*
* function func(type VarB) {}
* func(VarA);
*
* static operator==(type VarB_1, type VarB_2) {}
* if ( VarA_1 == VarA_2 ) {}
*
* @param bDisallowGeneralization controls whether it should be considered a match if this token's type is a generalization
* of the other token's type (or vice versa, when dealing with structs
* @param bIgnoreImplementedInterfaces controls whether two types can be considered a match if one type is an interface implemented
* by the other type.
*/
bool MatchesType( const FPropertyBase& Other, bool bDisallowGeneralization, bool bIgnoreImplementedInterfaces=false ) const
{
check(Type!=CPT_None || !bDisallowGeneralization);
bool bIsObjectType = IsObject();
bool bOtherIsObjectType = Other.IsObject();
bool bIsObjectComparison = bIsObjectType && bOtherIsObjectType;
bool bReverseClassChainCheck = true;
// If converting to an l-value, we require an exact match with an l-value.
if( (PropertyFlags&CPF_OutParm) != 0 )
{
// if the other type is not an l-value, disallow
if ( (Other.PropertyFlags&CPF_OutParm) == 0 )
{
return false;
}
// if the other type is const and we are not const, disallow
if ( (Other.PropertyFlags&CPF_ConstParm) != 0 && (PropertyFlags&CPF_ConstParm) == 0 )
{
return false;
}
if ( Type == CPT_Struct )
{
// Allow derived structs to be passed by reference, unless this is a dynamic array of structs
bDisallowGeneralization = bDisallowGeneralization || ArrayType == EArrayType::Dynamic || Other.ArrayType == EArrayType::Dynamic;
}
// if Type == CPT_ObjectReference, out object function parm; allow derived classes to be passed in
// if Type == CPT_Interface, out interface function parm; allow derived classes to be passed in
else if ( (PropertyFlags & CPF_ConstParm) == 0 || !IsObject() )
{
// all other variable types must match exactly when passed as the value to an 'out' parameter
bDisallowGeneralization = true;
}
// both types are objects, but one is an interface and one is an object reference
else if ( bIsObjectComparison && Type != Other.Type )
{
return false;
}
}
else if ((Type == CPT_ObjectReference || Type == CPT_WeakObjectReference || Type == CPT_LazyObjectReference || Type == CPT_SoftObjectReference) && Other.Type != CPT_Interface && (PropertyFlags & CPF_ReturnParm))
{
bReverseClassChainCheck = false;
}
// Check everything.
if( Type==CPT_None && (Other.Type==CPT_None || !bDisallowGeneralization) )
{
// If Other has no type, accept anything.
return true;
}
else if( Type != Other.Type && !bIsObjectComparison )
{
// Mismatched base types.
return false;
}
else if( ArrayType != Other.ArrayType )
{
// Mismatched array types.
return false;
}
else if( Type==CPT_Byte )
{
// Make sure enums match, or we're generalizing.
return Enum==Other.Enum || (Enum==NULL && !bDisallowGeneralization);
}
else if( bIsObjectType )
{
check(PropertyClass!=NULL);
// Make sure object types match, or we're generalizing.
if( bDisallowGeneralization )
{
// Exact match required.
return PropertyClass==Other.PropertyClass && MetaClass==Other.MetaClass;
}
else if( Other.PropertyClass==NULL )
{
// Cannonical 'None' matches all object classes.
return true;
}
else
{
// Generalization is ok (typical example of this check would look like: VarA = VarB;, where this is VarB and Other is VarA)
if ( Other.PropertyClass->IsChildOf(PropertyClass) )
{
if ( !bIgnoreImplementedInterfaces || ((Type == CPT_Interface) == (Other.Type == CPT_Interface)) )
{
if ( !PropertyClass->IsChildOf(UClass::StaticClass()) || MetaClass == NULL || Other.MetaClass->IsChildOf(MetaClass) ||
(bReverseClassChainCheck && (Other.MetaClass == NULL || MetaClass->IsChildOf(Other.MetaClass))) )
{
return true;
}
}
}
// check the opposite class chain for object types
else if (bReverseClassChainCheck && Type != CPT_Interface && bIsObjectComparison && PropertyClass != NULL && PropertyClass->IsChildOf(Other.PropertyClass))
{
if (!Other.PropertyClass->IsChildOf(UClass::StaticClass()) || MetaClass == NULL || Other.MetaClass == NULL || MetaClass->IsChildOf(Other.MetaClass) || Other.MetaClass->IsChildOf(MetaClass))
{
return true;
}
}
if ( PropertyClass->HasAnyClassFlags(CLASS_Interface) && !bIgnoreImplementedInterfaces )
{
if ( Other.PropertyClass->ImplementsInterface(PropertyClass) )
{
return true;
}
}
return false;
}
}
else if( Type==CPT_Struct )
{
check(Struct!=NULL);
check(Other.Struct!=NULL);
if ( Struct == Other.Struct )
{
// struct types match exactly
return true;
}
// returning false here prevents structs related through inheritance from being used interchangeably, such as passing a derived struct as the value for a parameter
// that expects the base struct, or vice versa. An easier example is assignment (e.g. Vector = Plane or Plane = Vector).
// there are two cases to consider (let's use vector and plane for the example):
// - Vector = Plane;
// in this expression, 'this' is the vector, and Other is the plane. This is an unsafe conversion, as the destination property type is used to copy the r-value to the l-value
// so in this case, the VM would call CopyCompleteValue on the FPlane struct, which would copy 16 bytes into the l-value's buffer; However, the l-value buffer will only be
// 12 bytes because that is the size of FVector
// - Plane = Vector;
// in this expression, 'this' is the plane, and Other is the vector. This is a safe conversion, since only 12 bytes would be copied from the r-value into the l-value's buffer
// (which would be 16 bytes). The problem with allowing this conversion is that what to do with the extra member (e.g. Plane.W); should it be left alone? should it be zeroed?
// difficult to say what the correct behavior should be, so let's just ignore inheritance for the sake of determining whether two structs are identical
// Previously, the logic for determining whether this is a generalization of Other was reversed; this is very likely the culprit behind all current issues with
// using derived structs interchangeably with their base versions. The inheritance check has been fixed; for now, allow struct generalization and see if we can find any further
// issues with allowing conversion. If so, then we disable all struct generalization by returning false here.
// return false;
if ( bDisallowGeneralization )
{
return false;
}
// Generalization is ok if this is not a dynamic array
if ( ArrayType != EArrayType::Dynamic && Other.ArrayType != EArrayType::Dynamic )
{
if ( !Other.Struct->IsChildOf(Struct) && Struct->IsChildOf(Other.Struct) )
{
return true;
}
}
return false;
}
else
{
// General match.
return true;
}
}
FString Describe()
{
return FString::Printf(
TEXT("Type:%s Flags:%lli ImpliedFlags:%lli Enum:%s PropertyClass:%s Struct:%s Function:%s MetaClass:%s"),
GetPropertyTypeText(Type), PropertyFlags, ImpliedPropertyFlags,
Enum!=NULL?*Enum->GetName():TEXT(""),
PropertyClass!=NULL?*PropertyClass->GetName():TEXT("NULL"),
Struct!=NULL?*Struct->GetName():TEXT("NULL"),
Function!=NULL?*Function->GetName():TEXT("NULL"),
MetaClass!=NULL?*MetaClass->GetName():TEXT("NULL")
);
}
//@}
EIntType GetSizedIntTypeFromPropertyType(EPropertyType PropType)
{
switch (PropType)
{
case CPT_Byte:
case CPT_UInt16:
case CPT_UInt32:
case CPT_UInt64:
case CPT_Int8:
case CPT_Int16:
case CPT_Int:
case CPT_Int64:
return EIntType::Sized;
default:
return EIntType::None;
}
}
static const TCHAR* GetPropertyTypeText( EPropertyType Type );
friend struct FPropertyBaseArchiveProxy;
};
//
// Token types.
//
enum ETokenType
{
TOKEN_None = 0x00, // No token.
TOKEN_Identifier = 0x01, // Alphanumeric identifier.
TOKEN_Symbol = 0x02, // Symbol.
TOKEN_Const = 0x03, // A constant.
TOKEN_Max = 0x0D
};
/*-----------------------------------------------------------------------------
FToken.
-----------------------------------------------------------------------------*/
//
// Information about a token that was just parsed.
//
class FToken : public FPropertyBase
{
public:
/** @name Variables */
//@{
/** Type of token. */
ETokenType TokenType;
/** Name of token. */
FName TokenName;
/** Starting position in script where this token came from. */
int32 StartPos;
/** Starting line in script. */
int32 StartLine;
/** Always valid. */
TCHAR Identifier[NAME_SIZE];
/** property that corresponds to this FToken - null if this Token doesn't correspond to a UProperty */
UProperty* TokenProperty;
union
{
// TOKEN_Const values.
uint8 Byte; // If CPT_Byte.
int64 Int64; // If CPT_Int64.
int32 Int; // If CPT_Int.
bool NativeBool; // if CPT_Bool
float Float; // If CPT_Float.
double Double; // If CPT_Double.
uint8 NameBytes[sizeof(FName)]; // If CPT_Name.
TCHAR String[MAX_STRING_CONST_SIZE]; // If CPT_String
};
//@}
/**
* Copies the properties from this token into another.
*
* @param Other the token to copy this token's properties to.
*/
void Clone( const FToken& Other );
FString GetConstantValue() const
{
if (TokenType == TOKEN_Const)
{
switch (Type)
{
case CPT_Byte:
return FString::Printf(TEXT("%u"), Byte);
case CPT_Int64:
return FString::Printf(TEXT("%ld"), Int64);
case CPT_Int:
return FString::Printf(TEXT("%i"), Int);
case CPT_Bool:
// Don't use GTrue/GFalse here because they can be localized
return FString::Printf(TEXT("%s"), NativeBool ? *(FName::GetEntry(NAME_TRUE)->GetPlainNameString()) : *(FName::GetEntry(NAME_FALSE)->GetPlainNameString()));
case CPT_Float:
return FString::Printf(TEXT("%f"), Float);
case CPT_Double:
return FString::Printf(TEXT("%f"), Double);
case CPT_Name:
return FString::Printf(TEXT("%s"), *(*(FName*)NameBytes).ToString());
case CPT_String:
return String;
// unsupported (parsing never produces a constant token of these types)
default:
return TEXT("InvalidTypeForAToken");
}
}
else
{
return TEXT("NotConstant");
}
}
// Constructors.
FToken()
: FPropertyBase(CPT_None)
, TokenProperty(NULL)
{
InitToken(CPT_None);
}
// copy constructors
explicit FToken(EPropertyType InType)
: FPropertyBase(InType)
, TokenProperty(NULL)
{
InitToken(InType);
}
// copy constructors
FToken(const FPropertyBase& InType)
: FPropertyBase(CPT_None)
, TokenProperty(NULL)
{
InitToken( CPT_None );
(FPropertyBase&)*this = InType;
}
// Inlines.
void InitToken( EPropertyType InType )
{
(FPropertyBase&)*this = FPropertyBase(InType);
TokenType = TOKEN_None;
TokenName = NAME_None;
StartPos = 0;
StartLine = 0;
*Identifier = 0;
FMemory::Memzero(String);
}
bool Matches( const TCHAR* Str, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase ) const
{
return (TokenType==TOKEN_Identifier || TokenType==TOKEN_Symbol) && ((SearchCase == ESearchCase::CaseSensitive) ? !FCString::Strcmp(Identifier, Str) : !FCString::Stricmp(Identifier, Str));
}
bool Matches( const FName& Name ) const
{
return TokenType==TOKEN_Identifier && TokenName==Name;
}
bool StartsWith( const TCHAR* Str, bool bCaseSensitive = false ) const
{
const int32 StrLength = FCString::Strlen(Str);
return (TokenType==TOKEN_Identifier || TokenType==TOKEN_Symbol) && (bCaseSensitive ? (!FCString::Strncmp(Identifier, Str, StrLength)) : (!FCString::Strnicmp(Identifier, Str, StrLength)));
}
bool IsBool() const
{
return Type == CPT_Bool || Type == CPT_Bool8 || Type == CPT_Bool16 || Type == CPT_Bool32 || Type == CPT_Bool64;
}
// Setters.
void SetIdentifier( const TCHAR* InString)
{
InitToken(CPT_None);
TokenType = TOKEN_Identifier;
FCString::Strncpy(Identifier, InString, NAME_SIZE);
TokenName = FName(Identifier, FNAME_Find);
}
void SetConstInt64( int64 InInt64 )
{
(FPropertyBase&)*this = FPropertyBase(CPT_Int64);
Int64 = InInt64;
TokenType = TOKEN_Const;
}
void SetConstInt( int32 InInt )
{
(FPropertyBase&)*this = FPropertyBase(CPT_Int);
Int = InInt;
TokenType = TOKEN_Const;
}
void SetConstBool( bool InBool )
{
(FPropertyBase&)*this = FPropertyBase(CPT_Bool);
NativeBool = InBool;
TokenType = TOKEN_Const;
}
void SetConstFloat( float InFloat )
{
(FPropertyBase&)*this = FPropertyBase(CPT_Float);
Float = InFloat;
TokenType = TOKEN_Const;
}
void SetConstDouble( double InDouble )
{
(FPropertyBase&)*this = FPropertyBase(CPT_Double);
Double = InDouble;
TokenType = TOKEN_Const;
}
void SetConstName( FName InName )
{
(FPropertyBase&)*this = FPropertyBase(CPT_Name);
*(FName *)NameBytes = InName;
TokenType = TOKEN_Const;
}
void SetConstString( const TCHAR* InString, int32 MaxLength=MAX_STRING_CONST_SIZE )
{
check(MaxLength>0);
(FPropertyBase&)*this = FPropertyBase(CPT_String);
if( InString != String )
{
FCString::Strncpy( String, InString, MaxLength );
}
TokenType = TOKEN_Const;
}
void SetConstChar(TCHAR InChar)
{
//@TODO: Treating this like a string for now, nothing consumes it
(FPropertyBase&)*this = FPropertyBase(CPT_String);
String[0] = InChar;
String[1] = 0;
TokenType = TOKEN_Const;
}
//!!struct constants
// Getters.
bool GetConstInt( int32& I ) const
{
if( TokenType==TOKEN_Const && Type==CPT_Int64 )
{
I = Int64;
return 1;
}
else if( TokenType==TOKEN_Const && Type==CPT_Int )
{
I = Int;
return 1;
}
else if( TokenType==TOKEN_Const && Type==CPT_Byte )
{
I = Byte;
return 1;
}
else if( TokenType==TOKEN_Const && Type==CPT_Float && Float==FMath::TruncToInt(Float))
{
I = (int32) Float;
return 1;
}
else if (TokenType == TOKEN_Const && Type == CPT_Double && Float == FMath::TruncToInt(Double))
{
I = (int32) Double;
return 1;
}
else return 0;
}
bool GetConstInt64( int64& I ) const
{
if( TokenType==TOKEN_Const && Type==CPT_Int64 )
{
I = Int64;
return 1;
}
else if( TokenType==TOKEN_Const && Type==CPT_Int )
{
I = Int;
return 1;
}
else if( TokenType==TOKEN_Const && Type==CPT_Byte )
{
I = Byte;
return 1;
}
else if( TokenType==TOKEN_Const && Type==CPT_Float && Float==FMath::TruncToInt(Float))
{
I = (int32) Float;
return 1;
}
else if (TokenType == TOKEN_Const && Type == CPT_Double && Float == FMath::TruncToInt(Double))
{
I = (int32) Double;
return 1;
}
else return 0;
}
FString Describe()
{
return FString::Printf(
TEXT("Property:%s Type:%s TokenName:%s ConstValue:%s Struct:%s Flags:%lli Implied:%lli"),
TokenProperty!=NULL?*TokenProperty->GetName():TEXT("NULL"),
GetPropertyTypeText(Type), *TokenName.ToString(), *GetConstantValue(),
Struct!=NULL?*Struct->GetName():TEXT("NULL"),
PropertyFlags,
ImpliedPropertyFlags
);
}
friend struct FTokenArchiveProxy;
};
/**
* A group of FTokens. Used for keeping track of reference chains tokens
* e.g. SomeObject.default.Foo.DoSomething()
*/
class FTokenChain : public TArray<FToken>
{
public:
FToken& operator+=( const FToken& NewToken )
{
FToken& Token = (*this)[AddZeroed()] = NewToken;
return Token;
}
};
/**
* Information about a function being compiled.
*/
struct FFuncInfo
{
/** @name Variables */
//@{
/** Name of the function or operator. */
FToken Function;
/** Function flags. */
EFunctionFlags FunctionFlags;
/** Function flags which are only required for exporting */
uint32 FunctionExportFlags;
/** Number of parameters expected for operator. */
int32 ExpectParms;
/** Pointer to the UFunction corresponding to this FFuncInfo */
UFunction* FunctionReference;
/** Name of the wrapper function that marshalls the arguments and does the indirect call **/
FString MarshallAndCallName;
/** Name of the actual implementation **/
FString CppImplName;
/** Name of the actual validation implementation **/
FString CppValidationImplName;
/** Name for callback-style names **/
FString UnMarshallAndCallName;
/** Endpoint name */
FString EndpointName;
/** Identifier for an RPC call to a platform service */
int16 RPCId;
/** Identifier for an RPC call expecting a response */
int16 RPCResponseId;
/** Whether this function represents a sealed event */
bool bSealedEvent;
/** Delegate macro line in header. */
int32 MacroLine;
/** Position in file where this function was declared. Points to first char of function name. */
int32 InputPos;
/** TRUE if the function is being forced to be considered as impure by the user */
bool bForceBlueprintImpure;
//@}
/** Constructor. */
FFuncInfo()
: Function()
, FunctionFlags(FUNC_None)
, FunctionExportFlags(0)
, ExpectParms(0)
, FunctionReference(NULL)
, CppImplName(TEXT(""))
, CppValidationImplName(TEXT(""))
, RPCId(0)
, RPCResponseId(0)
, bSealedEvent(false)
, MacroLine(-1)
, InputPos(-1)
, bForceBlueprintImpure(false)
{}
FFuncInfo(const FFuncInfo& Other)
: FunctionFlags(Other.FunctionFlags)
, FunctionExportFlags(Other.FunctionExportFlags)
, ExpectParms(Other.ExpectParms)
, FunctionReference(Other.FunctionReference)
, CppImplName(Other.CppImplName)
, CppValidationImplName(Other.CppValidationImplName)
, RPCId(Other.RPCId)
, RPCResponseId(Other.RPCResponseId)
, MacroLine(Other.MacroLine)
, InputPos(Other.InputPos)
, bForceBlueprintImpure(Other.bForceBlueprintImpure)
{
Function.Clone(Other.Function);
if (FunctionReference)
{
SetFunctionNames();
}
}
/** Set the internal function names based on flags **/
void SetFunctionNames()
{
FString FunctionName = FunctionReference->GetName();
if( FunctionReference->HasAnyFunctionFlags( FUNC_Delegate ) )
{
FunctionName = FunctionName.LeftChop( FString(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX).Len() );
}
UnMarshallAndCallName = FString(TEXT("exec")) + FunctionName;
if (FunctionReference->HasAnyFunctionFlags(FUNC_BlueprintEvent))
{
MarshallAndCallName = FunctionName;
}
else
{
MarshallAndCallName = FString(TEXT("event")) + FunctionName;
}
if (FunctionReference->HasAllFunctionFlags(FUNC_Native | FUNC_Net))
{
MarshallAndCallName = FunctionName;
if (FunctionReference->HasAllFunctionFlags(FUNC_NetResponse))
{
// Response function implemented by programmer and called directly from thunk
CppImplName = FunctionReference->GetName();
}
else
{
if (CppImplName.IsEmpty())
{
CppImplName = FunctionReference->GetName() + TEXT("_Implementation");
}
else if (CppImplName == FunctionName)
{
FError::Throwf(TEXT("Native implementation function must be different than original function name."));
}
if (CppValidationImplName.IsEmpty() && FunctionReference->HasAllFunctionFlags(FUNC_NetValidate))
{
CppValidationImplName = FunctionReference->GetName() + TEXT("_Validate");
}
else if(CppValidationImplName == FunctionName)
{
FError::Throwf(TEXT("Validation function must be different than original function name."));
}
}
}
if (FunctionReference->HasAllFunctionFlags(FUNC_Delegate))
{
MarshallAndCallName = FString(TEXT("delegate")) + FunctionName;
}
if (FunctionReference->HasAllFunctionFlags(FUNC_BlueprintEvent | FUNC_Native))
{
MarshallAndCallName = FunctionName;
CppImplName = FunctionReference->GetName() + TEXT("_Implementation");
}
if (CppImplName.IsEmpty())
{
CppImplName = FunctionName;
}
}
};
/**
* Stores "compiler" data about an FToken. "Compiler" data is data that is associated with a
* specific property, function or class that is only needed during script compile.
* This class is designed to make adding new compiler data very simple.
*
* - stores the raw evaluated bytecode associated with an FToken
*/
struct FTokenData
{
/** The token tracked by this FTokenData. */
FToken Token;
/** @name Constructors */
//@{
/**
* Defalt constructor
*/
FTokenData()
{}
/**
* Copy constructor
*/
FTokenData(const FToken& inToken)
: Token(inToken)
{}
//@}
};
/**
* Class for storing data about a list of properties. Though FToken contains a reference to its
* associated UProperty, it's faster lookup to use the UProperty as the key in a TMap.
*/
class FPropertyData : public TMap< UProperty*, TSharedPtr<FTokenData> >
{
typedef TMap<UProperty*, TSharedPtr<FTokenData> > Super;
public:
/**
* Returns the value associated with a specified key.
* @param Key - The key to search for.
* @return A pointer to the value associated with the specified key, or NULL if the key isn't contained in this map. The pointer
* is only valid until the next change to any key in the map.
*/
FTokenData* Find(UProperty* Key)
{
FTokenData* Result = NULL;
TSharedPtr<FTokenData>* pResult = Super::Find(Key);
if ( pResult != NULL )
{
Result = pResult->Get();
}
return Result;
}
const FTokenData* Find(UProperty* Key) const
{
const FTokenData* Result = NULL;
const TSharedPtr<FTokenData>* pResult = Super::Find(Key);
if ( pResult != NULL )
{
Result = pResult->Get();
}
return Result;
}
/**
* Sets the value associated with a key. If the key already exists in the map, uses the same
* value pointer and reinitalized the FTokenData with the input value.
*
* @param InKey the property to get a token wrapper for
* @param InValue the token wrapper for the specified key
*
* @return a pointer to token data created associated with the property
*/
FTokenData* Set(UProperty* InKey, const FTokenData& InValue, FUnrealSourceFile* UnrealSourceFile);
/**
* (debug) Dumps the values of this FPropertyData to the log file
*
* @param Indent number of spaces to insert at the beginning of each line
*/
void Dump( int32 Indent )
{
for (auto& Kvp : *this)
{
TSharedPtr<FTokenData>& PointerVal = Kvp.Value;
FToken& Token = PointerVal->Token;
if ( Token.Type != CPT_None )
{
UE_LOG(LogCompile, Log, TEXT("%s%s"), FCString::Spc(Indent), *Token.Describe());
}
}
}
};
/**
* Class for storing additional data about compiled structs and struct properties
*/
class FStructData
{
public:
/** info about the struct itself */
FToken StructData;
private:
/** info for the properties contained in this struct */
FPropertyData StructPropertyData;
public:
/**
* Adds a new struct property token
*
* @param PropertyToken token that should be added to the list
*/
void AddStructProperty(const FTokenData& PropertyToken, FUnrealSourceFile* UnrealSourceFile)
{
check(PropertyToken.Token.TokenProperty);
StructPropertyData.Set(PropertyToken.Token.TokenProperty, PropertyToken, UnrealSourceFile);
}
FPropertyData& GetStructPropertyData()
{
return StructPropertyData;
}
const FPropertyData& GetStructPropertyData() const
{
return StructPropertyData;
}
/**
* (debug) Dumps the values of this FStructData to the log file
*
* @param Indent number of spaces to insert at the beginning of each line
*/
void Dump( int32 Indent )
{
UE_LOG(LogCompile, Log, TEXT("%s%s"), FCString::Spc(Indent), *StructData.Describe());
UE_LOG(LogCompile, Log, TEXT("%sproperties:"), FCString::Spc(Indent));
StructPropertyData.Dump(Indent + 4);
}
/** Constructor */
FStructData( const FToken& StructToken ) : StructData(StructToken) {}
friend struct FStructDataArchiveProxy;
};
/**
* Class for storing additional data about compiled function properties.
*/
class FFunctionData
{
/** info about the function associated with this FFunctionData */
FFuncInfo FunctionData;
/** return value for this function */
FTokenData ReturnTypeData;
/** function parameter data */
FPropertyData ParameterData;
/**
* Adds a new parameter token
*
* @param PropertyToken token that should be added to the list
*/
void AddParameter(const FToken& PropertyToken, FUnrealSourceFile* UnrealSourceFile)
{
check(PropertyToken.TokenProperty);
ParameterData.Set(PropertyToken.TokenProperty, PropertyToken, UnrealSourceFile);
}
/**
* Sets the value of the return token for this function
*
* @param PropertyToken token that should be added
*/
void SetReturnData( const FToken& PropertyToken )
{
check(PropertyToken.TokenProperty);
ReturnTypeData.Token = PropertyToken;
}
public:
/** Constructors */
FFunctionData() {}
FFunctionData( const FFunctionData& Other )
{
(*this) = Other;
}
FFunctionData( const FFuncInfo& inFunctionData )
: FunctionData(inFunctionData)
{}
/** Copy operator */
FFunctionData& operator=( const FFunctionData& Other )
{
FunctionData = Other.FunctionData;
ParameterData = Other.ParameterData;
ReturnTypeData.Token.Clone(Other.ReturnTypeData.Token);
return *this;
}
/** @name getters */
//@{
const FFuncInfo& GetFunctionData() const { return FunctionData; }
const FToken& GetReturnData() const { return ReturnTypeData.Token; }
const FPropertyData& GetParameterData() const { return ParameterData; }
FPropertyData& GetParameterData() { return ParameterData; }
FTokenData* GetReturnTokenData() { return &ReturnTypeData; }
//@}
void UpdateFunctionData(FFuncInfo& UpdatedFuncData)
{
//@TODO: UCREMOVAL: Some more thorough evaluation should be done here
FunctionData.FunctionFlags |= UpdatedFuncData.FunctionFlags;
FunctionData.FunctionExportFlags |= UpdatedFuncData.FunctionExportFlags;
}
/**
* Adds a new function property to be tracked. Determines whether the property is a
* function parameter, local property, or return value, and adds it to the appropriate
* list
*
* @param PropertyToken the property to add
*/
void AddProperty(const FToken& PropertyToken, FUnrealSourceFile* UnrealSourceFile)
{
const UProperty* Prop = PropertyToken.TokenProperty;
check(Prop);
check( (Prop->PropertyFlags&CPF_Parm) != 0 );
if ( (Prop->PropertyFlags&CPF_ReturnParm) != 0 )
{
SetReturnData(PropertyToken);
}
else
{
AddParameter(PropertyToken, UnrealSourceFile);
}
}
/**
* (debug) Dumps the values of this FFunctionData to the log file
*
* @param Indent number of spaces to insert at the beginning of each line
*/
void Dump( int32 Indent )
{
UE_LOG(LogCompile, Log, TEXT("%sparameters:"), FCString::Spc(Indent));
ParameterData.Dump(Indent + 4);
UE_LOG(LogCompile, Log, TEXT("%sreturn prop:"), FCString::Spc(Indent));
if ( ReturnTypeData.Token.Type != CPT_None )
{
UE_LOG(LogCompile, Log, TEXT("%s%s"), FCString::Spc(Indent + 4), *ReturnTypeData.Token.Describe());
}
}
/**
* Sets the specified function export flags
*/
void SetFunctionExportFlag( uint32 NewFlags )
{
FunctionData.FunctionExportFlags |= NewFlags;
}
/**
* Clears the specified function export flags
*/
void ClearFunctionExportFlags( uint32 ClearFlags )
{
FunctionData.FunctionExportFlags &= ~ClearFlags;
}
/**
* Finds function data for given function object.
*/
static FFunctionData* FindForFunction(UFunction* Function);
/**
* Adds function data object for given function object.
*/
static FFunctionData* Add(UFunction* Function);
/**
* Adds function data object for given function object.
*/
static FFunctionData* Add(const FFuncInfo& FunctionInfo);
/**
* Tries to find function data for given function object.
*/
static bool TryFindForFunction(UFunction* Function, FFunctionData*& OutData);
private:
static TMap<UFunction*, TUniqueObj<FFunctionData> > FunctionDataMap;
};
/**
* Tracks information about a multiple inheritance parent declaration for native script classes.
*/
struct FMultipleInheritanceBaseClass
{
/**
* The name to use for the base class when exporting the script class to header file.
*/
FString ClassName;
/**
* For multiple inheritance parents declared using 'Implements', corresponds to the UClass for the interface. For multiple inheritance parents declared
* using 'Inherits', this value will be NULL.
*/
UClass* InterfaceClass;
/**
* Constructors
*/
FMultipleInheritanceBaseClass(const FString& BaseClassName)
: ClassName(BaseClassName), InterfaceClass(NULL)
{}
FMultipleInheritanceBaseClass(UClass* ImplementedInterfaceClass)
: InterfaceClass(ImplementedInterfaceClass)
{
ClassName = FString::Printf(TEXT("I%s"), *ImplementedInterfaceClass->GetName());
}
};
/**
* Class for storing compiler metadata about a class's properties.
*/
class FClassMetaData
{
/** member properties for this class */
FPropertyData GlobalPropertyData;
/** base classes to multiply inherit from (other than the main base class */
TArray<FMultipleInheritanceBaseClass*> MultipleInheritanceParents;
/** whether this class declares delegate functions or properties */
bool bContainsDelegates;
/** The line of UCLASS/UINTERFACE macro in this class. */
int32 PrologLine;
/** The line of GENERATED_BODY/GENERATED_UCLASS_BODY macro in this class. */
int32 GeneratedBodyLine;
/** Same as above, but for interface class associated with this class. */
int32 InterfaceGeneratedBodyLine;
public:
/** Default constructor */
FClassMetaData()
: bContainsDelegates(false)
, PrologLine(-1)
, GeneratedBodyLine(-1)
, InterfaceGeneratedBodyLine(-1)
, bConstructorDeclared(false)
, bDefaultConstructorDeclared(false)
, bObjectInitializerConstructorDeclared(false)
, bCustomVTableHelperConstructorDeclared(false)
, GeneratedBodyMacroAccessSpecifier(ACCESS_NotAnAccessSpecifier)
{
}
/**
* Gets prolog line number for this class.
*/
int32 GetPrologLine() const
{
check(PrologLine > 0);
return PrologLine;
}
/**
* Gets generated body line number for this class.
*/
int32 GetGeneratedBodyLine() const
{
check(GeneratedBodyLine > 0);
return GeneratedBodyLine;
}
/**
* Gets interface generated body line number for this class.
*/
int32 GetInterfaceGeneratedBodyLine() const
{
check(InterfaceGeneratedBodyLine > 0);
return InterfaceGeneratedBodyLine;
}
/**
* Sets prolog line number for this class.
*/
void SetPrologLine(int32 Line)
{
check(Line > 0);
PrologLine = Line;
}
/**
* Sets generated body line number for this class.
*/
void SetGeneratedBodyLine(int32 Line)
{
check(Line > 0);
GeneratedBodyLine = Line;
}
/**
* Sets interface generated body line number for this class.
*/
void SetInterfaceGeneratedBodyLine(int32 Line)
{
check(Line > 0);
InterfaceGeneratedBodyLine = Line;
}
/**
* Sets contains delegates flag for this class.
*/
void MarkContainsDelegate()
{
bContainsDelegates = true;
}
/**
* Adds a new property to be tracked. Determines the correct list for the property based on
* its owner (function, struct, etc).
*
* @param PropertyToken the property to add
*/
void AddProperty(const FToken& PropertyToken, FUnrealSourceFile* UnrealSourceFile)
{
UProperty* Prop = PropertyToken.TokenProperty;
check(Prop);
UObject* Outer = Prop->GetOuter();
UStruct* OuterClass = Cast<UStruct>(Outer);
if ( OuterClass != NULL )
{
// global property
GlobalPropertyData.Set(Prop, PropertyToken, UnrealSourceFile);
}
else
{
checkNoEntry();
UFunction* OuterFunction = Cast<UFunction>(Outer);
if ( OuterFunction != NULL )
{
// function parameter, return, or local property
FFunctionData::FindForFunction(OuterFunction)->AddProperty(PropertyToken, UnrealSourceFile);
}
}
// update the optimization flags
if ( !bContainsDelegates )
{
if( Prop->IsA( UDelegateProperty::StaticClass() ) || Prop->IsA( UMulticastDelegateProperty::StaticClass() ) )
{
bContainsDelegates = true;
}
else
{
UArrayProperty* ArrayProp = Cast<UArrayProperty>(Prop);
if ( ArrayProp != NULL )
{
if( ArrayProp->Inner->IsA( UDelegateProperty::StaticClass() ) || ArrayProp->Inner->IsA( UMulticastDelegateProperty::StaticClass() ) )
{
bContainsDelegates = true;
}
}
}
}
}
/**
* Adds new editor-only metadata (key/value pairs) to the class or struct that
* owns this property or function.
*
* @param Field the property or function to add to
* @param InMetaData the metadata to add
*/
static void AddMetaData(UField* Field, const TMap<FName, FString>& InMetaData)
{
// only add if we have some!
if (InMetaData.Num())
{
check(Field);
// get (or create) a metadata object for this package
UMetaData* MetaData = Field->GetOutermost()->GetMetaData();
// set the metadata for this field
MetaData->SetObjectValues(Field, InMetaData);
}
}
/**
* Finds the metadata for the function specified
*
* @param Func the function to search for
*
* @return pointer to the metadata for the function specified, or NULL
* if the function doesn't exist in the list (for example, if it
* is declared in a package that is already compiled and has had its
* source stripped)
*/
FFunctionData* FindFunctionData( UFunction* Func );
/**
* Finds the metadata for the property specified
*
* @param Prop the property to search for
*
* @return pointer to the metadata for the property specified, or NULL
* if the property doesn't exist in the list (for example, if it
* is declared in a package that is already compiled and has had its
* source stripped)
*/
FTokenData* FindTokenData( UProperty* Prop );
/**
* (debug) Dumps the values of this FFunctionData to the log file
*
* @param Indent number of spaces to insert at the beginning of each line
*/
void Dump( int32 Indent );
/**
* Add a string to the list of inheritance parents for this class.
*
* @param Inparent The C++ class name to add to the multiple inheritance list
* @param UnrealSourceFile Currently parsed source file.
*/
void AddInheritanceParent(const FString& InParent, FUnrealSourceFile* UnrealSourceFile);
/**
* Add a string to the list of inheritance parents for this class.
*
* @param Inparent The C++ class name to add to the multiple inheritance list
* @param UnrealSourceFile Currently parsed source file.
*/
void AddInheritanceParent(UClass* ImplementedInterfaceClass, FUnrealSourceFile* UnrealSourceFile);
/**
* Return the list of inheritance parents
*/
const TArray<FMultipleInheritanceBaseClass*>& GetInheritanceParents() const
{
return MultipleInheritanceParents;
}
/**
* Returns whether this class contains any delegate properties which need to be fixed up.
*/
bool ContainsDelegates() const
{
return bContainsDelegates;
}
/**
* Shrink TMaps to avoid slack in Pairs array.
*/
void Shrink()
{
GlobalPropertyData.Shrink();
MultipleInheritanceParents.Shrink();
}
// Is constructor declared?
bool bConstructorDeclared;
// Is default constructor declared?
bool bDefaultConstructorDeclared;
// Is ObjectInitializer constructor (i.e. a constructor with only one parameter of type FObjectInitializer) declared?
bool bObjectInitializerConstructorDeclared;
// Is custom VTable helper constructor declared?
bool bCustomVTableHelperConstructorDeclared;
// GENERATED_BODY access specifier to preserve.
EAccessSpecifier GeneratedBodyMacroAccessSpecifier;
friend struct FClassMetaDataArchiveProxy;
};
/**
* Class for storing and linking data about properties and functions that is only required by the compiler.
* The type of data tracked by this class is data that would otherwise only be accessible by adding a
* member property to UFunction/UProperty.
*/
class FCompilerMetadataManager : protected TMap<UStruct*, TUniquePtr<FClassMetaData> >
{
public:
/**
* Adds a new class to be tracked
*
* @param Struct the UStruct to add
*
* @return a pointer to the newly added metadata for the class specified
*/
FClassMetaData* AddClassData(UStruct* Struct, FUnrealSourceFile* UnrealSourceFile);
/**
* Find the metadata associated with the class specified
*
* @param Struct the UStruct to add
*
* @return a pointer to the newly added metadata for the class specified
*/
FClassMetaData* FindClassData(UStruct* Struct)
{
FClassMetaData* Result = nullptr;
TUniquePtr<FClassMetaData>* pClassData = Find(Struct);
if (pClassData)
{
Result = pClassData->Get();
}
return Result;
}
/**
* Shrink TMaps to avoid slack in Pairs array.
*/
void Shrink()
{
TMap<UStruct*, TUniquePtr<FClassMetaData> >::Shrink();
for (TMap<UStruct*, TUniquePtr<FClassMetaData> >::TIterator It(*this); It; ++It)
{
FClassMetaData* MetaData = It->Value.Get();
MetaData->Shrink();
}
}
friend struct FCompilerMetadataManagerArchiveProxy;
};
/*-----------------------------------------------------------------------------
Retry points.
-----------------------------------------------------------------------------*/
/**
* A point in the header parsing state that can be set and returned to
* using InitScriptLocation() and ReturnToLocation(). This is used in cases such as testing
* to see which overridden operator should be used, where code must be compiled
* and then "undone" if it was found not to match.
* <p>
* Retries are not allowed to cross command boundaries (and thus nesting
* boundaries). Retries can occur across a single command or expressions and
* subexpressions within a command.
*/
struct FScriptLocation
{
static class FHeaderParser* Compiler;
/** the text buffer for the class associated with this retry point */
const TCHAR* Input;
/** the position into the Input buffer where this retry point is located */
int32 InputPos;
/** the LineNumber of the compiler when this retry point was created */
int32 InputLine;
/** Constructor */
FScriptLocation();
};
/////////////////////////////////////////////////////
// FNameLookupCPP
/**
* Helper class used to cache UClass* -> TCHAR* name lookup for finding the named used for C++ declaration.
*/
struct FNameLookupCPP
{
/**
* Destructor, cleaning up allocated memory.
*/
~FNameLookupCPP()
{
for (TMap<UStruct*,TCHAR*>::TIterator It(StructNameMap); It; ++It)
{
TCHAR* Name = It.Value();
delete [] Name;
}
for (TArray<TCHAR*>::TIterator It(InterfaceAllocations); It; ++It)
{
TCHAR* Name = *It;
delete [] Name;
}
}
/**
* Returns the name used for declaring the passed in struct in C++
*
* @param Struct UStruct to obtain C++ name for
* @return Name used for C++ declaration
*/
const TCHAR* GetNameCPP( UStruct* Struct, bool bForceInterface = false );
void SetCurrentSourceFile(FUnrealSourceFile* InUnrealSourceFile)
{
UnrealSourceFile = InUnrealSourceFile;
}
private:
/** Map of UStruct pointers to C++ names */
TMap<UStruct*,TCHAR*> StructNameMap;
TArray<TCHAR*> InterfaceAllocations;
FUnrealSourceFile* UnrealSourceFile;
};
extern FNameLookupCPP NameLookupCPP;
/////////////////////////////////////////////////////
// FAdvancedDisplayParameterHandler - used by FHeaderParser::ParseParameterList, to check if a property if a function parameter has 'AdvancedDisplay' flag
/**
* AdvancedDisplay can be used in two ways:
* 1. 'AdvancedDisplay = "3"' - the number tells how many parameters (from beginning) should NOT BE marked
* 2. 'AdvancedDisplay = "AttachPointName, Location, LocationType"' - list the parameters, that should BE marked
*/
class FAdvancedDisplayParameterHandler
{
TArray<FString> ParametersNames;
int32 NumberLeaveUnmarked;
int32 AlreadyLeft;
bool bUseNumber;
public:
FAdvancedDisplayParameterHandler(const TMap<FName, FString>* MetaData);
/**
* return if given parameter should be marked as Advance View,
* the function should be called only once for any parameter
*/
bool ShouldMarkParameter(const FString& ParameterName);
/** return if more parameters can be marked */
bool CanMarkMore() const;
};