2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "UnrealHeaderTool.h"
# include "HeaderParser.h"
# include "NativeClassExporter.h"
# include "ClassMaps.h"
2014-04-23 18:42:01 -04:00
# include "Classes.h"
2014-04-23 18:46:37 -04:00
# include "StringUtils.h"
2014-03-14 14:13:41 -04:00
# include "UObjectAnnotation.h"
# include "DefaultValueHelper.h"
2014-04-29 06:45:18 -04:00
# include "IScriptGeneratorPluginInterface.h"
2014-05-22 04:36:37 -04:00
# include "Manifest.h"
2015-01-06 07:29:48 -05:00
# include "UnitConversion.h"
2015-04-10 06:02:22 -04:00
# include "GeneratedCodeVersion.h"
2014-03-14 14:13:41 -04:00
2015-03-27 17:16:51 -04:00
double GPluginOverheadTime = 0.0 ;
double GHeaderCodeGenTime = 0.0 ;
2014-03-14 14:13:41 -04:00
/*-----------------------------------------------------------------------------
Constants & declarations .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-01-20 09:33:54 -05:00
/**
* Data struct that annotates source files that failed during parsing .
*/
class FFailedFilesAnnotation
{
public :
/**
* Gets annotation state for given source file .
*/
bool Get ( FUnrealSourceFile * SourceFile ) const
{
return AnnotatedSet . Contains ( SourceFile ) ;
}
/**
* Sets annotation state to true for given source file .
*/
void Set ( FUnrealSourceFile * SourceFile )
{
AnnotatedSet . Add ( SourceFile ) ;
}
private :
// Annotation set.
TSet < FUnrealSourceFile * > AnnotatedSet ;
} static FailedFilesAnnotation ;
2014-03-14 14:13:41 -04:00
enum { MAX_ARRAY_SIZE = 2048 } ;
static const FName NAME_ToolTip ( TEXT ( " ToolTip " ) ) ;
2015-04-10 06:02:22 -04:00
EGeneratedCodeVersion FHeaderParser : : DefaultGeneratedCodeVersion = EGeneratedCodeVersion : : V1 ;
2015-01-20 09:33:54 -05:00
TMap < UClass * , ClassDefinitionRange > ClassDefinitionRanges ;
2014-04-23 18:36:17 -04:00
/**
* Dirty hack global variable to allow different result codes passed through
* exceptions . Needs to be fixed in future versions of UHT .
*/
extern ECompilationResult : : Type GCompilationResult ;
2014-03-14 14:13:41 -04:00
/*-----------------------------------------------------------------------------
Utility functions .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
namespace
{
bool ProbablyAMacro ( const TCHAR * Identifier )
{
// Test for known delegate and event macros.
TCHAR DelegateStart [ ] = TEXT ( " DECLARE_DELEGATE_ " ) ;
if ( ! FCString : : Strncmp ( Identifier , DelegateStart , ARRAY_COUNT ( DelegateStart ) - 1 ) )
return true ;
TCHAR DelegateEvent [ ] = TEXT ( " DECLARE_EVENT " ) ;
if ( ! FCString : : Strncmp ( Identifier , DelegateEvent , ARRAY_COUNT ( DelegateEvent ) - 1 ) )
return true ;
// Failing that, we'll guess about it being a macro based on it being a fully-capitalized identifier.
while ( TCHAR Ch = * Identifier + + )
{
if ( Ch ! = TEXT ( ' _ ' ) & & ( Ch < TEXT ( ' A ' ) | | Ch > TEXT ( ' Z ' ) ) )
return false ;
}
return true ;
}
/**
* Parse and validate an array of identifiers ( inside FUNC_NetRequest , FUNC_NetResponse )
* @ param FuncInfo function info for the current function
* @ param Identifiers identifiers inside the net service declaration
*/
void ParseNetServiceIdentifiers ( FFuncInfo & FuncInfo , const TArray < FString > & Identifiers )
{
FString IdTag ( TEXT ( " Id= " ) ) ;
FString ResponseIdTag ( TEXT ( " ResponseId= " ) ) ;
FString MCPTag ( TEXT ( " MCP " ) ) ;
FString ProtobufferTag ( TEXT ( " Protobuffer " ) ) ;
for ( auto & Identifier : Identifiers )
{
if ( Identifier = = ProtobufferTag )
{
FuncInfo . FunctionExportFlags | = FUNCEXPORT_NeedsProto ;
}
else if ( Identifier = = MCPTag )
{
FuncInfo . FunctionExportFlags | = FUNCEXPORT_NeedsMCP ;
}
else if ( Identifier . StartsWith ( IdTag ) )
{
int32 TempInt = FCString : : Atoi ( * Identifier . Mid ( IdTag . Len ( ) ) ) ;
if ( TempInt < = 0 | | TempInt > MAX_uint16 )
{
FError : : Throwf ( TEXT ( " Invalid network identifier %s for function " ) , * Identifier ) ;
}
FuncInfo . RPCId = TempInt ;
}
else if ( Identifier . StartsWith ( ResponseIdTag ) )
{
int32 TempInt = FCString : : Atoi ( * Identifier . Mid ( ResponseIdTag . Len ( ) ) ) ;
if ( TempInt < = 0 | | TempInt > MAX_uint16 )
{
FError : : Throwf ( TEXT ( " Invalid network identifier %s for function " ) , * Identifier ) ;
}
FuncInfo . RPCResponseId = TempInt ;
}
else
{
FError : : Throwf ( TEXT ( " Invalid network identifier %s for function " ) , * Identifier ) ;
}
}
if ( FuncInfo . FunctionExportFlags & FUNCEXPORT_NeedsProto )
{
if ( FuncInfo . RPCId = = 0 )
{
FError : : Throwf ( TEXT ( " net service function does not have an RPCId. " ) ) ;
}
if ( FuncInfo . RPCId = = FuncInfo . RPCResponseId )
{
FError : : Throwf ( TEXT ( " Net service RPCId and ResponseRPCId cannot be the same. " ) ) ;
}
if ( ( FuncInfo . FunctionFlags & FUNC_NetResponse ) & & FuncInfo . RPCResponseId > 0 )
{
FError : : Throwf ( TEXT ( " Net service response functions cannot have a ResponseId. " ) ) ;
}
}
if ( ! ( FuncInfo . FunctionExportFlags & FUNCEXPORT_NeedsProto ) & & ! ( FuncInfo . FunctionExportFlags & FUNCEXPORT_NeedsMCP ) )
{
FError : : Throwf ( TEXT ( " net service function needs to specify at least one provider type. " ) ) ;
}
}
/**
* Processes a set of UFUNCTION or UDELEGATE specifiers into an FFuncInfo struct .
*
* @ param FuncInfo - The FFuncInfo object to populate .
* @ param Specifiers - The specifiers to process .
*/
void ProcessFunctionSpecifiers ( FFuncInfo & FuncInfo , const TArray < FPropertySpecifier > & Specifiers )
{
bool bSpecifiedUnreliable = false ;
for ( const auto & Specifier : Specifiers )
{
if ( Specifier . Key = = TEXT ( " BlueprintNativeEvent " ) )
{
if ( FuncInfo . FunctionFlags & FUNC_Net )
{
FError : : Throwf ( TEXT ( " BlueprintNativeEvent functions cannot be replicated! " ) ) ;
}
else if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) & & ! ( FuncInfo . FunctionFlags & FUNC_Native ) )
{
// already a BlueprintImplementableEvent
FError : : Throwf ( TEXT ( " A function cannot be both BlueprintNativeEvent and BlueprintImplementableEvent! " ) ) ;
}
else if ( ( FuncInfo . FunctionFlags & FUNC_Private ) )
{
FError : : Throwf ( TEXT ( " A Private function cannot be a BlueprintNativeEvent! " ) ) ;
}
FuncInfo . FunctionFlags | = FUNC_Event ;
FuncInfo . FunctionFlags | = FUNC_BlueprintEvent ;
}
else if ( Specifier . Key = = TEXT ( " BlueprintImplementableEvent " ) )
{
if ( FuncInfo . FunctionFlags & FUNC_Net )
{
FError : : Throwf ( TEXT ( " BlueprintImplementableEvent functions cannot be replicated! " ) ) ;
}
else if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) & & ( FuncInfo . FunctionFlags & FUNC_Native ) )
{
// already a BlueprintNativeEvent
FError : : Throwf ( TEXT ( " A function cannot be both BlueprintNativeEvent and BlueprintImplementableEvent! " ) ) ;
}
else if ( ( FuncInfo . FunctionFlags & FUNC_Private ) )
{
FError : : Throwf ( TEXT ( " A Private function cannot be a BlueprintImplementableEvent! " ) ) ;
}
FuncInfo . FunctionFlags | = FUNC_Event ;
FuncInfo . FunctionFlags | = FUNC_BlueprintEvent ;
FuncInfo . FunctionFlags & = ~ FUNC_Native ;
}
else if ( Specifier . Key = = TEXT ( " Exec " ) )
{
FuncInfo . FunctionFlags | = FUNC_Exec ;
if ( FuncInfo . FunctionFlags & FUNC_Net )
{
FError : : Throwf ( TEXT ( " Exec functions cannot be replicated! " ) ) ;
}
}
else if ( Specifier . Key = = TEXT ( " SealedEvent " ) )
{
FuncInfo . bSealedEvent = true ;
}
else if ( Specifier . Key = = TEXT ( " Server " ) )
{
if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) ! = 0 )
{
FError : : Throwf ( TEXT ( " BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as Client or Server " ) ) ;
}
FuncInfo . FunctionFlags | = FUNC_Net ;
FuncInfo . FunctionFlags | = FUNC_NetServer ;
2015-02-06 09:54:41 -05:00
if ( Specifier . Values . Num ( ) )
{
FuncInfo . CppImplName = Specifier . Values [ 0 ] ;
}
2014-03-14 14:13:41 -04:00
if ( FuncInfo . FunctionFlags & FUNC_Exec )
{
FError : : Throwf ( TEXT ( " Exec functions cannot be replicated! " ) ) ;
}
}
else if ( Specifier . Key = = TEXT ( " Client " ) )
{
if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) ! = 0 )
{
FError : : Throwf ( TEXT ( " BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as Client or Server " ) ) ;
}
FuncInfo . FunctionFlags | = FUNC_Net ;
FuncInfo . FunctionFlags | = FUNC_NetClient ;
2015-02-06 09:54:41 -05:00
if ( Specifier . Values . Num ( ) )
{
FuncInfo . CppImplName = Specifier . Values [ 0 ] ;
}
2014-03-14 14:13:41 -04:00
}
else if ( Specifier . Key = = TEXT ( " NetMulticast " ) )
{
if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) ! = 0 )
{
FError : : Throwf ( TEXT ( " BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as Multicast " ) ) ;
}
FuncInfo . FunctionFlags | = FUNC_Net ;
FuncInfo . FunctionFlags | = FUNC_NetMulticast ;
}
else if ( Specifier . Key = = TEXT ( " ServiceRequest " ) )
{
if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) ! = 0 )
{
FError : : Throwf ( TEXT ( " BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as a ServiceRequest " ) ) ;
}
FuncInfo . FunctionFlags | = FUNC_Net ;
FuncInfo . FunctionFlags | = FUNC_NetReliable ;
FuncInfo . FunctionFlags | = FUNC_NetRequest ;
FuncInfo . FunctionExportFlags | = FUNCEXPORT_CustomThunk ;
ParseNetServiceIdentifiers ( FuncInfo , Specifier . Values ) ;
}
else if ( Specifier . Key = = TEXT ( " ServiceResponse " ) )
{
if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) ! = 0 )
{
FError : : Throwf ( TEXT ( " BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as a ServiceResponse " ) ) ;
}
FuncInfo . FunctionFlags | = FUNC_Net ;
FuncInfo . FunctionFlags | = FUNC_NetReliable ;
FuncInfo . FunctionFlags | = FUNC_NetResponse ;
ParseNetServiceIdentifiers ( FuncInfo , Specifier . Values ) ;
}
else if ( Specifier . Key = = TEXT ( " Reliable " ) )
{
FuncInfo . FunctionFlags | = FUNC_NetReliable ;
}
else if ( Specifier . Key = = TEXT ( " Unreliable " ) )
{
bSpecifiedUnreliable = true ;
}
else if ( Specifier . Key = = TEXT ( " CustomThunk " ) )
{
FuncInfo . FunctionExportFlags | = FUNCEXPORT_CustomThunk ;
}
else if ( Specifier . Key = = TEXT ( " BlueprintCallable " ) )
{
FuncInfo . FunctionFlags | = FUNC_BlueprintCallable ;
}
else if ( Specifier . Key = = TEXT ( " BlueprintPure " ) )
{
// This function can be called, and is also pure.
FuncInfo . FunctionFlags | = FUNC_BlueprintCallable ;
FuncInfo . FunctionFlags | = FUNC_BlueprintPure ;
}
else if ( Specifier . Key = = TEXT ( " BlueprintAuthorityOnly " ) )
{
FuncInfo . FunctionFlags | = FUNC_BlueprintAuthorityOnly ;
}
else if ( Specifier . Key = = TEXT ( " BlueprintCosmetic " ) )
{
FuncInfo . FunctionFlags | = FUNC_BlueprintCosmetic ;
}
else if ( Specifier . Key = = TEXT ( " WithValidation " ) )
{
FuncInfo . FunctionFlags | = FUNC_NetValidate ;
2015-02-06 09:54:41 -05:00
if ( Specifier . Values . Num ( ) )
{
FuncInfo . CppValidationImplName = Specifier . Values [ 0 ] ;
}
2014-03-14 14:13:41 -04:00
}
else
{
FError : : Throwf ( TEXT ( " Unknown function specifier '%s' " ) , * Specifier . Key ) ;
}
}
if ( ( FuncInfo . FunctionFlags & FUNC_NetServer ) & & ! ( FuncInfo . FunctionFlags & FUNC_NetValidate ) )
{
FError : : Throwf ( TEXT ( " Server RPC missing 'WithValidation' keyword in the UPROPERTY() declaration statement. Required for security purposes. " ) ) ;
}
if ( FuncInfo . FunctionFlags & FUNC_Net )
{
// Network replicated functions are always events
FuncInfo . FunctionFlags | = FUNC_Event ;
check ( ! ( FuncInfo . FunctionFlags & ( FUNC_BlueprintEvent | FUNC_Exec ) ) ) ;
bool bIsNetService = ! ! ( FuncInfo . FunctionFlags & ( FUNC_NetRequest | FUNC_NetResponse ) ) ;
bool bIsNetReliable = ! ! ( FuncInfo . FunctionFlags & FUNC_NetReliable ) ;
if ( FuncInfo . FunctionFlags & FUNC_Static )
FError : : Throwf ( TEXT ( " Static functions can't be replicated " ) ) ;
if ( ! bIsNetReliable & & ! bSpecifiedUnreliable & & ! bIsNetService )
FError : : Throwf ( TEXT ( " Replicated function: 'reliable' or 'unreliable' is required " ) ) ;
if ( bIsNetReliable & & bSpecifiedUnreliable & & ! bIsNetService )
FError : : Throwf ( TEXT ( " 'reliable' and 'unreliable' are mutually exclusive " ) ) ;
}
else if ( FuncInfo . FunctionFlags & FUNC_NetReliable )
{
FError : : Throwf ( TEXT ( " 'reliable' specified without 'client' or 'server' " ) ) ;
}
else if ( bSpecifiedUnreliable )
{
FError : : Throwf ( TEXT ( " 'unreliable' specified without 'client' or 'server' " ) ) ;
}
if ( FuncInfo . bSealedEvent & & ! ( FuncInfo . FunctionFlags & FUNC_Event ) )
{
FError : : Throwf ( TEXT ( " SealedEvent may only be used on events " ) ) ;
}
2014-09-03 10:46:58 -04:00
if ( FuncInfo . bSealedEvent & & FuncInfo . FunctionFlags & FUNC_BlueprintEvent )
{
FError : : Throwf ( TEXT ( " SealedEvent cannot be used on Blueprint events " ) ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-09-24 09:09:32 -04:00
void AddEditInlineMetaData ( TMap < FName , FString > & MetaData )
{
MetaData . Add ( TEXT ( " EditInline " ) , TEXT ( " true " ) ) ;
}
2015-03-19 13:27:48 -04:00
const TCHAR * GetHintText ( EVariableCategory : : Type VariableCategory )
{
switch ( VariableCategory )
{
case EVariableCategory : : ReplicatedParameter :
case EVariableCategory : : RegularParameter :
return TEXT ( " Function parameter " ) ;
case EVariableCategory : : Return :
return TEXT ( " Function return type " ) ;
case EVariableCategory : : Member :
return TEXT ( " Member variable declaration " ) ;
default :
FError : : Throwf ( TEXT ( " Unknown variable category " ) ) ;
}
// Unreachable
2015-03-31 07:03:34 -04:00
check ( false ) ;
return nullptr ;
}
// Check to see if anything in the class hierarchy passed in has CLASS_DefaultToInstanced
bool DoesAnythingInHierarchyHaveDefaultToInstanced ( UClass * TestClass )
{
bool bDefaultToInstanced = false ;
UClass * Search = TestClass ;
while ( ! bDefaultToInstanced & & ( Search ! = NULL ) )
{
bDefaultToInstanced = Search - > HasAnyClassFlags ( CLASS_DefaultToInstanced ) ;
if ( ! bDefaultToInstanced & & ! Search - > HasAnyClassFlags ( CLASS_Intrinsic | CLASS_Parsed ) )
{
// The class might not have been parsed yet, look for declaration data.
auto ClassDeclarationDataPtr = GClassDeclarations . Find ( Search - > GetFName ( ) ) ;
if ( ClassDeclarationDataPtr )
{
bDefaultToInstanced = ! ! ( ( * ClassDeclarationDataPtr ) - > ClassFlags & CLASS_DefaultToInstanced ) ;
}
}
Search = Search - > GetSuperClass ( ) ;
}
return bDefaultToInstanced ;
}
UProperty * CreateVariableProperty ( FPropertyBase & VarProperty , UObject * Scope , FName Name , EObjectFlags ObjectFlags , EVariableCategory : : Type VariableCategory )
{
switch ( VarProperty . Type )
{
case CPT_Byte :
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UByteProperty ( FObjectInitializer ( ) ) ;
Result - > Enum = VarProperty . Enum ;
return Result ;
}
case CPT_Int8 :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UInt8Property ( FObjectInitializer ( ) ) ;
case CPT_Int16 :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UInt16Property ( FObjectInitializer ( ) ) ;
case CPT_Int :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UIntProperty ( FObjectInitializer ( ) ) ;
case CPT_Int64 :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UInt64Property ( FObjectInitializer ( ) ) ;
case CPT_UInt16 :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UUInt16Property ( FObjectInitializer ( ) ) ;
case CPT_UInt32 :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UUInt32Property ( FObjectInitializer ( ) ) ;
case CPT_UInt64 :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UUInt64Property ( FObjectInitializer ( ) ) ;
case CPT_Bool :
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UBoolProperty ( FObjectInitializer ( ) ) ;
Result - > SetBoolSize ( sizeof ( bool ) , true ) ;
return Result ;
}
case CPT_Bool8 :
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UBoolProperty ( FObjectInitializer ( ) ) ;
Result - > SetBoolSize ( ( VariableCategory = = EVariableCategory : : Return ) ? sizeof ( bool ) : sizeof ( uint8 ) , VariableCategory = = EVariableCategory : : Return ) ;
return Result ;
}
case CPT_Bool16 :
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UBoolProperty ( FObjectInitializer ( ) ) ;
Result - > SetBoolSize ( ( VariableCategory = = EVariableCategory : : Return ) ? sizeof ( bool ) : sizeof ( uint16 ) , VariableCategory = = EVariableCategory : : Return ) ;
return Result ;
}
case CPT_Bool32 :
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UBoolProperty ( FObjectInitializer ( ) ) ;
Result - > SetBoolSize ( ( VariableCategory = = EVariableCategory : : Return ) ? sizeof ( bool ) : sizeof ( uint32 ) , VariableCategory = = EVariableCategory : : Return ) ;
return Result ;
}
case CPT_Bool64 :
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UBoolProperty ( FObjectInitializer ( ) ) ;
Result - > SetBoolSize ( ( VariableCategory = = EVariableCategory : : Return ) ? sizeof ( bool ) : sizeof ( uint64 ) , VariableCategory = = EVariableCategory : : Return ) ;
return Result ;
}
case CPT_Float :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UFloatProperty ( FObjectInitializer ( ) ) ;
case CPT_Double :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UDoubleProperty ( FObjectInitializer ( ) ) ;
case CPT_ObjectReference :
check ( VarProperty . PropertyClass ) ;
if ( VarProperty . PropertyClass - > IsChildOf ( UClass : : StaticClass ( ) ) )
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UClassProperty ( FObjectInitializer ( ) ) ;
Result - > MetaClass = VarProperty . MetaClass ;
Result - > PropertyClass = VarProperty . PropertyClass ;
return Result ;
}
else
{
if ( DoesAnythingInHierarchyHaveDefaultToInstanced ( VarProperty . PropertyClass ) )
{
VarProperty . PropertyFlags | = CPF_InstancedReference ;
AddEditInlineMetaData ( VarProperty . MetaData ) ;
}
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UObjectProperty ( FObjectInitializer ( ) ) ;
Result - > PropertyClass = VarProperty . PropertyClass ;
return Result ;
}
case CPT_WeakObjectReference :
{
check ( VarProperty . PropertyClass ) ;
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UWeakObjectProperty ( FObjectInitializer ( ) ) ;
Result - > PropertyClass = VarProperty . PropertyClass ;
return Result ;
}
case CPT_LazyObjectReference :
{
check ( VarProperty . PropertyClass ) ;
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) ULazyObjectProperty ( FObjectInitializer ( ) ) ;
Result - > PropertyClass = VarProperty . PropertyClass ;
return Result ;
}
case CPT_AssetObjectReference :
check ( VarProperty . PropertyClass ) ;
if ( VarProperty . PropertyClass - > IsChildOf ( UClass : : StaticClass ( ) ) )
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UAssetClassProperty ( FObjectInitializer ( ) ) ;
Result - > MetaClass = VarProperty . MetaClass ;
Result - > PropertyClass = VarProperty . PropertyClass ;
return Result ;
}
else
{
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UAssetObjectProperty ( FObjectInitializer ( ) ) ;
Result - > PropertyClass = VarProperty . PropertyClass ;
return Result ;
}
case CPT_Interface :
{
check ( VarProperty . PropertyClass ) ;
check ( VarProperty . PropertyClass - > HasAnyClassFlags ( CLASS_Interface ) ) ;
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UInterfaceProperty ( FObjectInitializer ( ) ) ;
Result - > InterfaceClass = VarProperty . PropertyClass ;
return Result ;
}
case CPT_Name :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UNameProperty ( FObjectInitializer ( ) ) ;
case CPT_String :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UStrProperty ( FObjectInitializer ( ) ) ;
case CPT_Text :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UTextProperty ( FObjectInitializer ( ) ) ;
case CPT_Struct :
{
if ( VarProperty . Struct - > StructFlags & STRUCT_HasInstancedReference )
{
VarProperty . PropertyFlags | = CPF_ContainsInstancedReference ;
}
auto * Result = new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UStructProperty ( FObjectInitializer ( ) ) ;
Result - > Struct = VarProperty . Struct ;
return Result ;
}
case CPT_Delegate :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UDelegateProperty ( FObjectInitializer ( ) ) ;
case CPT_MulticastDelegate :
return new ( EC_InternalUseOnlyConstructor , Scope , Name , ObjectFlags ) UMulticastDelegateProperty ( FObjectInitializer ( ) ) ;
default :
FError : : Throwf ( TEXT ( " Unknown property type %i " ) , ( uint8 ) VarProperty . Type ) ;
}
// Unreachable
check ( false ) ;
2015-03-19 13:27:48 -04:00
return nullptr ;
}
2015-03-27 15:02:56 -04:00
2015-03-31 07:20:11 -04:00
/**
* Ensures at script compile time that the metadata formatting is correct
* @ param InKey the metadata key being added
* @ param InValue the value string that will be associated with the InKey
*/
void ValidateMetaDataFormat ( UField * Field , const FString & InKey , const FString & InValue )
{
if ( ( InKey = = TEXT ( " UIMin " ) ) | | ( InKey = = TEXT ( " UIMax " ) ) | | ( InKey = = TEXT ( " ClampMin " ) ) | | ( InKey = = TEXT ( " ClampMax " ) ) )
{
if ( ! InValue . IsNumeric ( ) )
{
FError : : Throwf ( TEXT ( " Metadata value for '%s' is non-numeric : '%s' " ) , * InKey , * InValue ) ;
}
}
else if ( InKey = = /*FBlueprintMetadata::MD_Protected*/ TEXT ( " BlueprintProtected " ) )
{
if ( UFunction * Function = Cast < UFunction > ( Field ) )
{
if ( Function - > HasAnyFunctionFlags ( FUNC_Static ) )
{
// Determine if it's a function library
UClass * Class = Cast < UClass > ( Function - > GetOuterUClass ( ) ) ;
while ( Class ! = nullptr & & Class - > GetSuperClass ( ) ! = UObject : : StaticClass ( ) )
{
Class = Class - > GetSuperClass ( ) ;
}
if ( Class ! = nullptr & & Class - > GetName ( ) = = TEXT ( " BlueprintFunctionLibrary " ) )
{
FError : : Throwf ( TEXT ( " %s doesn't make sense on static method '%s' in a blueprint function library " ) , * InKey , * Function - > GetName ( ) ) ;
}
}
}
}
else if ( InKey = = TEXT ( " DevelopmentStatus " ) )
{
const FString EarlyAccessValue ( TEXT ( " EarlyAccess " ) ) ;
const FString ExperimentalValue ( TEXT ( " Experimental " ) ) ;
if ( ( InValue ! = EarlyAccessValue ) & & ( InValue ! = ExperimentalValue ) )
{
FError : : Throwf ( TEXT ( " '%s' metadata was '%s' but it must be %s or %s " ) , * InKey , * InValue , * ExperimentalValue , * EarlyAccessValue ) ;
}
}
else if ( InKey = = TEXT ( " Units " ) )
{
// Check for numeric property
if ( ! Cast < UNumericProperty > ( Field ) )
{
FError : : Throwf ( TEXT ( " 'Units' meta data can only be applied to numeric properties " ) ) ;
}
else if ( ! FUnitConversion : : UnitFromString ( * InValue ) )
{
FError : : Throwf ( TEXT ( " Unrecognized units (%s) specified for numeric property '%s' " ) , * InValue , * Field - > GetDisplayNameText ( ) . ToString ( ) ) ;
}
}
}
// Ensures at script compile time that the metadata formatting is correct
void ValidateMetaDataFormat ( UField * Field , const TMap < FName , FString > & MetaData )
{
for ( const auto & Pair : MetaData )
{
ValidateMetaDataFormat ( Field , Pair . Key . ToString ( ) , Pair . Value ) ;
}
}
// Validates the metadata, then adds it to the class data
void AddMetaDataToClassData ( UField * Field , const TMap < FName , FString > & InMetaData )
{
// Evaluate any key redirects on the passed in pairs
TMap < FName , FString > RemappedPairs ;
RemappedPairs . Empty ( InMetaData . Num ( ) ) ;
for ( const auto & Pair : InMetaData )
{
FName CurrentKey = Pair . Key ;
FName NewKey = UMetaData : : GetRemappedKeyName ( CurrentKey ) ;
if ( NewKey ! = NAME_None )
{
UE_LOG ( LogCompile , Warning , TEXT ( " Remapping old metadata key '%s' to new key '%s', please update the declaration. " ) , * CurrentKey . ToString ( ) , * NewKey . ToString ( ) ) ;
CurrentKey = NewKey ;
}
RemappedPairs . Add ( CurrentKey , Pair . Value ) ;
}
// Finish validating and associate the metadata with the field
ValidateMetaDataFormat ( Field , RemappedPairs ) ;
FClassMetaData : : AddMetaData ( Field , RemappedPairs ) ;
}
2015-04-01 20:59:46 -04:00
bool IsPropertySupportedByBlueprint ( const UProperty * Property , bool bMemberVariable )
2015-03-27 15:02:56 -04:00
{
if ( Property = = NULL )
{
return false ;
}
if ( auto ArrayProperty = Cast < const UArrayProperty > ( Property ) )
{
2015-04-01 20:59:46 -04:00
// Inner Property can be handled as a member variable
return IsPropertySupportedByBlueprint ( ArrayProperty - > Inner , true ) ;
2015-03-27 15:02:56 -04:00
}
const bool bSupportedType = Property - > IsA < UInterfaceProperty > ( )
| | Property - > IsA < UClassProperty > ( )
2015-04-01 20:59:46 -04:00
| | Property - > IsA < UObjectProperty > ( )
2015-03-27 15:02:56 -04:00
| | Property - > IsA < UStructProperty > ( )
| | Property - > IsA < UFloatProperty > ( )
| | Property - > IsA < UIntProperty > ( )
| | Property - > IsA < UByteProperty > ( )
| | Property - > IsA < UNameProperty > ( )
| | Property - > IsA < UBoolProperty > ( )
| | Property - > IsA < UStrProperty > ( )
| | Property - > IsA < UTextProperty > ( )
| | Property - > IsA < UMulticastDelegateProperty > ( )
| | Property - > IsA < UDelegateProperty > ( ) ;
2015-04-01 20:59:46 -04:00
const bool bIsSupportedMemberVariable = Property - > IsA < UObjectPropertyBase > ( ) ;
return bSupportedType | | ( bIsSupportedMemberVariable & & bMemberVariable ) ;
2015-03-27 15:02:56 -04:00
}
2014-03-14 14:13:41 -04:00
}
/////////////////////////////////////////////////////
// FScriptLocation
FHeaderParser * FScriptLocation : : Compiler = NULL ;
FScriptLocation : : FScriptLocation ( )
{
if ( Compiler ! = NULL )
{
Compiler - > InitScriptLocation ( * this ) ;
}
}
/////////////////////////////////////////////////////
// FHeaderParser
FString FHeaderParser : : GetContext ( )
{
2015-01-20 09:33:54 -05:00
auto * FileScope = GetCurrentFileScope ( ) ;
2014-03-14 14:13:41 -04:00
2015-04-22 14:15:56 -04:00
FString ScopeFilename = IFileManager : : Get ( ) . ConvertToAbsolutePathForExternalAppForRead ( * FileScope - > GetSourceFile ( ) - > GetFilename ( ) ) ;
2014-03-14 14:13:41 -04:00
2015-04-22 14:15:56 -04:00
return FString : : Printf ( TEXT ( " %s(%i) " ) , * ScopeFilename , InputLine ) ;
2014-03-14 14:13:41 -04:00
}
/*-----------------------------------------------------------------------------
Code emitting .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// Get a qualified class.
//
2014-04-23 18:42:01 -04:00
FClass * FHeaderParser : : GetQualifiedClass ( const FClasses & AllClasses , const TCHAR * Thing )
2014-03-14 14:13:41 -04:00
{
TCHAR ClassName [ 256 ] = TEXT ( " " ) ;
FToken Token ;
if ( GetIdentifier ( Token ) )
{
FCString : : Strncat ( ClassName , Token . Identifier , ARRAY_COUNT ( ClassName ) ) ;
}
if ( ! ClassName [ 0 ] )
{
FError : : Throwf ( TEXT ( " %s: Missing class name " ) , Thing ) ;
}
2014-04-23 18:42:01 -04:00
return AllClasses . FindScriptClassOrThrow ( ClassName ) ;
2014-03-14 14:13:41 -04:00
}
/*-----------------------------------------------------------------------------
Fields .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Find a field in the specified context . Starts with the specified scope , then iterates
* through the Outer chain until the field is found .
*
* @ param InScope scope to start searching for the field in
* @ param InIdentifier name of the field we ' re searching for
* @ param bIncludeParents whether to allow searching in the scope of a parent struct
* @ param FieldClass class of the field to search for . used to e . g . search for functions only
* @ param Thing hint text that will be used in the error message if an error is encountered
*
* @ return a pointer to a UField with a name matching InIdentifier , or NULL if it wasn ' t found
*/
UField * FHeaderParser : : FindField
(
UStruct * Scope ,
const TCHAR * InIdentifier ,
bool bIncludeParents ,
UClass * FieldClass ,
const TCHAR * Thing
)
{
check ( InIdentifier ) ;
FName InName ( InIdentifier , FNAME_Find , true ) ;
if ( InName ! = NAME_None )
{
2014-04-02 18:09:23 -04:00
for ( ; Scope ; Scope = Cast < UStruct > ( Scope - > GetOuter ( ) ) )
2014-03-14 14:13:41 -04:00
{
for ( TFieldIterator < UField > It ( Scope ) ; It ; + + It )
{
if ( It - > GetFName ( ) = = InName )
{
if ( ! It - > IsA ( FieldClass ) )
{
if ( Thing )
{
FError : : Throwf ( TEXT ( " %s: expecting %s, got %s " ) , Thing , * FieldClass - > GetName ( ) , * It - > GetClass ( ) - > GetName ( ) ) ;
}
return NULL ;
}
return * It ;
}
}
if ( ! bIncludeParents )
{
break ;
}
}
}
return NULL ;
}
/**
* @ return true if Scope has UProperty objects in its list of fields
*/
bool FHeaderParser : : HasMemberProperties ( const UStruct * Scope )
{
// it's safe to pass a NULL Scope to TFieldIterator, but this function shouldn't be called with a NULL Scope
checkSlow ( Scope ) ;
TFieldIterator < UProperty > It ( Scope , EFieldIteratorFlags : : ExcludeSuper ) ;
return It ? true : false ;
}
/**
* Get the parent struct specified .
*
* @ param CurrentScope scope to start in
* @ param SearchName parent scope to search for
*
* @ return a pointer to the parent struct with the specified name , or NULL if the parent couldn ' t be found
*/
UStruct * FHeaderParser : : GetSuperScope ( UStruct * CurrentScope , const FName & SearchName )
{
UStruct * SuperScope = CurrentScope ;
while ( SuperScope & & ! SuperScope - > GetInheritanceSuper ( ) )
{
SuperScope = CastChecked < UStruct > ( SuperScope - > GetOuter ( ) ) ;
}
if ( SuperScope ! = NULL )
{
// iterate up the inheritance chain looking for one that has the desired name
do
{
UStruct * NextScope = SuperScope - > GetInheritanceSuper ( ) ;
if ( NextScope )
{
SuperScope = NextScope ;
}
else
{
// otherwise we've failed
SuperScope = NULL ;
}
} while ( SuperScope ! = NULL & & SuperScope - > GetFName ( ) ! = SearchName ) ;
}
return SuperScope ;
}
2015-01-20 09:33:54 -05:00
/**
* Adds source file ' s include path to given metadata .
*
* @ param Type Type for which to add include path .
* @ param MetaData Meta data to fill the information .
*/
void AddIncludePathToMetadata ( UField * Type , TMap < FName , FString > & MetaData )
{
// Add metadata for the include path.
auto * TypeDefinitionPtr = GTypeDefinitionInfoMap . Find ( Type ) ;
if ( TypeDefinitionPtr ! = nullptr )
{
MetaData . Add ( TEXT ( " IncludePath " ) , * ( * TypeDefinitionPtr ) - > GetUnrealSourceFile ( ) . GetIncludePath ( ) ) ;
}
}
/**
* Adds module ' s relative path from given file .
*
* @ param SourceFile Given source file .
* @ param MetaData Meta data to fill the information .
*/
void AddModuleRelativePathToMetadata ( FUnrealSourceFile & SourceFile , TMap < FName , FString > & MetaData )
{
MetaData . Add ( TEXT ( " ModuleRelativePath " ) , * SourceFile . GetModuleRelativePath ( ) ) ;
}
/**
* Adds module ' s relative path to given metadata .
*
* @ param Type Type for which to add module ' s relative path .
* @ param MetaData Meta data to fill the information .
*/
void AddModuleRelativePathToMetadata ( UField * Type , TMap < FName , FString > & MetaData )
{
// Add metadata for the module relative path.
auto * TypeDefinitionPtr = GTypeDefinitionInfoMap . Find ( Type ) ;
if ( TypeDefinitionPtr ! = nullptr )
{
MetaData . Add ( TEXT ( " ModuleRelativePath " ) , * ( * TypeDefinitionPtr ) - > GetUnrealSourceFile ( ) . GetModuleRelativePath ( ) ) ;
}
}
2014-03-14 14:13:41 -04:00
/*-----------------------------------------------------------------------------
Variables .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// Compile an enumeration definition.
//
2015-01-20 09:33:54 -05:00
UEnum * FHeaderParser : : CompileEnum ( FUnrealSourceFile & SourceFile )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
auto Scope = SourceFile . GetScope ( ) ;
2014-03-14 14:13:41 -04:00
CheckAllow ( TEXT ( " 'Enum' " ) , ALLOW_TypeDecl ) ;
// Get the enum specifier list
FToken EnumToken ;
TArray < FPropertySpecifier > SpecifiersFound ;
ReadSpecifierSetInsideMacro ( SpecifiersFound , TEXT ( " Enum " ) , EnumToken . MetaData ) ;
FScriptLocation DeclarationPosition ;
2014-08-12 08:51:25 -04:00
// Check enum type. This can be global 'enum', 'namespace' or 'enum class' enums.
bool bReadEnumName = false ;
UEnum : : ECppForm CppForm = UEnum : : ECppForm : : Regular ;
2014-03-14 14:13:41 -04:00
if ( ! GetIdentifier ( EnumToken ) )
{
2014-08-12 08:51:25 -04:00
FError : : Throwf ( TEXT ( " Missing identifier after UENUM() " ) ) ;
}
if ( EnumToken . Matches ( TEXT ( " namespace " ) , ESearchCase : : CaseSensitive ) )
{
CppForm = UEnum : : ECppForm : : Namespaced ;
bReadEnumName = GetIdentifier ( EnumToken ) ;
}
else if ( EnumToken . Matches ( TEXT ( " enum " ) , ESearchCase : : CaseSensitive ) )
{
if ( ! GetIdentifier ( EnumToken ) )
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
FError : : Throwf ( TEXT ( " Missing identifier after enum " ) ) ;
}
if ( EnumToken . Matches ( TEXT ( " class " ) , ESearchCase : : CaseSensitive ) | | EnumToken . Matches ( TEXT ( " struct " ) , ESearchCase : : CaseSensitive ) )
{
CppForm = UEnum : : ECppForm : : EnumClass ;
bReadEnumName = GetIdentifier ( EnumToken ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-08-12 08:51:25 -04:00
CppForm = UEnum : : ECppForm : : Regular ;
bReadEnumName = true ;
2014-03-14 14:13:41 -04:00
}
}
2014-08-12 08:51:25 -04:00
else
{
FError : : Throwf ( TEXT ( " UENUM() should be followed by \' enum \' or \' namespace \' keywords. " ) ) ;
}
// Get enumeration name.
if ( ! bReadEnumName )
{
FError : : Throwf ( TEXT ( " Missing enumeration name " ) ) ;
}
2014-03-14 14:13:41 -04:00
// Verify that the enumeration definition is unique within this scope.
2015-01-20 09:33:54 -05:00
auto * Existing = Scope - > FindTypeByName ( EnumToken . Identifier ) ;
if ( Existing )
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
FError : : Throwf ( TEXT ( " enum: '%s' already defined here " ) , * EnumToken . TokenName . ToString ( ) ) ;
2014-03-14 14:13:41 -04:00
}
ParseFieldMetaData ( EnumToken . MetaData , EnumToken . Identifier ) ;
// Create enum definition.
2015-01-30 08:30:03 -05:00
UEnum * Enum = new ( EC_InternalUseOnlyConstructor , SourceFile . GetPackage ( ) , EnumToken . Identifier , RF_Public ) UEnum ( FObjectInitializer ( ) ) ;
2015-01-20 09:33:54 -05:00
Scope - > AddType ( Enum ) ;
2014-03-14 14:13:41 -04:00
2015-02-20 15:37:38 -05:00
AddTypeDefinition ( SourceFile , Enum , InputLine ) ;
2014-03-14 14:13:41 -04:00
// Validate the metadata for the enum
ValidateMetaDataFormat ( Enum , EnumToken . MetaData ) ;
2014-08-12 08:51:25 -04:00
// Read optional base for enum class
if ( CppForm = = UEnum : : ECppForm : : EnumClass & & MatchSymbol ( TEXT ( " : " ) ) )
{
FToken BaseToken ;
if ( ! GetIdentifier ( BaseToken ) )
{
FError : : Throwf ( TEXT ( " Missing enum base " ) ) ;
}
// We only support uint8 at the moment, until the properties get updated
if ( FCString : : Strcmp ( BaseToken . Identifier , TEXT ( " uint8 " ) ) )
{
FError : : Throwf ( TEXT ( " Only enum bases of type uint8 are currently supported " ) ) ;
}
GEnumUnderlyingTypes . Add ( Enum , CPT_Byte ) ;
}
2014-03-14 14:13:41 -04:00
// Get opening brace.
RequireSymbol ( TEXT ( " { " ) , TEXT ( " 'Enum' " ) ) ;
2014-08-12 08:51:25 -04:00
switch ( CppForm )
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
case UEnum : : ECppForm : : Namespaced :
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
// Now handle the inner true enum portion
RequireIdentifier ( TEXT ( " enum " ) , TEXT ( " 'Enum' " ) ) ;
2014-03-14 14:13:41 -04:00
2014-08-12 08:51:25 -04:00
FToken InnerEnumToken ;
if ( ! GetIdentifier ( InnerEnumToken ) )
{
FError : : Throwf ( TEXT ( " Missing enumeration name " ) ) ;
}
Enum - > CppType = FString : : Printf ( TEXT ( " %s::%s " ) , EnumToken . Identifier , InnerEnumToken . Identifier ) ;
RequireSymbol ( TEXT ( " { " ) , TEXT ( " 'Enum' " ) ) ;
}
break ;
case UEnum : : ECppForm : : Regular :
case UEnum : : ECppForm : : EnumClass :
{
Enum - > CppType = EnumToken . Identifier ;
}
break ;
2014-03-14 14:13:41 -04:00
}
// List of all metadata generated for this enum
TMap < FName , FString > EnumValueMetaData = EnumToken . MetaData ;
2015-01-20 09:33:54 -05:00
AddModuleRelativePathToMetadata ( Enum , EnumValueMetaData ) ;
2014-03-14 14:13:41 -04:00
AddFormattedPrevCommentAsTooltipMetaData ( EnumValueMetaData ) ;
// Parse all enums tags.
FToken TagToken ;
TArray < FScriptLocation > EnumTagLocations ;
TArray < FName > EnumNames ;
int32 CurrentEnumValue = 0 ;
while ( GetIdentifier ( TagToken ) )
{
AddFormattedPrevCommentAsTooltipMetaData ( TagToken . MetaData ) ;
FScriptLocation * ValueDeclarationPos = new ( EnumTagLocations ) FScriptLocation ( ) ;
// Try to read an optional explicit enum value specification
if ( MatchSymbol ( TEXT ( " = " ) ) )
{
int32 NewEnumValue = 0 ;
GetConstInt ( /*out*/ NewEnumValue , TEXT ( " Enumerator value " ) ) ;
if ( ( NewEnumValue < CurrentEnumValue ) | | ( NewEnumValue > 255 ) )
{
FError : : Throwf ( TEXT ( " Explicitly specified enum values must be greater than any previous value and less than 256 " ) ) ;
}
CurrentEnumValue = NewEnumValue ;
}
int32 iFound ;
FName NewTag ;
2014-08-12 08:51:25 -04:00
switch ( CppForm )
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
case UEnum : : ECppForm : : Namespaced :
case UEnum : : ECppForm : : EnumClass :
{
NewTag = FName ( * FString : : Printf ( TEXT ( " %s::%s " ) , EnumToken . Identifier , TagToken . Identifier ) , FNAME_Add , true ) ;
}
break ;
case UEnum : : ECppForm : : Regular :
{
NewTag = FName ( TagToken . Identifier , FNAME_Add , true ) ;
}
break ;
2014-03-14 14:13:41 -04:00
}
2014-08-12 08:51:25 -04:00
2014-03-14 14:13:41 -04:00
if ( EnumNames . Find ( NewTag , iFound ) )
{
FError : : Throwf ( TEXT ( " Duplicate enumeration tag %s " ) , TagToken . Identifier ) ;
}
2014-08-12 08:51:25 -04:00
if ( CurrentEnumValue > 255 )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Exceeded maximum of 255 enumerators " ) ) ;
}
2014-08-12 08:51:25 -04:00
UEnum * FoundEnum = NULL ;
if ( UEnum : : LookupEnumName ( NewTag , & FoundEnum ) ! = INDEX_NONE )
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
FError : : Throwf ( TEXT ( " Enumeration tag '%s' already in use by enum '%s' " ) , TagToken . Identifier , * FoundEnum - > GetPathName ( ) ) ;
2014-03-14 14:13:41 -04:00
}
2014-08-12 08:51:25 -04:00
// Make sure the enum names array is tightly packed by inserting dummies
//@TODO: UCREMOVAL: Improve the UEnum system so we can have loosely packed values for e.g., bitfields
for ( int32 DummyIndex = EnumNames . Num ( ) ; DummyIndex < CurrentEnumValue ; + + DummyIndex )
{
FString DummyName = FString : : Printf ( TEXT ( " UnusedSpacer_%d " ) , DummyIndex ) ;
FString DummyNameWithQualifier = FString : : Printf ( TEXT ( " %s::%s " ) , EnumToken . Identifier , * DummyName ) ;
EnumNames . Add ( FName ( * DummyNameWithQualifier ) ) ;
// These ternary operators are the correct way around, believe it or not.
// Spacers are qualified with the ETheEnum:: when they're regular enums in order to prevent spacer name clashes.
// They're not qualified when they're actually in a namespace or are enum classes.
InsertMetaDataPair ( EnumValueMetaData , ( ( CppForm ! = UEnum : : ECppForm : : Regular ) ? DummyName : DummyNameWithQualifier ) + TEXT ( " .Hidden " ) , TEXT ( " " ) ) ;
InsertMetaDataPair ( EnumValueMetaData , ( ( CppForm ! = UEnum : : ECppForm : : Regular ) ? DummyName : DummyNameWithQualifier ) + TEXT ( " .Spacer " ) , TEXT ( " " ) ) ;
}
// Save the new tag
EnumNames . Add ( NewTag ) ;
// Autoincrement the current enumerant value
CurrentEnumValue + + ;
2014-03-14 14:13:41 -04:00
// check for metadata on this enum value
ParseFieldMetaData ( TagToken . MetaData , TagToken . Identifier ) ;
if ( TagToken . MetaData . Num ( ) > 0 )
{
// special case for enum value metadata - we need to prepend the key name with the enum value name
const FString TokenString = TagToken . Identifier ;
for ( const auto & MetaData : TagToken . MetaData )
{
FString KeyString = TokenString + TEXT ( " . " ) + MetaData . Key . ToString ( ) ;
EnumValueMetaData . Add ( FName ( * KeyString ) , MetaData . Value ) ;
}
// now clear the metadata because we're going to reuse this token for parsing the next enum value
TagToken . MetaData . Empty ( ) ;
}
if ( ! MatchSymbol ( TEXT ( " , " ) ) )
{
break ;
}
}
// Add the metadata gathered for the enum to the package
if ( EnumValueMetaData . Num ( ) > 0 )
{
UMetaData * PackageMetaData = Enum - > GetOutermost ( ) - > GetMetaData ( ) ;
checkSlow ( PackageMetaData ) ;
PackageMetaData - > SetObjectValues ( Enum , EnumValueMetaData ) ;
}
if ( ! EnumNames . Num ( ) )
{
FError : : Throwf ( TEXT ( " Enumeration must contain at least one enumerator " ) ) ;
}
// Trailing brace and semicolon for the enum
RequireSymbol ( TEXT ( " } " ) , TEXT ( " 'Enum' " ) ) ;
MatchSemi ( ) ;
2014-08-12 08:51:25 -04:00
if ( CppForm = = UEnum : : ECppForm : : Namespaced )
2014-03-14 14:13:41 -04:00
{
// Trailing brace for the namespace.
RequireSymbol ( TEXT ( " } " ) , TEXT ( " 'Enum' " ) ) ;
}
// Register the list of enum names.
2014-08-12 08:51:25 -04:00
if ( ! Enum - > SetEnums ( EnumNames , CppForm ) )
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
const FName MaxEnumItem = * ( Enum - > GenerateEnumPrefix ( ) + TEXT ( " _MAX " ) ) ;
2014-03-14 14:13:41 -04:00
const int32 MaxEnumItemIndex = Enum - > FindEnumIndex ( MaxEnumItem ) ;
if ( MaxEnumItemIndex ! = INDEX_NONE )
{
ReturnToLocation ( EnumTagLocations [ MaxEnumItemIndex ] , false , true ) ;
FError : : Throwf ( TEXT ( " Illegal enumeration tag specified. Conflicts with auto-generated tag '%s' " ) , * MaxEnumItem . ToString ( ) ) ;
}
2014-08-12 08:51:25 -04:00
FError : : Throwf ( TEXT ( " Unable to generate enum MAX entry '%s' due to name collision " ) , * MaxEnumItem . ToString ( ) ) ;
2014-03-14 14:13:41 -04:00
}
return Enum ;
}
/**
* Checks if a string is made up of all the same character .
*
* @ param Str The string to check for all
* @ param Ch The character to check for
*
* @ return True if the string is made up only of Ch characters .
*/
bool IsAllSameChar ( const TCHAR * Str , TCHAR Ch )
{
check ( Str ) ;
while ( TCHAR StrCh = * Str + + )
{
if ( StrCh ! = Ch )
return false ;
}
return true ;
}
/**
* Checks if a string is made up of all the same character .
*
* @ param Str The string to check for all
* @ param Ch The character to check for
*
* @ return True if the string is made up only of Ch characters .
*/
bool IsLineSeparator ( const TCHAR * Str )
{
check ( Str ) ;
return IsAllSameChar ( Str , TEXT ( ' - ' ) ) | | IsAllSameChar ( Str , TEXT ( ' = ' ) ) | | IsAllSameChar ( Str , TEXT ( ' * ' ) ) ;
}
/**
* @ param Input An input string , expected to be a script comment .
* @ return The input string , reformatted in such a way as to be appropriate for use as a tooltip .
*/
FString FHeaderParser : : FormatCommentForToolTip ( const FString & Input )
{
// Return an empty string if there are no alpha-numeric characters or a Unicode characters above 0xFF
// (which would be the case for pure CJK comments) in the input string.
bool bFoundAlphaNumericChar = false ;
for ( int32 i = 0 ; i < Input . Len ( ) ; + + i )
{
if ( FChar : : IsAlnum ( Input [ i ] ) | | ( Input [ i ] > 0xFF ) )
{
bFoundAlphaNumericChar = true ;
break ;
}
}
if ( ! bFoundAlphaNumericChar )
{
return FString ( TEXT ( " " ) ) ;
}
// Check for known commenting styles.
FString Result ( Input ) ;
const bool bJavaDocStyle = Input . Contains ( TEXT ( " /** " ) ) ;
const bool bCStyle = Input . Contains ( TEXT ( " /* " ) ) ;
const bool bCPPStyle = Input . StartsWith ( TEXT ( " // " ) ) ;
if ( bJavaDocStyle | | bCStyle )
{
// Remove beginning and end markers.
Result = Result . Replace ( TEXT ( " /** " ) , TEXT ( " " ) ) ;
Result = Result . Replace ( TEXT ( " /* " ) , TEXT ( " " ) ) ;
Result = Result . Replace ( TEXT ( " */ " ) , TEXT ( " " ) ) ;
}
if ( bCPPStyle )
{
// Remove c++-style comment markers. Also handle javadoc-style comments by replacing
// all triple slashes with double-slashes
Result = Result . Replace ( TEXT ( " /// " ) , TEXT ( " // " ) ) . Replace ( TEXT ( " // " ) , TEXT ( " " ) ) ;
// Parser strips cpptext and replaces it with "// (cpptext)" -- prevent
// this from being treated as a comment on variables declared below the
// cpptext section
Result = Result . Replace ( TEXT ( " (cpptext) " ) , TEXT ( " " ) ) ;
}
// Get rid of carriage return or tab characters, which mess up tooltips.
Result = Result . Replace ( TEXT ( " \r " ) , TEXT ( " " ) ) ;
//wx widgets has a hard coded tab size of 8
{
const int32 SpacesPerTab = 8 ;
Result = Result . ConvertTabsToSpaces ( SpacesPerTab ) ;
}
// get rid of uniform leading whitespace and all trailing whitespace, on each line
TArray < FString > Lines ;
2015-03-02 15:51:37 -05:00
Result . ParseIntoArray ( Lines , TEXT ( " \n " ) , false ) ;
2014-03-14 14:13:41 -04:00
for ( auto & Line : Lines )
{
2014-10-01 13:24:30 -04:00
// Remove trailing whitespace
2014-03-14 14:13:41 -04:00
Line . TrimTrailing ( ) ;
2014-10-01 13:24:30 -04:00
// Remove leading "*" and "* " in javadoc comments.
if ( bJavaDocStyle )
{
// Find first non-whitespace character
int32 Pos = 0 ;
while ( Pos < Line . Len ( ) & & FChar : : IsWhitespace ( Line [ Pos ] ) )
{
+ + Pos ;
}
// Is it a *?
if ( Pos < Line . Len ( ) & & Line [ Pos ] = = ' * ' )
{
// Eat next space as well
if ( Pos + 1 < Line . Len ( ) & & FChar : : IsWhitespace ( Line [ Pos + 1 ] ) )
{
+ + Pos ;
}
Line = Line . RightChop ( Pos + 1 ) ;
}
}
2014-03-14 14:13:41 -04:00
}
// Find first meaningful line
int32 FirstIndex = 0 ;
for ( FString Line : Lines )
{
Line . Trim ( ) ;
if ( Line . Len ( ) & & ! IsLineSeparator ( * Line ) )
break ;
+ + FirstIndex ;
}
int32 LastIndex = Lines . Num ( ) ;
while ( LastIndex ! = FirstIndex )
{
FString Line = Lines [ LastIndex - 1 ] ;
Line . Trim ( ) ;
if ( Line . Len ( ) & & ! IsLineSeparator ( * Line ) )
break ;
- - LastIndex ;
}
Result . Empty ( ) ;
if ( FirstIndex ! = LastIndex )
{
auto & FirstLine = Lines [ FirstIndex ] ;
// Figure out how much whitespace is on the first line
int32 MaxNumWhitespaceToRemove ;
for ( MaxNumWhitespaceToRemove = 0 ; MaxNumWhitespaceToRemove < FirstLine . Len ( ) ; MaxNumWhitespaceToRemove + + )
{
if ( ! FChar : : IsLinebreak ( FirstLine [ MaxNumWhitespaceToRemove ] ) & & ! FChar : : IsWhitespace ( FirstLine [ MaxNumWhitespaceToRemove ] ) )
{
break ;
}
}
for ( int32 Index = FirstIndex ; Index ! = LastIndex ; + + Index )
{
FString Line = Lines [ Index ] ;
int32 TemporaryMaxWhitespace = MaxNumWhitespaceToRemove ;
// Allow eating an extra tab on subsequent lines if it's present
if ( ( Index > 0 ) & & ( Line . Len ( ) > 0 ) & & ( Line [ 0 ] = = ' \t ' ) )
{
TemporaryMaxWhitespace + + ;
}
// Advance past whitespace
int32 Pos = 0 ;
while ( Pos < TemporaryMaxWhitespace & & Pos < Line . Len ( ) & & FChar : : IsWhitespace ( Line [ Pos ] ) )
{
+ + Pos ;
}
if ( Pos > 0 )
{
Line = Line . Mid ( Pos ) ;
}
if ( Index > 0 )
{
Result + = TEXT ( " \n " ) ;
}
if ( Line . Len ( ) & & ! IsAllSameChar ( * Line , TEXT ( ' = ' ) ) )
{
Result + = Line ;
}
}
}
//@TODO: UCREMOVAL: Really want to trim an arbitrary number of newlines above and below, but keep multiple newlines internally
// Make sure it doesn't start with a newline
if ( ! Result . IsEmpty ( ) & & FChar : : IsLinebreak ( Result [ 0 ] ) )
{
Result = Result . Mid ( 1 ) ;
}
// Make sure it doesn't end with a dead newline
if ( ! Result . IsEmpty ( ) & & FChar : : IsLinebreak ( Result [ Result . Len ( ) - 1 ] ) )
{
Result = Result . Left ( Result . Len ( ) - 1 ) ;
}
// Done.
return Result ;
}
void FHeaderParser : : AddFormattedPrevCommentAsTooltipMetaData ( TMap < FName , FString > & MetaData )
{
// Don't add a tooltip if one already exists.
if ( MetaData . Find ( NAME_ToolTip ) )
{
return ;
}
// Don't add a tooltip if the comment is empty after formatting.
FString FormattedComment = FormatCommentForToolTip ( PrevComment ) ;
if ( ! FormattedComment . Len ( ) )
{
return ;
}
MetaData . Add ( NAME_ToolTip , * FormattedComment ) ;
// We've already used this comment as a tooltip, so clear it so that it doesn't get used again
PrevComment . Empty ( ) ;
}
static const TCHAR * GetAccessSpecifierName ( EAccessSpecifier AccessSpecifier )
{
switch ( AccessSpecifier )
{
case ACCESS_Public :
return TEXT ( " public " ) ;
case ACCESS_Protected :
return TEXT ( " protected " ) ;
case ACCESS_Private :
return TEXT ( " private " ) ;
default :
check ( 0 ) ;
}
return TEXT ( " " ) ;
}
// Tries to parse the token as an access protection specifier (public:, protected:, or private:)
EAccessSpecifier FHeaderParser : : ParseAccessProtectionSpecifier ( FToken & Token )
{
EAccessSpecifier ResultAccessSpecifier = ACCESS_NotAnAccessSpecifier ;
for ( EAccessSpecifier Test = EAccessSpecifier ( ACCESS_NotAnAccessSpecifier + 1 ) ; Test ! = ACCESS_Num ; Test = EAccessSpecifier ( Test + 1 ) )
{
2014-10-30 21:14:35 -04:00
if ( Token . Matches ( GetAccessSpecifierName ( Test ) ) | | ( Token . Matches ( TEXT ( " private_subobject " ) ) & & Test = = ACCESS_Public ) )
2014-03-14 14:13:41 -04:00
{
// Consume the colon after the specifier
RequireSymbol ( TEXT ( " : " ) , * FString : : Printf ( TEXT ( " after %s " ) , Token . Identifier ) ) ;
return Test ;
}
}
return ACCESS_NotAnAccessSpecifier ;
}
/**
* Compile a struct definition .
*/
2015-01-20 09:33:54 -05:00
UScriptStruct * FHeaderParser : : CompileStructDeclaration ( FClasses & AllClasses , FUnrealSourceFile & SourceFile )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
auto Scope = SourceFile . GetScope ( ) ;
2014-03-14 14:13:41 -04:00
// Make sure structs can be declared here.
CheckAllow ( TEXT ( " 'struct' " ) , ALLOW_TypeDecl ) ; //@TODO: UCREMOVAL: After the switch: Make this require global scope
FScriptLocation StructDeclaration ;
bool IsNative = false ;
bool IsExport = false ;
bool IsTransient = false ;
uint32 StructFlags = STRUCT_Native ;
TMap < FName , FString > MetaData ;
// Get the struct specifier list
TArray < FPropertySpecifier > SpecifiersFound ;
ReadSpecifierSetInsideMacro ( SpecifiersFound , TEXT ( " Struct " ) , MetaData ) ;
// Consume the struct keyword
RequireIdentifier ( TEXT ( " struct " ) , TEXT ( " Struct declaration specifier " ) ) ;
// The struct name as parsed in script and stripped of it's prefix
FString StructNameInScript ;
// The struct name stripped of it's prefix
FString StructNameStripped ;
// The required API module for this struct, if any
FString RequiredAPIMacroIfPresent ;
2014-10-24 02:59:18 -04:00
SkipDeprecatedMacroIfNecessary ( ) ;
2014-03-14 14:13:41 -04:00
// Read the struct name
ParseNameWithPotentialAPIMacroPrefix ( /*out*/ StructNameInScript , /*out*/ RequiredAPIMacroIfPresent , TEXT ( " struct " ) ) ;
// Record that this struct is RequiredAPI if the CORE_API style macro was present
if ( ! RequiredAPIMacroIfPresent . IsEmpty ( ) )
{
StructFlags | = STRUCT_RequiredAPI ;
}
2015-01-20 09:33:54 -05:00
StructNameStripped = GetClassNameWithPrefixRemoved ( StructNameInScript ) ;
2014-03-14 14:13:41 -04:00
// Effective struct name
2015-01-20 09:33:54 -05:00
const FString EffectiveStructName = * StructNameStripped ;
2014-03-14 14:13:41 -04:00
// Process the list of specifiers
for ( TArray < FPropertySpecifier > : : TIterator SpecifierIt ( SpecifiersFound ) ; SpecifierIt ; + + SpecifierIt )
{
const FString & Specifier = SpecifierIt - > Key ;
if ( Specifier = = TEXT ( " NoExport " ) )
{
//UE_LOG(LogCompile, Warning, TEXT("Struct named %s in %s is still marked noexport"), *EffectiveStructName, *(Class->GetName()));//@TODO: UCREMOVAL: Debug printing
StructFlags & = ~ STRUCT_Native ;
2015-01-20 09:33:54 -05:00
StructFlags | = STRUCT_NoExport ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " Atomic " ) )
{
StructFlags | = STRUCT_Atomic ;
}
else if ( Specifier = = TEXT ( " Immutable " ) )
{
StructFlags | = STRUCT_Immutable | STRUCT_Atomic ;
2015-01-20 09:33:54 -05:00
if ( ! FPaths : : IsSamePath ( Filename , GTypeDefinitionInfoMap [ UObject : : StaticClass ( ) ] - > GetUnrealSourceFile ( ) . GetFilename ( ) ) )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Immutable is being phased out in favor of SerializeNative, and is only legal on the mirror structs declared in UObject " ) ) ;
}
}
else
{
FError : : Throwf ( TEXT ( " Unknown struct specifier '%s' " ) , * Specifier ) ;
}
}
// Verify uniqueness (if declared within UClass).
{
2015-01-20 09:33:54 -05:00
auto * Existing = Scope - > FindTypeByName ( * EffectiveStructName ) ;
if ( Existing )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " struct: '%s' already defined here " ) , * EffectiveStructName ) ;
}
2014-08-12 08:51:25 -04:00
2015-01-20 09:33:54 -05:00
if ( FindObject < UStruct > ( ANY_PACKAGE , * EffectiveStructName ) ! = NULL )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " struct: '%s' conflicts with class name " ) , * EffectiveStructName ) ;
}
}
// Get optional superstruct.
bool bExtendsBaseStruct = false ;
if ( MatchSymbol ( TEXT ( " : " ) ) )
{
RequireIdentifier ( TEXT ( " public " ) , TEXT ( " struct inheritance " ) ) ;
bExtendsBaseStruct = true ;
}
UScriptStruct * BaseStruct = NULL ;
if ( bExtendsBaseStruct )
{
FToken ParentScope , ParentName ;
if ( GetIdentifier ( ParentScope ) )
{
2015-01-20 09:33:54 -05:00
TSharedRef < FScope > StructScope = Scope ;
2014-03-14 14:13:41 -04:00
FString ParentStructNameInScript = FString ( ParentScope . Identifier ) ;
if ( MatchSymbol ( TEXT ( " . " ) ) )
{
if ( GetIdentifier ( ParentName ) )
{
ParentStructNameInScript = FString ( ParentName . Identifier ) ;
FString ParentNameStripped = GetClassNameWithPrefixRemoved ( ParentScope . Identifier ) ;
2015-01-20 09:33:54 -05:00
FClass * StructClass = AllClasses . FindClass ( * ParentNameStripped ) ;
2014-03-14 14:13:41 -04:00
if ( ! StructClass )
{
// If we find the literal class name, the user didn't use a prefix
2014-04-23 18:42:01 -04:00
StructClass = AllClasses . FindClass ( ParentScope . Identifier ) ;
2014-03-14 14:13:41 -04:00
if ( StructClass )
{
FError : : Throwf ( TEXT ( " 'struct': Parent struct class '%s' is missing a prefix, expecting '%s' " ) , ParentScope . Identifier , * FString : : Printf ( TEXT ( " %s%s " ) , StructClass - > GetPrefixCPP ( ) , ParentScope . Identifier ) ) ;
}
else
{
FError : : Throwf ( TEXT ( " 'struct': Can't find parent struct class '%s' " ) , ParentScope . Identifier ) ;
}
}
2015-01-20 09:33:54 -05:00
StructScope = FScope : : GetTypeScope ( StructClass ) ;
2014-03-14 14:13:41 -04:00
}
else
{
FError : : Throwf ( TEXT ( " 'struct': Missing parent struct type after '%s.' " ) , ParentScope . Identifier ) ;
}
}
FString ParentStructNameStripped ;
2015-01-20 09:33:54 -05:00
const UField * Type = nullptr ;
2014-03-14 14:13:41 -04:00
bool bOverrideParentStructName = false ;
if ( ! StructsWithNoPrefix . Contains ( ParentStructNameInScript ) )
{
bOverrideParentStructName = true ;
ParentStructNameStripped = GetClassNameWithPrefixRemoved ( ParentStructNameInScript ) ;
}
// If we're expecting a prefix, first try finding the correct field with the stripped struct name
if ( bOverrideParentStructName )
{
2015-01-20 09:33:54 -05:00
Type = StructScope - > FindTypeByName ( * ParentStructNameStripped ) ;
2014-03-14 14:13:41 -04:00
}
// If it wasn't found, try to find the literal name given
2015-01-20 09:33:54 -05:00
if ( Type = = NULL )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
Type = StructScope - > FindTypeByName ( * ParentStructNameInScript ) ;
2014-03-14 14:13:41 -04:00
}
// Resolve structs declared in another class //@TODO: UCREMOVAL: This seems extreme
2015-01-20 09:33:54 -05:00
if ( Type = = NULL )
2014-03-14 14:13:41 -04:00
{
if ( bOverrideParentStructName )
{
2015-01-20 09:33:54 -05:00
Type = FindObject < UScriptStruct > ( ANY_PACKAGE , * ParentStructNameStripped ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
if ( Type = = NULL )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
Type = FindObject < UScriptStruct > ( ANY_PACKAGE , * ParentStructNameInScript ) ;
2014-03-14 14:13:41 -04:00
}
}
// If the struct still wasn't found, throw an error
2015-01-20 09:33:54 -05:00
if ( Type = = NULL )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " 'struct': Can't find struct '%s' " ) , * ParentStructNameInScript ) ;
}
else
{
// If the struct was found, confirm it adheres to the correct syntax. This should always fail if we were expecting an override that was not found.
2015-01-20 09:33:54 -05:00
BaseStruct = ( ( UScriptStruct * ) Type ) ;
2014-03-14 14:13:41 -04:00
if ( bOverrideParentStructName )
{
const TCHAR * PrefixCPP = StructsWithTPrefix . Contains ( ParentStructNameStripped ) ? TEXT ( " T " ) : BaseStruct - > GetPrefixCPP ( ) ;
if ( ParentStructNameInScript ! = FString : : Printf ( TEXT ( " %s%s " ) , PrefixCPP , * ParentStructNameStripped ) )
{
BaseStruct = NULL ;
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Parent Struct '%s' is missing a valid Unreal prefix, expecting '%s' " ) , * ParentStructNameInScript , * FString : : Printf ( TEXT ( " %s%s " ) , PrefixCPP , * Type - > GetName ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
}
else
{
}
}
}
else
{
FError : : Throwf ( TEXT ( " 'struct': Missing parent struct after ': public' " ) ) ;
}
}
// if we have a base struct, propagate inherited struct flags now
if ( BaseStruct ! = NULL )
{
StructFlags | = ( BaseStruct - > StructFlags & STRUCT_Inherit ) ;
}
2015-01-20 09:33:54 -05:00
2014-03-14 14:13:41 -04:00
// Create.
2015-01-30 08:30:03 -05:00
UScriptStruct * Struct = new ( EC_InternalUseOnlyConstructor , SourceFile . GetPackage ( ) , * EffectiveStructName , RF_Public ) UScriptStruct ( FObjectInitializer ( ) , BaseStruct ) ;
2015-01-20 09:33:54 -05:00
AddModuleRelativePathToMetadata ( Struct , MetaData ) ;
Scope - > AddType ( Struct ) ;
FScope : : AddTypeScope ( Struct , & SourceFile . GetScope ( ) . Get ( ) ) ;
AddTypeDefinition ( SourceFile , Struct , InputLine ) ;
2014-03-14 14:13:41 -04:00
// Check to make sure the syntactic native prefix was set-up correctly.
// If this check results in a false positive, it will be flagged as an identifier failure.
2015-01-20 09:33:54 -05:00
FString DeclaredPrefix = GetClassPrefix ( StructNameInScript ) ;
if ( DeclaredPrefix = = Struct - > GetPrefixCPP ( ) | | DeclaredPrefix = = TEXT ( " T " ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
// Found a prefix, do a basic check to see if it's valid
const TCHAR * ExpectedPrefixCPP = StructsWithTPrefix . Contains ( StructNameStripped ) ? TEXT ( " T " ) : Struct - > GetPrefixCPP ( ) ;
FString ExpectedStructName = FString : : Printf ( TEXT ( " %s%s " ) , ExpectedPrefixCPP , * StructNameStripped ) ;
if ( StructNameInScript ! = ExpectedStructName )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Struct '%s' has an invalid Unreal prefix, expecting '%s' " ) , * StructNameInScript , * ExpectedStructName ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
}
else
{
const TCHAR * ExpectedPrefixCPP = StructsWithTPrefix . Contains ( StructNameInScript ) ? TEXT ( " T " ) : Struct - > GetPrefixCPP ( ) ;
FString ExpectedStructName = FString : : Printf ( TEXT ( " %s%s " ) , ExpectedPrefixCPP , * StructNameInScript ) ;
FError : : Throwf ( TEXT ( " Struct '%s' is missing a valid Unreal prefix, expecting '%s' " ) , * StructNameInScript , * ExpectedStructName ) ;
2014-03-14 14:13:41 -04:00
}
Struct - > StructFlags = EStructFlags ( Struct - > StructFlags | StructFlags ) ;
AddFormattedPrevCommentAsTooltipMetaData ( MetaData ) ;
// Register the metadata
AddMetaDataToClassData ( Struct , MetaData ) ;
// Get opening brace.
RequireSymbol ( TEXT ( " { " ) , TEXT ( " 'struct' " ) ) ;
// Members of structs have a default public access level in c++
// Assume that, but restore the parser state once we finish parsing this struct
TGuardValue < EAccessSpecifier > HoldFromClass ( CurrentAccessSpecifier , ACCESS_Public ) ;
{
FToken StructToken ;
StructToken . Struct = Struct ;
// add this struct to the compiler's persistent tracking system
2015-01-20 09:33:54 -05:00
GScriptHelper . AddClassData ( StructToken . Struct ) ;
2014-03-14 14:13:41 -04:00
}
int32 SavedLineNumber = InputLine ;
// Clear comment before parsing body of the struct.
// Parse all struct variables.
FToken Token ;
while ( 1 )
{
ClearComment ( ) ;
GetToken ( Token ) ;
if ( EAccessSpecifier AccessSpecifier = ParseAccessProtectionSpecifier ( Token ) )
{
CurrentAccessSpecifier = AccessSpecifier ;
}
else if ( Token . Matches ( TEXT ( " UPROPERTY " ) , ESearchCase : : CaseSensitive ) )
{
2015-01-28 09:41:47 -05:00
CompileVariableDeclaration ( AllClasses , Struct ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " UFUNCTION " ) , ESearchCase : : CaseSensitive ) )
{
2014-05-07 15:46:05 -04:00
FError : : Throwf ( TEXT ( " USTRUCTs cannot contain UFUNCTIONs. " ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-03-17 06:02:26 -04:00
else if ( Token . Matches ( TEXT ( " GENERATED_USTRUCT_BODY " ) ) | | Token . Matches ( TEXT ( " GENERATED_BODY " ) ) )
2014-03-14 14:13:41 -04:00
{
2015-03-17 06:02:26 -04:00
// Match 'GENERATED_USTRUCT_BODY' '(' [StructName] ')' or 'GENERATED_BODY' '(' [StructName] ')'
2014-03-14 14:13:41 -04:00
if ( CurrentAccessSpecifier ! = ACCESS_Public )
{
2015-03-17 06:02:26 -04:00
FError : : Throwf ( TEXT ( " %s must be in the public scope of '%s', not private or protected. " ) , Token . Identifier , * StructNameInScript ) ;
2014-03-14 14:13:41 -04:00
}
if ( Struct - > StructMacroDeclaredLineNumber ! = INDEX_NONE )
{
2015-03-17 06:02:26 -04:00
FError : : Throwf ( TEXT ( " Multiple %s declarations found in '%s' " ) , Token . Identifier , * StructNameInScript ) ;
2014-03-14 14:13:41 -04:00
}
Struct - > StructMacroDeclaredLineNumber = InputLine ;
RequireSymbol ( TEXT ( " ( " ) , TEXT ( " 'struct' " ) ) ;
2015-04-10 06:02:22 -04:00
CompileVersionDeclaration ( SourceFile , Struct ) ;
2014-03-14 14:13:41 -04:00
RequireSymbol ( TEXT ( " ) " ) , TEXT ( " 'struct' " ) ) ;
// Eat a semicolon if present (not required)
SafeMatchSymbol ( TEXT ( " ; " ) ) ;
}
2014-05-01 15:24:39 -04:00
else if ( Token . Matches ( TEXT ( " # " ) ) & & MatchIdentifier ( TEXT ( " ifdef " ) ) )
{
PushCompilerDirective ( ECompilerDirective : : Insignificant ) ;
}
else if ( Token . Matches ( TEXT ( " # " ) ) & & MatchIdentifier ( TEXT ( " ifndef " ) ) )
{
PushCompilerDirective ( ECompilerDirective : : Insignificant ) ;
}
else if ( Token . Matches ( TEXT ( " # " ) ) & & MatchIdentifier ( TEXT ( " endif " ) ) )
2014-03-14 14:13:41 -04:00
{
if ( CompilerDirectiveStack . Num ( ) < 1 )
{
FError : : Throwf ( TEXT ( " Unmatched '#endif' in class or global scope " ) ) ;
}
CompilerDirectiveStack . Pop ( ) ;
// Do nothing and hope that the if code below worked out OK earlier
}
else if ( Token . Matches ( TEXT ( " # " ) ) & & MatchIdentifier ( TEXT ( " if " ) ) )
{
//@TODO: This parsing should be combined with CompileDirective and probably happen much much higher up!
bool bInvertConditional = MatchSymbol ( TEXT ( " ! " ) ) ;
bool bConsumeAsCppText = false ;
if ( MatchIdentifier ( TEXT ( " WITH_EDITORONLY_DATA " ) ) )
{
if ( bInvertConditional )
{
FError : : Throwf ( TEXT ( " Cannot use !WITH_EDITORONLY_DATA " ) ) ;
}
PushCompilerDirective ( ECompilerDirective : : WithEditorOnlyData ) ;
}
else if ( MatchIdentifier ( TEXT ( " WITH_EDITOR " ) ) )
{
if ( bInvertConditional )
{
FError : : Throwf ( TEXT ( " Cannot use !WITH_EDITOR " ) ) ;
}
PushCompilerDirective ( ECompilerDirective : : WithEditor ) ;
}
else if ( MatchIdentifier ( TEXT ( " CPP " ) ) )
{
bConsumeAsCppText = ! bInvertConditional ;
PushCompilerDirective ( ECompilerDirective : : Insignificant ) ;
//@todo: UCREMOVAL, !CPP should be interpreted as noexport and you should not need the no export.
// this applies to structs, enums, and everything else
}
else
{
FError : : Throwf ( TEXT ( " 'struct': Unsupported preprocessor directive inside a struct. " ) ) ;
}
if ( bConsumeAsCppText )
{
// Skip over the text, it is not recorded or processed
int32 nest = 1 ;
while ( nest > 0 )
{
TCHAR ch = GetChar ( 1 ) ;
if ( ch = = 0 )
{
FError : : Throwf ( TEXT ( " Unexpected end of struct definition %s " ) , * Struct - > GetName ( ) ) ;
}
2014-04-02 18:09:23 -04:00
else if ( ch = = ' { ' | | ( ch = = ' # ' & & ( PeekIdentifier ( TEXT ( " if " ) ) | | PeekIdentifier ( TEXT ( " ifdef " ) ) ) ) )
2014-03-14 14:13:41 -04:00
{
nest + + ;
}
2014-04-02 18:09:23 -04:00
else if ( ch = = ' } ' | | ( ch = = ' # ' & & PeekIdentifier ( TEXT ( " endif " ) ) ) )
2014-03-14 14:13:41 -04:00
{
nest - - ;
}
if ( nest = = 0 )
{
RequireIdentifier ( TEXT ( " endif " ) , TEXT ( " 'if' " ) ) ;
}
}
}
}
else
{
if ( ! Token . Matches ( TEXT ( " } " ) ) )
{
FToken DeclarationFirstToken = Token ;
if ( ! SkipDeclaration ( Token ) )
{
FError : : Throwf ( TEXT ( " 'struct': Unexpected '%s' " ) , DeclarationFirstToken . Identifier ) ;
}
}
else
{
MatchSemi ( ) ;
break ;
}
}
}
// Validation
bool bStructBodyFound = Struct - > StructMacroDeclaredLineNumber ! = INDEX_NONE ;
bool bExported = ! ! ( StructFlags & STRUCT_Native ) ;
if ( ! bStructBodyFound & & bExported )
{
// Roll the line number back to the start of the struct body and error out
InputLine = SavedLineNumber ;
2015-03-17 06:02:26 -04:00
FError : : Throwf ( TEXT ( " Expected a GENERATED_BODY() at the start of struct " ) ) ;
2014-03-14 14:13:41 -04:00
}
// Link the properties within the struct
Struct - > StaticLink ( true ) ;
return Struct ;
}
/*-----------------------------------------------------------------------------
Retry management .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Remember the current compilation points , both in the source being
* compiled and the object code being emitted .
*
* @ param Retry [ out ] filled in with current compiler position information
*/
void FHeaderParser : : InitScriptLocation ( FScriptLocation & Retry )
{
Retry . Input = Input ;
Retry . InputPos = InputPos ;
Retry . InputLine = InputLine ;
}
/**
* Return to a previously - saved retry point .
*
* @ param Retry the point to return to
* @ param Binary whether to modify the compiled bytecode
* @ param bText whether to modify the compiler ' s current location in the text
*/
void FHeaderParser : : ReturnToLocation ( const FScriptLocation & Retry , bool Binary , bool bText )
{
if ( bText )
{
Input = Retry . Input ;
InputPos = Retry . InputPos ;
InputLine = Retry . InputLine ;
}
}
/*-----------------------------------------------------------------------------
Nest information .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// Return the name for a nest type.
//
const TCHAR * FHeaderParser : : NestTypeName ( ENestType NestType )
{
switch ( NestType )
{
case NEST_GlobalScope :
return TEXT ( " Global Scope " ) ;
case NEST_Class :
return TEXT ( " Class " ) ;
2015-03-17 06:02:26 -04:00
case NEST_NativeInterface :
2014-03-14 14:13:41 -04:00
case NEST_Interface :
return TEXT ( " Interface " ) ;
case NEST_FunctionDeclaration :
return TEXT ( " Function " ) ;
default :
check ( false ) ;
return TEXT ( " Unknown " ) ;
}
}
// Checks to see if a particular kind of command is allowed on this nesting level.
bool FHeaderParser : : IsAllowedInThisNesting ( uint32 AllowFlags )
{
return ( ( TopNest - > Allow & AllowFlags ) ! = 0 ) ;
}
//
// Make sure that a particular kind of command is allowed on this nesting level.
// If it's not, issues a compiler error referring to the token and the current
// nesting level.
//
void FHeaderParser : : CheckAllow ( const TCHAR * Thing , uint32 AllowFlags )
{
if ( ! IsAllowedInThisNesting ( AllowFlags ) )
{
if ( TopNest - > NestType = = NEST_GlobalScope )
{
FError : : Throwf ( TEXT ( " %s is not allowed before the Class definition " ) , Thing ) ;
}
else
{
FError : : Throwf ( TEXT ( " %s is not allowed here " ) , Thing ) ;
}
}
}
2015-01-20 09:33:54 -05:00
bool FHeaderParser : : AllowReferenceToClass ( UStruct * Scope , UClass * CheckClass ) const
2014-03-14 14:13:41 -04:00
{
check ( CheckClass ) ;
2015-01-20 09:33:54 -05:00
return ( Scope - > GetOutermost ( ) = = CheckClass - > GetOutermost ( ) )
| | ( ( CheckClass - > ClassFlags & CLASS_Parsed ) ! = 0 )
| | ( ( CheckClass - > ClassFlags & CLASS_Intrinsic ) ! = 0 ) ;
2014-03-14 14:13:41 -04:00
}
/*-----------------------------------------------------------------------------
Nest management .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-01-20 09:33:54 -05:00
void FHeaderParser : : PushNest ( ENestType NestType , UStruct * InNode , FUnrealSourceFile * SourceFile )
2014-03-14 14:13:41 -04:00
{
// Update pointer to top nesting level.
2015-01-20 09:33:54 -05:00
TopNest = & Nest [ NestLevel + + ] ;
TopNest - > SetScope ( NestType = = NEST_GlobalScope ? & SourceFile - > GetScope ( ) . Get ( ) : & FScope : : GetTypeScope ( InNode ) . Get ( ) ) ;
TopNest - > NestType = NestType ;
2014-03-14 14:13:41 -04:00
// Prevent overnesting.
2015-01-20 09:33:54 -05:00
if ( NestLevel > = MAX_NEST_LEVELS )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Maximum nesting limit exceeded " ) ) ;
2014-03-14 14:13:41 -04:00
}
// Inherit info from stack node above us.
2015-01-20 09:33:54 -05:00
if ( NestLevel > 1 & & NestType = = NEST_GlobalScope )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
// Use the existing stack node.
TopNest - > SetScope ( TopNest [ - 1 ] . GetScope ( ) ) ;
2014-03-14 14:13:41 -04:00
}
// NestType specific logic.
2015-01-20 09:33:54 -05:00
switch ( NestType )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
case NEST_GlobalScope :
TopNest - > Allow = ALLOW_Class | ALLOW_TypeDecl | ALLOW_Function ;
break ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
case NEST_Class :
TopNest - > Allow = ALLOW_VarDecl | ALLOW_Function | ALLOW_TypeDecl ;
break ;
2014-03-14 14:13:41 -04:00
2015-03-17 06:02:26 -04:00
case NEST_NativeInterface :
2015-01-20 09:33:54 -05:00
case NEST_Interface :
TopNest - > Allow = ALLOW_Function | ALLOW_TypeDecl ;
break ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
case NEST_FunctionDeclaration :
TopNest - > Allow = ALLOW_VarDecl ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
break ;
default :
FError : : Throwf ( TEXT ( " Internal error in PushNest, type %i " ) , ( uint8 ) NestType ) ;
break ;
2014-03-14 14:13:41 -04:00
}
}
/**
* Decrease the nesting level and handle any errors that result .
*
* @ param NestType nesting type of the current node
* @ param Descr text to use in error message if any errors are encountered
*/
2015-01-20 09:33:54 -05:00
void FHeaderParser : : PopNest ( ENestType NestType , const TCHAR * Descr )
2014-03-14 14:13:41 -04:00
{
// Validate the nesting state.
if ( NestLevel < = 0 )
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Unexpected '%s' at global scope " ) , Descr , NestTypeName ( NestType ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( TopNest - > NestType ! = NestType )
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Unexpected end of %s in '%s' block " ) , Descr , NestTypeName ( TopNest - > NestType ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-03-17 06:02:26 -04:00
if ( NestType ! = NEST_GlobalScope & & NestType ! = NEST_Class & & NestType ! = NEST_Interface & & NestType ! = NEST_NativeInterface & & NestType ! = NEST_FunctionDeclaration )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Bad first pass NestType %i " ) , ( uint8 ) NestType ) ;
2014-03-14 14:13:41 -04:00
}
bool bLinkProps = true ;
if ( NestType = = NEST_Class )
{
2015-01-20 09:33:54 -05:00
UClass * TopClass = CastChecked < UClass > ( GetCurrentClass ( ) ) ;
2014-03-14 14:13:41 -04:00
bLinkProps = ! TopClass - > HasAnyClassFlags ( CLASS_Intrinsic ) ;
}
2015-01-20 09:33:54 -05:00
if ( NestType ! = NEST_GlobalScope )
{
GetCurrentClass ( ) - > StaticLink ( bLinkProps ) ;
}
2014-03-14 14:13:41 -04:00
// Pop the nesting level.
NestType = TopNest - > NestType ;
NestLevel - - ;
TopNest - - ;
}
2015-01-20 09:33:54 -05:00
void FHeaderParser : : FixupDelegateProperties ( FClasses & AllClasses , UStruct * Struct , FScope & Scope , TMap < FName , UFunction * > & DelegateCache )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
check ( Struct ) ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
for ( UField * Field = Struct - > Children ; Field ; Field = Field - > Next )
2014-03-14 14:13:41 -04:00
{
UProperty * Property = Cast < UProperty > ( Field ) ;
if ( Property ! = NULL )
{
UDelegateProperty * DelegateProperty = Cast < UDelegateProperty > ( Property ) ;
UMulticastDelegateProperty * MulticastDelegateProperty = Cast < UMulticastDelegateProperty > ( Property ) ;
if ( DelegateProperty = = NULL & & MulticastDelegateProperty = = NULL )
{
// if this is an array property, see if the array's type is a delegate
UArrayProperty * ArrayProp = Cast < UArrayProperty > ( Property ) ;
if ( ArrayProp ! = NULL )
{
DelegateProperty = Cast < UDelegateProperty > ( ArrayProp - > Inner ) ;
MulticastDelegateProperty = Cast < UMulticastDelegateProperty > ( ArrayProp - > Inner ) ;
}
}
2015-01-20 09:33:54 -05:00
if ( DelegateProperty ! = nullptr | | MulticastDelegateProperty ! = nullptr )
2014-03-14 14:13:41 -04:00
{
// this UDelegateProperty corresponds to an actual delegate variable (i.e. delegate<SomeDelegate> Foo); we need to lookup the token data for
// this property and verify that the delegate property's "type" is an actual delegate function
2015-01-20 09:33:54 -05:00
FTokenData * DelegatePropertyToken = GScriptHelper . FindClassData ( Struct ) - > FindTokenData ( Property ) ;
2014-03-14 14:13:41 -04:00
check ( DelegatePropertyToken ) ;
// attempt to find the delegate function in the map of functions we've already found
UFunction * SourceDelegateFunction = DelegateCache . FindRef ( DelegatePropertyToken - > Token . DelegateName ) ;
2015-01-20 09:33:54 -05:00
if ( SourceDelegateFunction = = nullptr )
2014-03-14 14:13:41 -04:00
{
FString NameOfDelegateFunction = DelegatePropertyToken - > Token . DelegateName . ToString ( ) + FString ( HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ) ;
if ( ! NameOfDelegateFunction . Contains ( TEXT ( " . " ) ) )
{
// an unqualified delegate function name - search for a delegate function by this name within the current scope
2015-01-20 09:33:54 -05:00
SourceDelegateFunction = Cast < UFunction > ( Scope . FindTypeByName ( * NameOfDelegateFunction ) ) ;
if ( SourceDelegateFunction = = nullptr )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
// Try to find in other packages.
SourceDelegateFunction = Cast < UFunction > ( StaticFindObject ( UFunction : : StaticClass ( ) , ANY_PACKAGE , * NameOfDelegateFunction ) ) ;
if ( SourceDelegateFunction = = nullptr )
{
// convert this into a fully qualified path name for the error message.
NameOfDelegateFunction = Scope . GetName ( ) . ToString ( ) + TEXT ( " . " ) + NameOfDelegateFunction ;
}
2014-03-14 14:13:41 -04:00
}
}
else
{
FString DelegateClassName , DelegateName ;
NameOfDelegateFunction . Split ( TEXT ( " . " ) , & DelegateClassName , & DelegateName ) ;
// verify that we got a valid string for the class name
if ( DelegateClassName . Len ( ) = = 0 )
{
UngetToken ( DelegatePropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Invalid scope specified in delegate property function reference: '%s' " ) , * NameOfDelegateFunction ) ;
}
// verify that we got a valid string for the name of the function
if ( DelegateName . Len ( ) = = 0 )
{
UngetToken ( DelegatePropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Invalid delegate name specified in delegate property function reference '%s' " ) , * NameOfDelegateFunction ) ;
}
// make sure that the class that contains the delegate can be referenced here
2014-04-23 18:42:01 -04:00
UClass * DelegateOwnerClass = AllClasses . FindScriptClassOrThrow ( DelegateClassName ) ;
2015-01-20 09:33:54 -05:00
if ( FScope : : GetTypeScope ( DelegateOwnerClass ) - > FindTypeByName ( * DelegateName ) ! = nullptr )
{
FError : : Throwf ( TEXT ( " Inaccessible type: '%s' " ) , * DelegateOwnerClass - > GetPathName ( ) ) ;
}
2014-03-14 14:13:41 -04:00
SourceDelegateFunction = Cast < UFunction > ( FindField ( DelegateOwnerClass , * DelegateName , false , UFunction : : StaticClass ( ) , NULL ) ) ;
}
if ( SourceDelegateFunction = = NULL )
{
UngetToken ( DelegatePropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Failed to find delegate function '%s' " ) , * NameOfDelegateFunction ) ;
}
else if ( ( SourceDelegateFunction - > FunctionFlags & FUNC_Delegate ) = = 0 )
{
UngetToken ( DelegatePropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Only delegate functions can be used as the type for a delegate property; '%s' is not a delegate. " ) , * NameOfDelegateFunction ) ;
}
}
// successfully found the delegate function that this delegate property corresponds to
// save this into the delegate cache for faster lookup later
DelegateCache . Add ( DelegatePropertyToken - > Token . DelegateName , SourceDelegateFunction ) ;
// bind it to the delegate property
if ( DelegateProperty ! = NULL )
{
if ( ! SourceDelegateFunction - > HasAnyFunctionFlags ( FUNC_MulticastDelegate ) )
{
DelegateProperty - > SignatureFunction = DelegatePropertyToken - > Token . Function = SourceDelegateFunction ;
}
else
{
FError : : Throwf ( TEXT ( " Unable to declare a single-cast delegate property for a multi-cast delegate type '%s'. Either add a 'multicast' qualifier to the property or change the delegate type to be single-cast as well. " ) , * SourceDelegateFunction - > GetName ( ) ) ;
}
}
else if ( MulticastDelegateProperty ! = NULL )
{
if ( SourceDelegateFunction - > HasAnyFunctionFlags ( FUNC_MulticastDelegate ) )
{
MulticastDelegateProperty - > SignatureFunction = DelegatePropertyToken - > Token . Function = SourceDelegateFunction ;
if ( MulticastDelegateProperty - > HasAnyPropertyFlags ( CPF_BlueprintAssignable | CPF_BlueprintCallable ) )
{
for ( TFieldIterator < UProperty > PropIt ( SourceDelegateFunction ) ; PropIt & & ( PropIt - > PropertyFlags & CPF_Parm ) ; + + PropIt )
{
UProperty * FuncParam = * PropIt ;
if ( FuncParam - > HasAllPropertyFlags ( CPF_OutParm ) & & ! FuncParam - > HasAllPropertyFlags ( CPF_ConstParm ) )
{
FError : : Throwf ( TEXT ( " BlueprintAssignable delegates do not support non-const references at the moment. Function: %s Parameter: '%s' " ) , * SourceDelegateFunction - > GetName ( ) , * FuncParam - > GetName ( ) ) ;
}
}
}
}
else
{
FError : : Throwf ( TEXT ( " Unable to declare a multi-cast delegate property for a single-cast delegate type '%s'. Either remove the 'multicast' qualifier from the property or change the delegate type to be 'multicast' as well. " ) , * SourceDelegateFunction - > GetName ( ) ) ;
}
}
}
}
else
{
// if this is a state, function, or script struct, it might have its own delegate properties which need to be validated
UStruct * InternalStruct = Cast < UStruct > ( Field ) ;
if ( InternalStruct ! = NULL )
{
2015-01-20 09:33:54 -05:00
FixupDelegateProperties ( AllClasses , InternalStruct , Scope , DelegateCache ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
/**
* Verifies that all specified class ' s UProperties with CFG_RepNotify have valid callback targets with no parameters nor return values
*
* @ param TargetClass class to verify rep notify properties for
*/
void FHeaderParser : : VerifyRepNotifyCallbacks ( UClass * TargetClass )
{
// Iterate over all properties, looking for those flagged as CPF_RepNotify
for ( UField * Field = TargetClass - > Children ; Field ; Field = Field - > Next )
{
UProperty * Prop = Cast < UProperty > ( Field ) ;
if ( Prop & & ( Prop - > GetPropertyFlags ( ) & CPF_RepNotify ) )
{
2015-01-20 09:33:54 -05:00
FTokenData * PropertyToken = GScriptHelper . FindClassData ( TargetClass ) - > FindTokenData ( Prop ) ;
2014-03-14 14:13:41 -04:00
check ( PropertyToken ) ;
// Search through this class and its superclasses looking for the specified callback
UFunction * TargetFunc = NULL ;
UClass * SearchClass = TargetClass ;
while ( SearchClass & & ! TargetFunc )
{
// Since the function map is not valid yet, we have to iterate over the fields to look for the function
for ( UField * TestField = SearchClass - > Children ; TestField ; TestField = TestField - > Next )
{
UFunction * TestFunc = Cast < UFunction > ( TestField ) ;
if ( TestFunc & & TestFunc - > GetFName ( ) = = Prop - > RepNotifyFunc )
{
TargetFunc = TestFunc ;
break ;
}
}
SearchClass = SearchClass - > GetSuperClass ( ) ;
}
if ( TargetFunc )
{
if ( TargetFunc - > GetReturnProperty ( ) )
{
UngetToken ( PropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Replication notification function %s must not have return values " ) , * Prop - > RepNotifyFunc . ToString ( ) ) ;
break ;
}
bool IsArrayProperty = ( Prop - > ArrayDim > 1 | | Cast < UArrayProperty > ( Prop ) ) ;
int32 MaxParms = IsArrayProperty ? 2 : 1 ;
if ( TargetFunc - > NumParms > MaxParms )
{
UngetToken ( PropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Replication notification function %s has too many parameters " ) , * Prop - > RepNotifyFunc . ToString ( ) ) ;
break ;
}
TFieldIterator < UProperty > Parm ( TargetFunc ) ;
if ( TargetFunc - > NumParms > = 1 & & Parm )
{
// First parameter is always the old value:
if ( Parm - > GetClass ( ) ! = Prop - > GetClass ( ) )
{
UngetToken ( PropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Replication notification function %s has invalid parameter for property $%s. First (optional) parameter must be a const reference of the same property type. " ) , * Prop - > RepNotifyFunc . ToString ( ) , * Prop - > GetName ( ) ) ;
break ;
}
+ + Parm ;
}
if ( TargetFunc - > NumParms > = 2 & & Parm )
{
// A 2nd parmaeter for arrays can be specified as a const TArray<uint8>&. This is a list of element indices that have changed
UArrayProperty * ArrayProp = Cast < UArrayProperty > ( * Parm ) ;
if ( ! ( ArrayProp & & Cast < UByteProperty > ( ArrayProp - > Inner ) ) | | ! ( Parm - > GetPropertyFlags ( ) & CPF_ConstParm ) | | ! ( Parm - > GetPropertyFlags ( ) & CPF_ReferenceParm ) )
{
UngetToken ( PropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Replication notification function %s (optional) parameter must be of type 'const TArray<uint8>&' " ) , * Prop - > RepNotifyFunc . ToString ( ) ) ;
break ;
}
}
}
else
{
// Couldn't find a valid function...
UngetToken ( PropertyToken - > Token ) ;
FError : : Throwf ( TEXT ( " Replication notification function %s not found " ) , * Prop - > RepNotifyFunc . ToString ( ) ) ;
}
}
}
}
/*-----------------------------------------------------------------------------
Compiler directives .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// Process a compiler directive.
//
2015-01-20 09:33:54 -05:00
void FHeaderParser : : CompileDirective ( FClasses & AllClasses , FUnrealSourceFile & SourceFile )
2014-03-14 14:13:41 -04:00
{
FToken Directive ;
int32 LineAtStartOfDirective = InputLine ;
// Define directive are skipped but they can be multiline.
bool bDefineDirective = false ;
if ( ! GetIdentifier ( Directive ) )
{
FError : : Throwf ( TEXT ( " Missing compiler directive after '#' " ) ) ;
}
else if ( Directive . Matches ( TEXT ( " Error " ) ) )
{
FError : : Throwf ( TEXT ( " #Error directive encountered " ) ) ;
}
else if ( Directive . Matches ( TEXT ( " pragma " ) ) )
{
// Ignore all pragmas
}
else if ( Directive . Matches ( TEXT ( " linenumber " ) ) )
{
FToken Number ;
if ( ! GetToken ( Number ) | | ( Number . TokenType ! = TOKEN_Const ) | | ( Number . Type ! = CPT_Int ) )
{
FError : : Throwf ( TEXT ( " Missing line number in line number directive " ) ) ;
}
int32 newInputLine ;
if ( Number . GetConstInt ( newInputLine ) )
{
InputLine = newInputLine ;
}
}
else if ( Directive . Matches ( TEXT ( " include " ) ) )
{
2015-01-20 09:33:54 -05:00
FString ExpectedHeaderName = SourceFile . GetGeneratedHeaderFilename ( ) ;
2014-03-14 14:13:41 -04:00
FToken IncludeName ;
if ( GetToken ( IncludeName ) & & ( IncludeName . TokenType = = TOKEN_Const ) & & ( IncludeName . Type = = CPT_String ) )
{
if ( FCString : : Stricmp ( IncludeName . String , * ExpectedHeaderName ) = = 0 )
{
bSpottedAutogeneratedHeaderInclude = true ;
}
}
}
else if ( Directive . Matches ( TEXT ( " if " ) ) )
{
// Eat the ! if present
bool bNotDefined = MatchSymbol ( TEXT ( " ! " ) ) ;
FToken Define ;
if ( ! GetIdentifier ( Define ) )
{
FError : : Throwf ( TEXT ( " Missing define name '#if' " ) ) ;
}
if ( Define . Matches ( TEXT ( " WITH_EDITORONLY_DATA " ) ) )
{
PushCompilerDirective ( ECompilerDirective : : WithEditorOnlyData ) ;
}
else if ( Define . Matches ( TEXT ( " WITH_EDITOR " ) ) )
{
PushCompilerDirective ( ECompilerDirective : : WithEditor ) ;
}
else if ( Define . Matches ( TEXT ( " CPP " ) ) & & bNotDefined )
{
PushCompilerDirective ( ECompilerDirective : : Insignificant ) ;
}
else
{
FError : : Throwf ( TEXT ( " Unknown define '#if %s' in class or global scope " ) , Define . Identifier ) ;
}
}
else if ( Directive . Matches ( TEXT ( " endif " ) ) )
{
if ( CompilerDirectiveStack . Num ( ) < 1 )
{
FError : : Throwf ( TEXT ( " Unmatched '#endif' in class or global scope " ) ) ;
}
CompilerDirectiveStack . Pop ( ) ;
}
else if ( Directive . Matches ( TEXT ( " define " ) ) )
{
// Ignore the define directive (can be multiline).
bDefineDirective = true ;
}
2014-05-01 15:24:39 -04:00
else if ( Directive . Matches ( TEXT ( " ifdef " ) ) | | Directive . Matches ( TEXT ( " ifndef " ) ) )
{
PushCompilerDirective ( ECompilerDirective : : Insignificant ) ;
}
else if ( Directive . Matches ( TEXT ( " undef " ) ) | | Directive . Matches ( TEXT ( " else " ) ) )
2014-03-14 14:13:41 -04:00
{
// Ignore. UHT can only handle #if directive
}
else
{
FError : : Throwf ( TEXT ( " Unrecognized compiler directive %s " ) , Directive . Identifier ) ;
}
// Skip to end of line (or end of multiline #define).
if ( LineAtStartOfDirective = = InputLine )
{
TCHAR LastCharacter = ' \0 ' ;
TCHAR c ;
do
{
while ( ! IsEOL ( c = GetChar ( ) ) )
{
LastCharacter = c ;
}
}
// Continue until the entire multiline directive has been skipped.
while ( LastCharacter = = ' \\ ' & & bDefineDirective ) ;
if ( c = = 0 )
{
UngetChar ( ) ;
}
}
}
/*-----------------------------------------------------------------------------
Variable declaration parser .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-03-19 13:42:08 -04:00
void FHeaderParser : : GetVarType
2014-03-14 14:13:41 -04:00
(
2014-04-23 18:42:01 -04:00
FClasses & AllClasses ,
2015-01-20 09:33:54 -05:00
FScope * Scope ,
2014-03-14 14:13:41 -04:00
FPropertyBase & VarProperty ,
uint64 Disallow ,
FToken * OuterPropertyType ,
EPropertyDeclarationStyle : : Type PropertyDeclarationStyle ,
2014-09-30 09:10:54 -04:00
EVariableCategory : : Type VariableCategory ,
FIndexRange * ParsedVarIndexRange
2014-03-14 14:13:41 -04:00
)
{
2015-01-20 09:33:54 -05:00
UStruct * OwnerStruct = Scope - > IsFileScope ( ) ? nullptr : ( ( FStructScope * ) Scope ) - > GetStruct ( ) ;
2015-03-16 11:26:20 -04:00
FName RepCallbackName = FName ( NAME_None ) ;
2014-03-14 14:13:41 -04:00
// Get flags.
uint64 Flags = 0 ;
// force members to be 'blueprint read only' if in a const class
2015-01-20 09:33:54 -05:00
if ( VariableCategory = = EVariableCategory : : Member & & ( Cast < UClass > ( OwnerStruct ) ! = nullptr ) & & ( ( ( UClass * ) OwnerStruct ) - > ClassFlags & CLASS_Const ) )
2014-03-14 14:13:41 -04:00
{
Flags | = CPF_BlueprintReadOnly ;
}
uint32 ExportFlags = PROPEXPORT_Public ;
// Build up a list of specifiers
TArray < FPropertySpecifier > SpecifiersFound ;
TMap < FName , FString > MetaDataFromNewStyle ;
bool bIsParamList = VariableCategory ! = EVariableCategory : : Member & & MatchIdentifier ( TEXT ( " UPARAM " ) ) ;
// No specifiers are allowed inside a TArray
if ( ( OuterPropertyType = = NULL ) | | ! OuterPropertyType - > Matches ( TEXT ( " TArray " ) ) )
{
// New-style UPROPERTY() syntax
if ( PropertyDeclarationStyle = = EPropertyDeclarationStyle : : UPROPERTY | | bIsParamList )
{
ReadSpecifierSetInsideMacro ( SpecifiersFound , TEXT ( " Variable " ) , MetaDataFromNewStyle ) ;
}
2015-03-16 11:26:20 -04:00
}
if ( VariableCategory ! = EVariableCategory : : Member )
{
// const before the variable type support (only for params)
if ( MatchIdentifier ( TEXT ( " const " ) ) )
2014-03-14 14:13:41 -04:00
{
2015-03-16 11:26:20 -04:00
Flags | = CPF_ConstParm ;
2014-03-14 14:13:41 -04:00
}
}
if ( CompilerDirectiveStack . Num ( ) > 0 & & ( CompilerDirectiveStack . Last ( ) & ECompilerDirective : : WithEditorOnlyData ) ! = 0 )
{
Flags | = CPF_EditorOnly ;
}
2014-09-30 09:10:54 -04:00
// Store the start and end positions of the parsed type
if ( ParsedVarIndexRange )
{
ParsedVarIndexRange - > StartIndex = InputPos ;
}
2014-03-14 14:13:41 -04:00
// Process the list of specifiers
2014-04-23 16:45:57 -04:00
bool bSeenEditSpecifier = false ;
bool bSeenBlueprintEditSpecifier = false ;
2014-03-14 14:13:41 -04:00
for ( TArray < FPropertySpecifier > : : TIterator SpecifierIt ( SpecifiersFound ) ; SpecifierIt ; + + SpecifierIt )
{
const FString & Specifier = SpecifierIt - > Key ;
if ( VariableCategory = = EVariableCategory : : Member )
{
if ( Specifier = = TEXT ( " EditAnywhere " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one edit/visibility specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-03-14 14:13:41 -04:00
Flags | = CPF_Edit ;
2014-04-23 16:45:57 -04:00
bSeenEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " EditInstanceOnly " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one edit/visibility specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-04-23 18:42:01 -04:00
Flags | = CPF_Edit | CPF_DisableEditOnTemplate ;
2014-04-23 16:45:57 -04:00
bSeenEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " EditDefaultsOnly " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one edit/visibility specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-04-23 18:42:01 -04:00
Flags | = CPF_Edit | CPF_DisableEditOnInstance ;
2014-04-23 16:45:57 -04:00
bSeenEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " VisibleAnywhere " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one edit/visibility specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-04-23 18:42:01 -04:00
Flags | = CPF_Edit | CPF_EditConst ;
2014-04-23 16:45:57 -04:00
bSeenEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " VisibleInstanceOnly " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one edit/visibility specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-04-23 18:42:01 -04:00
Flags | = CPF_Edit | CPF_EditConst | CPF_DisableEditOnTemplate ;
2014-04-23 16:45:57 -04:00
bSeenEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " VisibleDefaultsOnly " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one edit/visibility specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-04-23 18:42:01 -04:00
Flags | = CPF_Edit | CPF_EditConst | CPF_DisableEditOnInstance ;
2014-04-23 16:45:57 -04:00
bSeenEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " BlueprintReadWrite " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenBlueprintEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one Blueprint read/write specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-03-14 14:13:41 -04:00
const FString * PrivateAccessMD = MetaDataFromNewStyle . Find ( TEXT ( " AllowPrivateAccess " ) ) ; // FBlueprintMetadata::MD_AllowPrivateAccess
const bool bAllowPrivateAccess = PrivateAccessMD ? ( * PrivateAccessMD = = TEXT ( " true " ) ) : false ;
if ( ( CurrentAccessSpecifier = = ACCESS_Private ) & & ! bAllowPrivateAccess )
{
FError : : Throwf ( TEXT ( " BlueprintReadWrite should not be used on private members " ) ) ;
}
Flags | = CPF_BlueprintVisible ;
2014-04-23 16:45:57 -04:00
bSeenBlueprintEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " BlueprintReadOnly " ) )
{
2014-04-23 16:45:57 -04:00
if ( bSeenBlueprintEditSpecifier )
{
FError : : Throwf ( TEXT ( " Found more than one Blueprint read/write specifier (%s), only one is allowed " ) , * Specifier ) ;
}
2014-03-14 14:13:41 -04:00
const FString * PrivateAccessMD = MetaDataFromNewStyle . Find ( TEXT ( " AllowPrivateAccess " ) ) ; // FBlueprintMetadata::MD_AllowPrivateAccess
const bool bAllowPrivateAccess = PrivateAccessMD ? ( * PrivateAccessMD = = TEXT ( " true " ) ) : false ;
if ( ( CurrentAccessSpecifier = = ACCESS_Private ) & & ! bAllowPrivateAccess )
{
FError : : Throwf ( TEXT ( " BlueprintReadOnly should not be used on private members " ) ) ;
}
Flags | = CPF_BlueprintVisible | CPF_BlueprintReadOnly ;
2014-04-23 16:45:57 -04:00
bSeenBlueprintEditSpecifier = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " Config " ) )
{
Flags | = CPF_Config ;
}
else if ( Specifier = = TEXT ( " GlobalConfig " ) )
{
Flags | = CPF_GlobalConfig | CPF_Config ;
}
else if ( Specifier = = TEXT ( " Localized " ) )
{
2015-01-27 05:50:06 -05:00
FError : : Throwf ( TEXT ( " The Localized specifier is deprecated " ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " Transient " ) )
{
Flags | = CPF_Transient ;
}
else if ( Specifier = = TEXT ( " DuplicateTransient " ) )
{
Flags | = CPF_DuplicateTransient ;
}
else if ( Specifier = = TEXT ( " TextExportTransient " ) )
{
Flags | = CPF_TextExportTransient ;
}
else if ( Specifier = = TEXT ( " NonPIETransient " ) )
{
2014-08-19 15:07:24 -04:00
UE_LOG ( LogCompile , Warning , TEXT ( " NonPIETransient is deprecated - NonPIEDuplicateTransient should be used instead " ) ) ;
Flags | = CPF_NonPIEDuplicateTransient ;
}
else if ( Specifier = = TEXT ( " NonPIEDuplicateTransient " ) )
{
Flags | = CPF_NonPIEDuplicateTransient ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " Export " ) )
{
Flags | = CPF_ExportObject ;
}
else if ( Specifier = = TEXT ( " EditInline " ) )
{
2014-09-01 14:15:28 -04:00
FError : : Throwf ( TEXT ( " EditInline is deprecated. Remove it, or use Instanced instead. " ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " NoClear " ) )
{
Flags | = CPF_NoClear ;
}
else if ( Specifier = = TEXT ( " EditFixedSize " ) )
{
Flags | = CPF_EditFixedSize ;
}
else if ( Specifier = = TEXT ( " Replicated " ) | | Specifier = = TEXT ( " ReplicatedUsing " ) )
{
2015-01-20 09:33:54 -05:00
if ( ! OwnerStruct - > IsA < UScriptStruct > ( ) )
2014-03-14 14:13:41 -04:00
{
Flags | = CPF_Net ;
// See if we've specified a rep notification function
if ( Specifier = = TEXT ( " ReplicatedUsing " ) )
{
RepCallbackName = FName ( * RequireExactlyOneSpecifierValue ( * SpecifierIt ) ) ;
Flags | = CPF_RepNotify ;
}
}
else
{
FError : : Throwf ( TEXT ( " Struct members cannot be replicated " ) ) ;
}
}
else if ( Specifier = = TEXT ( " NotReplicated " ) )
{
2015-01-20 09:33:54 -05:00
if ( OwnerStruct - > IsA < UScriptStruct > ( ) )
2014-03-14 14:13:41 -04:00
{
Flags | = CPF_RepSkip ;
}
else
{
FError : : Throwf ( TEXT ( " Only Struct members can be marked NotReplicated " ) ) ;
}
}
else if ( Specifier = = TEXT ( " RepRetry " ) )
{
2015-04-09 18:10:40 -04:00
FError : : Throwf ( TEXT ( " 'RepRetry' is deprecated. " ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " Interp " ) )
{
Flags | = CPF_Edit ;
Flags | = CPF_BlueprintVisible ;
Flags | = CPF_Interp ;
}
else if ( Specifier = = TEXT ( " NonTransactional " ) )
{
Flags | = CPF_NonTransactional ;
}
else if ( Specifier = = TEXT ( " Instanced " ) )
{
2014-09-24 09:09:32 -04:00
Flags | = CPF_PersistentInstance | CPF_ExportObject | CPF_InstancedReference ;
AddEditInlineMetaData ( MetaDataFromNewStyle ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " BlueprintAssignable " ) )
{
Flags | = CPF_BlueprintAssignable ;
}
else if ( Specifier = = TEXT ( " BlueprintCallable " ) )
{
Flags | = CPF_BlueprintCallable ;
}
else if ( Specifier = = TEXT ( " BlueprintAuthorityOnly " ) )
{
Flags | = CPF_BlueprintAuthorityOnly ;
}
else if ( Specifier = = TEXT ( " AssetRegistrySearchable " ) )
{
Flags | = CPF_AssetRegistrySearchable ;
}
else if ( Specifier = = TEXT ( " SimpleDisplay " ) )
{
Flags | = CPF_SimpleDisplay ;
}
else if ( Specifier = = TEXT ( " AdvancedDisplay " ) )
{
Flags | = CPF_AdvancedDisplay ;
}
else if ( Specifier = = TEXT ( " SaveGame " ) )
{
Flags | = CPF_SaveGame ;
}
else
{
FError : : Throwf ( TEXT ( " Unknown variable specifier '%s' " ) , * Specifier ) ;
}
}
else
{
if ( Specifier = = TEXT ( " Const " ) )
{
Flags | = CPF_ConstParm ;
}
else if ( Specifier = = TEXT ( " Ref " ) )
{
Flags | = CPF_OutParm | CPF_ReferenceParm ;
}
else if ( Specifier = = TEXT ( " NotReplicated " ) )
{
if ( VariableCategory = = EVariableCategory : : ReplicatedParameter )
{
VariableCategory = EVariableCategory : : RegularParameter ;
Flags | = CPF_RepSkip ;
}
else
{
FError : : Throwf ( TEXT ( " Only parameters in service request functions can be marked NotReplicated " ) ) ;
}
}
else
{
FError : : Throwf ( TEXT ( " Unknown variable specifier '%s' " ) , * Specifier ) ;
}
}
}
2014-04-02 18:09:23 -04:00
{
const FString * ExposeOnSpawnStr = MetaDataFromNewStyle . Find ( TEXT ( " ExposeOnSpawn " ) ) ;
const bool bExposeOnSpawn = ( NULL ! = ExposeOnSpawnStr ) ;
if ( bExposeOnSpawn )
{
2014-04-23 17:34:52 -04:00
if ( 0 ! = ( CPF_DisableEditOnInstance & Flags ) )
2014-04-02 18:09:23 -04:00
{
UE_LOG ( LogCompile , Warning , TEXT ( " Property cannot have 'DisableEditOnInstance' or 'BlueprintReadOnly' and 'ExposeOnSpawn' flags " ) ) ;
}
if ( 0 = = ( CPF_BlueprintVisible & Flags ) )
{
UE_LOG ( LogCompile , Warning , TEXT ( " Property cannot have 'ExposeOnSpawn' with 'BlueprintVisible' flag. " ) ) ;
}
Flags | = CPF_ExposeOnSpawn ;
}
}
2014-03-14 14:13:41 -04:00
if ( CurrentAccessSpecifier = = ACCESS_Public | | VariableCategory ! = EVariableCategory : : Member )
{
Flags & = ~ CPF_Protected ;
ExportFlags | = PROPEXPORT_Public ;
ExportFlags & = ~ ( PROPEXPORT_Private | PROPEXPORT_Protected ) ;
}
else if ( CurrentAccessSpecifier = = ACCESS_Protected )
{
Flags | = CPF_Protected ;
ExportFlags | = PROPEXPORT_Protected ;
ExportFlags & = ~ ( PROPEXPORT_Public | PROPEXPORT_Private ) ;
}
else if ( CurrentAccessSpecifier = = ACCESS_Private )
{
Flags & = ~ CPF_Protected ;
ExportFlags | = PROPEXPORT_Private ;
ExportFlags & = ~ ( PROPEXPORT_Public | PROPEXPORT_Protected ) ;
}
else
{
FError : : Throwf ( TEXT ( " Unknown access level " ) ) ;
}
// Get variable type.
bool bUnconsumedStructKeyword = false ;
bool bUnconsumedClassKeyword = false ;
bool bUnconsumedEnumKeyword = false ;
bool bUnconsumedConstKeyword = false ;
if ( MatchIdentifier ( TEXT ( " const " ) ) )
{
//@TODO: UCREMOVAL: Should use this to set the new (currently non-existent) CPF_Const flag appropriately!
bUnconsumedConstKeyword = true ;
}
if ( MatchIdentifier ( TEXT ( " mutable " ) ) )
{
//@TODO: Should flag as settable from a const context, but this is at least good enough to allow use for C++ land
}
if ( MatchIdentifier ( TEXT ( " struct " ) ) )
{
bUnconsumedStructKeyword = true ;
}
else if ( MatchIdentifier ( TEXT ( " class " ) ) )
{
bUnconsumedClassKeyword = true ;
}
else if ( MatchIdentifier ( TEXT ( " enum " ) ) )
{
if ( VariableCategory = = EVariableCategory : : Member )
{
2015-03-19 13:27:48 -04:00
FError : : Throwf ( TEXT ( " %s: Cannot declare enum at variable declaration " ) , GetHintText ( VariableCategory ) ) ;
2014-03-14 14:13:41 -04:00
}
bUnconsumedEnumKeyword = true ;
}
//
FToken VarType ;
if ( ! GetIdentifier ( VarType , 1 ) )
{
2015-03-19 13:27:48 -04:00
FError : : Throwf ( TEXT ( " %s: Missing variable type " ) , GetHintText ( VariableCategory ) ) ;
2014-03-14 14:13:41 -04:00
}
2014-08-12 08:51:25 -04:00
if ( VarType . Matches ( TEXT ( " int8 " ) ) )
2014-03-14 14:13:41 -04:00
{
VarProperty = FPropertyBase ( CPT_Int8 ) ;
}
else if ( VarType . Matches ( TEXT ( " int16 " ) ) )
{
VarProperty = FPropertyBase ( CPT_Int16 ) ;
}
else if ( VarType . Matches ( TEXT ( " int32 " ) ) )
{
VarProperty = FPropertyBase ( CPT_Int ) ;
}
else if ( VarType . Matches ( TEXT ( " int64 " ) ) )
{
VarProperty = FPropertyBase ( CPT_Int64 ) ;
}
else if ( VarType . Matches ( TEXT ( " uint32 " ) ) & & IsBitfieldProperty ( ) )
{
// 32-bit bitfield (bool) type, treat it like 8 bit type
VarProperty = FPropertyBase ( CPT_Bool8 ) ;
}
else if ( VarType . Matches ( TEXT ( " uint16 " ) ) & & IsBitfieldProperty ( ) )
{
// 16-bit bitfield (bool) type, treat it like 8 bit type.
VarProperty = FPropertyBase ( CPT_Bool8 ) ;
}
else if ( VarType . Matches ( TEXT ( " uint8 " ) ) & & IsBitfieldProperty ( ) )
{
// 8-bit bitfield (bool) type
VarProperty = FPropertyBase ( CPT_Bool8 ) ;
}
else if ( VarType . Matches ( TEXT ( " bool " ) ) )
{
if ( IsBitfieldProperty ( ) )
{
FError : : Throwf ( TEXT ( " bool bitfields are not supported. " ) ) ;
}
// C++ bool type
VarProperty = FPropertyBase ( CPT_Bool ) ;
}
else if ( VarType . Matches ( TEXT ( " uint8 " ) ) )
{
// Intrinsic Byte type.
VarProperty = FPropertyBase ( CPT_Byte ) ;
}
else if ( VarType . Matches ( TEXT ( " uint16 " ) ) )
{
VarProperty = FPropertyBase ( CPT_UInt16 ) ;
}
else if ( VarType . Matches ( TEXT ( " uint32 " ) ) )
{
VarProperty = FPropertyBase ( CPT_UInt32 ) ;
}
else if ( VarType . Matches ( TEXT ( " uint64 " ) ) )
{
VarProperty = FPropertyBase ( CPT_UInt64 ) ;
}
else if ( VarType . Matches ( TEXT ( " float " ) ) )
{
// Intrinsic single precision floating point type.
VarProperty = FPropertyBase ( CPT_Float ) ;
}
else if ( VarType . Matches ( TEXT ( " double " ) ) )
{
// Intrinsic double precision floating point type type.
VarProperty = FPropertyBase ( CPT_Double ) ;
}
else if ( VarType . Matches ( TEXT ( " FName " ) ) )
{
// Intrinsic Name type.
VarProperty = FPropertyBase ( CPT_Name ) ;
}
else if ( VarType . Matches ( TEXT ( " TArray " ) ) )
{
RequireSymbol ( TEXT ( " < " ) , TEXT ( " 'tarray' " ) ) ;
// GetVarType() clears the property flags of the array var, so use dummy
// flags when getting the inner property
uint64 OriginalVarTypeFlags = VarType . PropertyFlags ;
VarType . PropertyFlags | = Flags ;
2015-03-19 13:27:48 -04:00
GetVarType ( AllClasses , Scope , VarProperty , Disallow , & VarType , EPropertyDeclarationStyle : : None , VariableCategory ) ;
2015-04-21 10:25:59 -04:00
if ( VarProperty . ArrayType ! = EArrayType : : None | | VarProperty . MapKeyProp . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2015-04-21 10:25:59 -04:00
FError : : Throwf ( TEXT ( " Nested containers are not supported. " ) ) ;
2014-03-14 14:13:41 -04:00
}
OriginalVarTypeFlags | = VarProperty . PropertyFlags & ( CPF_ContainsInstancedReference | CPF_InstancedReference ) ; // propagate these to the array, we will fix them later
VarType . PropertyFlags = OriginalVarTypeFlags ;
VarProperty . ArrayType = EArrayType : : Dynamic ;
2015-02-23 08:53:06 -05:00
FToken CloseTemplateToken ;
if ( ! GetToken ( CloseTemplateToken , /*bNoConsts=*/ true , ESymbolParseOption : : CloseTemplateBracket ) )
{
FError : : Throwf ( TEXT ( " Missing token while parsing TArray. " ) ) ;
}
if ( CloseTemplateToken . TokenType ! = TOKEN_Symbol | | FCString : : Stricmp ( CloseTemplateToken . Identifier , TEXT ( " > " ) ) )
{
// If we didn't find a comma, report it
if ( FCString : : Stricmp ( CloseTemplateToken . Identifier , TEXT ( " , " ) ) )
{
FError : : Throwf ( TEXT ( " Expected '>' but found '%s' " ) , CloseTemplateToken . Identifier ) ;
}
// If we found a comma, read the next thing, assume it's an allocator, and report that
FToken AllocatorToken ;
if ( ! GetToken ( AllocatorToken , /*bNoConsts=*/ true , ESymbolParseOption : : CloseTemplateBracket ) )
{
FError : : Throwf ( TEXT ( " Expected '>' but found '%s' " ) , CloseTemplateToken . Identifier ) ;
}
FError : : Throwf ( TEXT ( " Found '%s' - explicit allocators are not supported in TArray properties. " ) , AllocatorToken . Identifier ) ;
}
2014-03-14 14:13:41 -04:00
}
2015-04-21 10:25:59 -04:00
else if ( VarType . Matches ( TEXT ( " TMap " ) ) )
{
RequireSymbol ( TEXT ( " < " ) , TEXT ( " 'tmap' " ) ) ;
// GetVarType() clears the property flags of the array var, so use dummy
// flags when getting the inner property
uint64 OriginalVarTypeFlags = VarType . PropertyFlags ;
VarType . PropertyFlags | = Flags ;
FToken MapKeyType ;
GetVarType ( AllClasses , Scope , MapKeyType , Disallow , & VarType , EPropertyDeclarationStyle : : None , VariableCategory ) ;
if ( VarProperty . ArrayType ! = EArrayType : : None | | VarProperty . MapKeyProp . IsValid ( ) )
{
FError : : Throwf ( TEXT ( " Nested containers are not supported. " ) ) ;
}
if ( MapKeyType . Type = = CPT_Struct )
{
FError : : Throwf ( TEXT ( " USTRUCTs are not currently supported as key types. " ) ) ;
}
FToken CommaToken ;
if ( ! GetToken ( CommaToken , /*bNoConsts=*/ true ) | | CommaToken . TokenType ! = TOKEN_Symbol | | FCString : : Stricmp ( CommaToken . Identifier , TEXT ( " , " ) ) )
{
FError : : Throwf ( TEXT ( " Missing value type while parsing TMap. " ) ) ;
}
GetVarType ( AllClasses , Scope , VarProperty , Disallow , & VarType , EPropertyDeclarationStyle : : None , VariableCategory ) ;
if ( VarProperty . ArrayType ! = EArrayType : : None | | VarProperty . MapKeyProp . IsValid ( ) )
{
FError : : Throwf ( TEXT ( " Nested containers are not supported. " ) ) ;
}
OriginalVarTypeFlags | = VarProperty . PropertyFlags & ( CPF_ContainsInstancedReference | CPF_InstancedReference ) ; // propagate these to the array, we will fix them later
OriginalVarTypeFlags | = MapKeyType . PropertyFlags & ( CPF_ContainsInstancedReference | CPF_InstancedReference ) ; // propagate these to the array, we will fix them later
VarType . PropertyFlags = OriginalVarTypeFlags ;
VarProperty . MapKeyProp = MakeShareable < FToken > ( new FToken ( MapKeyType ) ) ;
VarProperty . MapKeyProp - > PropertyFlags = OriginalVarTypeFlags ;
FToken CloseTemplateToken ;
if ( ! GetToken ( CloseTemplateToken , /*bNoConsts=*/ true , ESymbolParseOption : : CloseTemplateBracket ) )
{
FError : : Throwf ( TEXT ( " Missing token while parsing TMap. " ) ) ;
}
if ( CloseTemplateToken . TokenType ! = TOKEN_Symbol | | FCString : : Stricmp ( CloseTemplateToken . Identifier , TEXT ( " > " ) ) )
{
// If we didn't find a comma, report it
if ( FCString : : Stricmp ( CloseTemplateToken . Identifier , TEXT ( " , " ) ) )
{
FError : : Throwf ( TEXT ( " Expected '>' but found '%s' " ) , CloseTemplateToken . Identifier ) ;
}
// If we found a comma, read the next thing, assume it's an allocator, and report that
FToken AllocatorToken ;
if ( ! GetToken ( AllocatorToken , /*bNoConsts=*/ true , ESymbolParseOption : : CloseTemplateBracket ) )
{
FError : : Throwf ( TEXT ( " Expected '>' but found '%s' " ) , CloseTemplateToken . Identifier ) ;
}
FError : : Throwf ( TEXT ( " Found '%s' - explicit allocators are not supported in TMap properties. " ) , AllocatorToken . Identifier ) ;
}
}
2014-03-14 14:13:41 -04:00
else if ( VarType . Matches ( TEXT ( " FString " ) ) )
{
VarProperty = FPropertyBase ( CPT_String ) ;
if ( VariableCategory ! = EVariableCategory : : Member )
{
if ( MatchSymbol ( TEXT ( " & " ) ) )
{
if ( Flags & CPF_ConstParm )
{
// 'const FString& Foo' came from 'FString' in .uc, no flags
Flags & = ~ CPF_ConstParm ;
// We record here that we encountered a const reference, because we need to remove that information from flags for code generation purposes.
VarProperty . RefQualifier = ERefQualifier : : ConstRef ;
}
else
{
// 'FString& Foo' came from 'out FString' in .uc
Flags | = CPF_OutParm ;
// And we record here that we encountered a non-const reference here too.
VarProperty . RefQualifier = ERefQualifier : : NonConstRef ;
}
}
}
}
else if ( VarType . Matches ( TEXT ( " Text " ) ) )
{
FError : : Throwf ( TEXT ( " %s' is missing a prefix, expecting 'FText' " ) , VarType . Identifier ) ;
}
else if ( VarType . Matches ( TEXT ( " FText " ) ) )
{
VarProperty = FPropertyBase ( CPT_Text ) ;
}
else if ( VarType . Matches ( TEXT ( " TEnumAsByte " ) ) )
{
RequireSymbol ( TEXT ( " < " ) , VarType . Identifier ) ;
// Eat the forward declaration enum text if present
MatchIdentifier ( TEXT ( " enum " ) ) ;
2014-08-12 08:51:25 -04:00
bool bFoundEnum = false ;
2014-03-14 14:13:41 -04:00
FToken InnerEnumType ;
if ( GetIdentifier ( InnerEnumType , true ) )
{
if ( UEnum * Enum = FindObject < UEnum > ( ANY_PACKAGE , InnerEnumType . Identifier ) )
{
// In-scope enumeration.
2014-08-12 08:51:25 -04:00
VarProperty = FPropertyBase ( Enum , CPT_Byte ) ;
bFoundEnum = true ;
}
2014-03-14 14:13:41 -04:00
}
// Try to handle namespaced enums
// Note: We do not verify the scoped part is correct, and trust in the C++ compiler to catch that sort of mistake
if ( MatchSymbol ( TEXT ( " :: " ) ) )
{
FToken ScopedTrueEnumName ;
if ( ! GetIdentifier ( ScopedTrueEnumName , true ) )
{
FError : : Throwf ( TEXT ( " Expected a namespace scoped enum name. " ) ) ;
}
}
2014-08-12 08:51:25 -04:00
if ( ! bFoundEnum )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Expected the name of a previously defined enum " ) ) ;
}
2014-05-22 05:21:52 -04:00
RequireSymbol ( TEXT ( " > " ) , VarType . Identifier , ESymbolParseOption : : CloseTemplateBracket ) ;
2014-03-14 14:13:41 -04:00
}
2014-08-12 08:51:25 -04:00
else if ( UEnum * Enum = FindObject < UEnum > ( ANY_PACKAGE , VarType . Identifier ) )
2014-03-14 14:13:41 -04:00
{
2014-08-12 08:51:25 -04:00
EPropertyType UnderlyingType = CPT_Byte ;
2014-03-14 14:13:41 -04:00
if ( VariableCategory = = EVariableCategory : : Member )
{
2015-04-22 14:47:12 -04:00
auto * EnumUnderlyingType = GEnumUnderlyingTypes . Find ( Enum ) ;
if ( ! EnumUnderlyingType | | * EnumUnderlyingType ! = CPT_Byte )
2014-08-12 08:51:25 -04:00
{
FError : : Throwf ( TEXT ( " You cannot use the raw enum name as a type for member variables, instead use TEnumAsByte or a C++11 enum class with an explicit underlying type (currently only uint8 supported). " ) , * Enum - > CppType ) ;
}
2014-03-14 14:13:41 -04:00
}
// Try to handle namespaced enums
// Note: We do not verify the scoped part is correct, and trust in the C++ compiler to catch that sort of mistake
if ( MatchSymbol ( TEXT ( " :: " ) ) )
{
FToken ScopedTrueEnumName ;
if ( ! GetIdentifier ( ScopedTrueEnumName , true ) )
{
FError : : Throwf ( TEXT ( " Expected a namespace scoped enum name. " ) ) ;
}
}
// In-scope enumeration.
2014-08-12 08:51:25 -04:00
VarProperty = FPropertyBase ( Enum , UnderlyingType ) ;
2014-03-14 14:13:41 -04:00
bUnconsumedEnumKeyword = false ;
}
else
{
// Check for structs/classes
bool bHandledType = false ;
FString IdentifierStripped = GetClassNameWithPrefixRemoved ( VarType . Identifier ) ;
bool bStripped = false ;
UScriptStruct * Struct = FindObject < UScriptStruct > ( ANY_PACKAGE , VarType . Identifier ) ;
if ( ! Struct )
{
Struct = FindObject < UScriptStruct > ( ANY_PACKAGE , * IdentifierStripped ) ;
bStripped = true ;
}
if ( Struct )
{
if ( bStripped )
{
const TCHAR * PrefixCPP = StructsWithTPrefix . Contains ( IdentifierStripped ) ? TEXT ( " T " ) : Struct - > GetPrefixCPP ( ) ;
FString ExpectedStructName = FString : : Printf ( TEXT ( " %s%s " ) , PrefixCPP , * Struct - > GetName ( ) ) ;
if ( FString ( VarType . Identifier ) ! = ExpectedStructName )
{
FError : : Throwf ( TEXT ( " Struct '%s' is missing or has an incorrect prefix, expecting '%s' " ) , VarType . Identifier , * ExpectedStructName ) ;
}
}
else if ( ! StructsWithNoPrefix . Contains ( VarType . Identifier ) )
{
const TCHAR * PrefixCPP = StructsWithTPrefix . Contains ( VarType . Identifier ) ? TEXT ( " T " ) : Struct - > GetPrefixCPP ( ) ;
FError : : Throwf ( TEXT ( " Struct '%s' is missing a prefix, expecting '%s' " ) , VarType . Identifier , * FString : : Printf ( TEXT ( " %s%s " ) , PrefixCPP , * Struct - > GetName ( ) ) ) ;
}
bHandledType = true ;
VarProperty = FPropertyBase ( Struct ) ;
if ( ( Struct - > StructFlags & STRUCT_HasInstancedReference ) & & ! ( Disallow & CPF_ContainsInstancedReference ) )
{
Flags | = CPF_ContainsInstancedReference ;
}
// Struct keyword in front of a struct is legal, we 'consume' it
bUnconsumedStructKeyword = false ;
}
2015-03-27 16:11:50 -04:00
else if ( FindObject < UScriptStruct > ( ANY_PACKAGE , * IdentifierStripped ) ! = nullptr )
2014-03-14 14:13:41 -04:00
{
bHandledType = true ;
// Struct keyword in front of a struct is legal, we 'consume' it
bUnconsumedStructKeyword = false ;
}
2015-01-20 09:33:54 -05:00
else if ( UFunction * DelegateFunc = Cast < UFunction > ( Scope - > FindTypeByName ( * ( IdentifierStripped + HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ) ) ) )
2014-03-14 14:13:41 -04:00
{
bHandledType = true ;
VarProperty = FPropertyBase ( DelegateFunc - > HasAnyFunctionFlags ( FUNC_MulticastDelegate ) ? CPT_MulticastDelegate : CPT_Delegate ) ;
VarProperty . DelegateName = * IdentifierStripped ;
if ( ! ( Disallow & CPF_InstancedReference ) )
{
Flags | = CPF_InstancedReference ;
}
}
else
{
// An object reference of some type (maybe a restricted class?)
UClass * TempClass = NULL ;
2015-03-16 15:05:08 -04:00
const bool bIsLazyPtrTemplate = VarType . Matches ( TEXT ( " TLazyObjectPtr " ) ) ;
const bool bIsAssetPtrTemplate = VarType . Matches ( TEXT ( " TAssetPtr " ) ) ;
const bool bIsAssetClassTemplate = VarType . Matches ( TEXT ( " TAssetSubclassOf " ) ) ;
const bool bIsWeakPtrTemplate = VarType . Matches ( TEXT ( " TWeakObjectPtr " ) ) ;
const bool bIsAutoweakPtrTemplate = VarType . Matches ( TEXT ( " TAutoWeakObjectPtr " ) ) ;
2014-03-14 14:13:41 -04:00
const bool bIsScriptInterfaceWrapper = VarType . Matches ( TEXT ( " TScriptInterface " ) ) ;
2015-03-16 15:05:08 -04:00
const bool bIsSubobjectPtrTemplate = VarType . Matches ( TEXT ( " TSubobjectPtr " ) ) ;
bool bIsWeak = false ;
bool bIsLazy = false ;
bool bIsAsset = false ;
bool bWeakIsAuto = false ;
2014-03-14 14:13:41 -04:00
if ( VarType . Matches ( TEXT ( " TSubclassOf " ) ) )
{
TempClass = UClass : : StaticClass ( ) ;
}
2014-04-02 18:09:23 -04:00
else if ( VarType . Matches ( TEXT ( " FScriptInterface " ) ) )
{
TempClass = UInterface : : StaticClass ( ) ;
2015-04-10 14:19:15 -04:00
Flags | = CPF_UObjectWrapper ;
2014-04-02 18:09:23 -04:00
}
2014-03-14 14:13:41 -04:00
else if ( bIsAssetClassTemplate )
{
TempClass = UClass : : StaticClass ( ) ;
bIsAsset = true ;
}
else if ( bIsLazyPtrTemplate | | bIsWeakPtrTemplate | | bIsAutoweakPtrTemplate | | bIsScriptInterfaceWrapper | | bIsAssetPtrTemplate | | bIsSubobjectPtrTemplate )
{
RequireSymbol ( TEXT ( " < " ) , VarType . Identifier ) ;
// Consume a forward class declaration 'class' if present
MatchIdentifier ( TEXT ( " class " ) ) ;
// Also consume const
MatchIdentifier ( TEXT ( " const " ) ) ;
// Find the lazy/weak class
FToken InnerClass ;
if ( GetIdentifier ( InnerClass ) )
{
2014-04-23 18:42:01 -04:00
TempClass = AllClasses . FindScriptClass ( InnerClass . Identifier ) ;
2014-06-28 16:36:30 -04:00
if ( TempClass = = nullptr )
{
FError : : Throwf ( TEXT ( " Unrecognized type '%s' (in expression %s<%s>) " ) , InnerClass . Identifier , VarType . Identifier , InnerClass . Identifier ) ;
}
2014-03-14 14:13:41 -04:00
if ( bIsAutoweakPtrTemplate )
{
bIsWeak = true ;
bWeakIsAuto = true ;
}
else if ( bIsLazyPtrTemplate )
{
bIsLazy = true ;
}
else if ( bIsWeakPtrTemplate )
{
bIsWeak = true ;
}
else if ( bIsAssetPtrTemplate )
{
bIsAsset = true ;
}
else if ( bIsSubobjectPtrTemplate )
{
2014-10-16 09:02:30 -04:00
Flags | = CPF_SubobjectReference | CPF_InstancedReference ;
2014-03-14 14:13:41 -04:00
}
2015-04-10 14:19:15 -04:00
Flags | = CPF_UObjectWrapper ;
2014-03-14 14:13:41 -04:00
}
else
{
FError : : Throwf ( TEXT ( " %s: Missing template type " ) , VarType . Identifier ) ;
}
2014-05-22 05:21:52 -04:00
RequireSymbol ( TEXT ( " > " ) , VarType . Identifier , ESymbolParseOption : : CloseTemplateBracket ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-04-23 18:42:01 -04:00
TempClass = AllClasses . FindScriptClass ( VarType . Identifier ) ;
2014-03-14 14:13:41 -04:00
}
if ( TempClass ! = NULL )
{
bHandledType = true ;
bool bAllowWeak = ! ( Disallow & CPF_AutoWeak ) ; // if it is not allowing anything, force it strong. this is probably a function arg
2014-08-12 08:51:25 -04:00
VarProperty = FPropertyBase ( TempClass , NULL , bAllowWeak , bIsWeak , bWeakIsAuto , bIsLazy , bIsAsset ) ;
2014-03-14 14:13:41 -04:00
if ( TempClass - > IsChildOf ( UClass : : StaticClass ( ) ) )
{
if ( MatchSymbol ( TEXT ( " < " ) ) )
{
2015-04-10 14:19:15 -04:00
Flags | = CPF_UObjectWrapper ;
2014-03-14 14:13:41 -04:00
// Consume a forward class declaration 'class' if present
MatchIdentifier ( TEXT ( " class " ) ) ;
// Get the actual class type to restrict this to
FToken Limitor ;
if ( ! GetIdentifier ( Limitor ) )
{
FError : : Throwf ( TEXT ( " 'class': Missing class limitor " ) ) ;
}
2014-04-23 18:42:01 -04:00
VarProperty . MetaClass = AllClasses . FindScriptClassOrThrow ( Limitor . Identifier ) ;
2014-03-14 14:13:41 -04:00
2014-05-22 05:21:52 -04:00
RequireSymbol ( TEXT ( " > " ) , TEXT ( " 'class limitor' " ) , ESymbolParseOption : : CloseTemplateBracket ) ;
2014-03-14 14:13:41 -04:00
}
else
{
VarProperty . MetaClass = UObject : : StaticClass ( ) ;
}
if ( bIsWeak )
{
FError : : Throwf ( TEXT ( " Class variables cannot be weak, they are always strong. " ) ) ;
}
if ( bIsLazy )
{
FError : : Throwf ( TEXT ( " Class variables cannot be lazy, they are always strong. " ) ) ;
}
if ( bIsAssetPtrTemplate )
{
FError : : Throwf ( TEXT ( " Class variables cannot be stored in TAssetPtr, use TAssetSubclassOf instead. " ) ) ;
}
}
// Inherit instancing flags
if ( DoesAnythingInHierarchyHaveDefaultToInstanced ( TempClass ) )
{
Flags | = ( ( CPF_InstancedReference | CPF_ExportObject ) & ( ~ Disallow ) ) ;
}
// Eat the star that indicates this is a pointer to the UObject
2015-04-10 14:19:15 -04:00
if ( ! ( Flags & CPF_UObjectWrapper ) )
2014-03-14 14:13:41 -04:00
{
2014-06-10 09:17:04 -04:00
// Const after variable type but before pointer symbol
MatchIdentifier ( TEXT ( " const " ) ) ;
2014-03-14 14:13:41 -04:00
RequireSymbol ( TEXT ( " * " ) , TEXT ( " Expected a pointer type " ) ) ;
VarProperty . PointerType = EPointerType : : Native ;
}
// Imply const if it's a parameter that is a pointer to a const class
if ( VariableCategory ! = EVariableCategory : : Member & & ( TempClass ! = NULL ) & & ( TempClass - > HasAnyClassFlags ( CLASS_Const ) ) )
{
Flags | = CPF_ConstParm ;
}
// Class keyword in front of a class is legal, we 'consume' it
bUnconsumedClassKeyword = false ;
bUnconsumedConstKeyword = false ;
}
}
// Resolve delegates declared in another class //@TODO: UCREMOVAL: This seems extreme
if ( ! bHandledType )
{
2015-01-20 09:33:54 -05:00
if ( UFunction * DelegateFunc = ( UFunction * ) StaticFindObject ( UFunction : : StaticClass ( ) , ANY_PACKAGE , * ( IdentifierStripped + HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ) ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
bHandledType = true ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
VarProperty = FPropertyBase ( DelegateFunc - > HasAnyFunctionFlags ( FUNC_MulticastDelegate ) ? CPT_MulticastDelegate : CPT_Delegate ) ;
VarProperty . DelegateName = * IdentifierStripped ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
if ( ! ( Disallow & CPF_InstancedReference ) )
{
Flags | = CPF_InstancedReference ;
2014-03-14 14:13:41 -04:00
}
}
if ( ! bHandledType )
{
FError : : Throwf ( TEXT ( " Unrecognized type '%s' " ) , VarType . Identifier ) ;
}
}
2014-10-16 09:02:30 -04:00
if ( ( Flags & CPF_InstancedReference ) & & CurrentAccessSpecifier = = ACCESS_Private & & VariableCategory = = EVariableCategory : : Member )
{
if ( ( ( Flags & CPF_Edit ) & & ( Flags & CPF_EditConst ) = = 0 ) | |
( ( Flags & CPF_BlueprintVisible ) & & ( Flags & CPF_BlueprintReadOnly ) = = 0 ) )
{
FError : : Throwf ( TEXT ( " %s: Subobject (instanced) properties can't be editable (use VisibleAnywhere or BlueprintReadOnly instead). " ) , VarType . Identifier ) ;
}
}
2014-03-14 14:13:41 -04:00
}
2014-06-10 09:17:04 -04:00
if ( VariableCategory ! = EVariableCategory : : Member )
{
// const after the variable type support (only for params)
if ( MatchIdentifier ( TEXT ( " const " ) ) )
{
Flags | = CPF_ConstParm ;
}
}
2014-03-14 14:13:41 -04:00
if ( bUnconsumedConstKeyword )
{
2015-01-16 05:00:10 -05:00
if ( VariableCategory = = EVariableCategory : : Member )
{
FError : : Throwf ( TEXT ( " Const properties are not supported. " ) ) ;
}
else
{
FError : : Throwf ( TEXT ( " Inappropriate keyword 'const' on variable of type '%s' " ) , VarType . Identifier ) ;
}
2014-03-14 14:13:41 -04:00
}
if ( bUnconsumedClassKeyword )
{
FError : : Throwf ( TEXT ( " Inappropriate keyword 'class' on variable of type '%s' " ) , VarType . Identifier ) ;
}
if ( bUnconsumedStructKeyword )
{
FError : : Throwf ( TEXT ( " Inappropriate keyword 'struct' on variable of type '%s' " ) , VarType . Identifier ) ;
}
if ( bUnconsumedEnumKeyword )
{
FError : : Throwf ( TEXT ( " Inappropriate keyword 'enum' on variable of type '%s' " ) , VarType . Identifier ) ;
}
if ( MatchSymbol ( TEXT ( " * " ) ) )
{
2014-04-23 19:08:00 -04:00
FError : : Throwf ( TEXT ( " Inappropriate '*' on variable of type '%s', cannot have an exposed pointer to this type. " ) , VarType . Identifier ) ;
2014-03-14 14:13:41 -04:00
}
//@TODO: UCREMOVAL: 'const' member variables that will get written post-construction by defaultproperties
2015-01-20 09:33:54 -05:00
if ( VariableCategory = = EVariableCategory : : Member & & OwnerStruct - > IsA < UClass > ( ) & & ( ( UClass * ) OwnerStruct ) - > HasAnyClassFlags ( CLASS_Const ) )
2014-03-14 14:13:41 -04:00
{
// Eat a 'not quite truthful' const after the type; autogenerated for member variables of const classes.
MatchIdentifier ( TEXT ( " const " ) ) ;
}
// Arrays are passed by reference but are only implicitly so; setting it explicitly could cause a problem with replicated functions
if ( MatchSymbol ( TEXT ( " & " ) ) )
{
switch ( VariableCategory )
{
case EVariableCategory : : RegularParameter :
case EVariableCategory : : Return :
{
Flags | = CPF_OutParm ;
//@TODO: UCREMOVAL: How to determine if we have a ref param?
if ( Flags & CPF_ConstParm )
{
Flags | = CPF_ReferenceParm ;
}
}
break ;
case EVariableCategory : : ReplicatedParameter :
{
if ( ! ( Flags & CPF_ConstParm ) )
2014-10-07 11:04:06 -04:00
{
2014-03-14 14:13:41 -04:00
FError : : Throwf ( TEXT ( " Replicated %s parameters cannot be passed by non-const reference " ) , VarType . Identifier ) ;
2014-10-07 11:04:06 -04:00
}
Flags | = CPF_ReferenceParm ;
2014-03-14 14:13:41 -04:00
}
break ;
default :
{
}
break ;
}
if ( Flags & CPF_ConstParm )
{
VarProperty . RefQualifier = ERefQualifier : : ConstRef ;
}
else
{
VarProperty . RefQualifier = ERefQualifier : : NonConstRef ;
}
}
VarProperty . PropertyExportFlags = ExportFlags ;
// Set FPropertyBase info.
VarProperty . PropertyFlags | = Flags ;
// Set the RepNotify name, if the variable needs it
if ( VarProperty . PropertyFlags & CPF_RepNotify )
{
if ( RepCallbackName ! = NAME_None )
{
VarProperty . RepNotifyName = RepCallbackName ;
}
else
{
FError : : Throwf ( TEXT ( " Must specify a valid function name for replication notifications " ) ) ;
}
}
// Perform some more specific validation on the property flags
2014-09-24 09:09:32 -04:00
if ( ( VarProperty . PropertyFlags & CPF_PersistentInstance ) & & VarProperty . Type ! = CPT_ObjectReference )
2014-08-27 14:39:09 -04:00
{
2014-09-01 14:15:28 -04:00
FError : : Throwf ( TEXT ( " 'Instanced' is only allowed on object property (or array of objects) " ) ) ;
2014-08-27 14:39:09 -04:00
}
2014-03-14 14:13:41 -04:00
if ( VarProperty . IsObject ( ) & & VarProperty . MetaClass = = NULL & & ( VarProperty . PropertyFlags & CPF_Config ) ! = 0 )
{
FError : : Throwf ( TEXT ( " Not allowed to use 'config' with object variables " ) ) ;
}
if ( ( VarProperty . PropertyFlags & CPF_BlueprintAssignable ) & & VarProperty . Type ! = CPT_MulticastDelegate )
{
FError : : Throwf ( TEXT ( " 'BlueprintAssignable' is only allowed on multicast delegate properties " ) ) ;
}
if ( ( VarProperty . PropertyFlags & CPF_BlueprintCallable ) & & VarProperty . Type ! = CPT_MulticastDelegate )
{
2014-09-05 17:50:18 -04:00
FError : : Throwf ( TEXT ( " 'BlueprintCallable' is only allowed on a property when it is a multicast delegate " ) ) ;
2014-03-14 14:13:41 -04:00
}
if ( ( VarProperty . PropertyFlags & CPF_BlueprintAuthorityOnly ) & & VarProperty . Type ! = CPT_MulticastDelegate )
{
2014-09-05 17:50:18 -04:00
FError : : Throwf ( TEXT ( " 'BlueprintAuthorityOnly' is only allowed on a property when it is a multicast delegate " ) ) ;
2014-03-14 14:13:41 -04:00
}
if ( VariableCategory ! = EVariableCategory : : Member )
{
// These conditions are checked externally for struct/member variables where the flag can be inferred later on from the variable name itself
ValidatePropertyIsDeprecatedIfNecessary ( VarProperty , OuterPropertyType ) ;
}
// Check for invalid transients
2014-08-19 15:07:24 -04:00
uint64 Transients = VarProperty . PropertyFlags & ( CPF_DuplicateTransient | CPF_TextExportTransient | CPF_NonPIEDuplicateTransient ) ;
2015-01-20 09:33:54 -05:00
if ( Transients & & ! Cast < UClass > ( OwnerStruct ) )
2014-03-14 14:13:41 -04:00
{
TArray < const TCHAR * > FlagStrs = ParsePropertyFlags ( Transients ) ;
FError : : Throwf ( TEXT ( " '%s' specifier(s) are only allowed on class member variables " ) , * FString : : Join ( FlagStrs , TEXT ( " , " ) ) ) ;
}
// Make sure the overrides are allowed here.
if ( VarProperty . PropertyFlags & Disallow )
{
FError : : Throwf ( TEXT ( " Specified type modifiers not allowed here " ) ) ;
}
2015-04-21 10:25:59 -04:00
// For now, copy the flags that a TMap value has to the key
if ( FPropertyBase * KeyProp = VarProperty . MapKeyProp . Get ( ) )
{
KeyProp - > PropertyFlags = VarProperty . PropertyFlags ;
}
2014-03-14 14:13:41 -04:00
VarProperty . MetaData = MetaDataFromNewStyle ;
2014-09-30 09:10:54 -04:00
if ( ParsedVarIndexRange )
{
ParsedVarIndexRange - > Count = InputPos - ParsedVarIndexRange - > StartIndex ;
}
2014-03-14 14:13:41 -04:00
}
/**
* If the property has already been seen during compilation , then return add . If not ,
* then return replace so that INI files don ' t mess with header exporting
*
* @ param PropertyName the string token for the property
*
* @ return FNAME_Replace_Not_Safe_For_Threading or FNAME_Add
*/
EFindName FHeaderParser : : GetFindFlagForPropertyName ( const TCHAR * PropertyName )
{
static TMap < FString , int32 > PreviousNames ;
FString PropertyStr ( PropertyName ) ;
FString UpperPropertyStr = PropertyStr . ToUpper ( ) ;
// See if it's in the list already
if ( PreviousNames . Find ( UpperPropertyStr ) )
{
return FNAME_Add ;
}
// Add it to the list for future look ups
PreviousNames . Add ( UpperPropertyStr , 1 ) ;
// Check for a mismatch between the INI file and the config property name
FName CurrentText ( PropertyName , FNAME_Find ) ;
if ( CurrentText ! = NAME_None & &
FCString : : Strcmp ( PropertyName , * CurrentText . ToString ( ) ) ! = 0 )
{
FError : : Throwf (
TEXT ( " INI file contains an incorrect case for (%s) should be (%s) " ) ,
* CurrentText . ToString ( ) ,
PropertyName ) ;
}
return FNAME_Replace_Not_Safe_For_Threading ;
}
UProperty * FHeaderParser : : GetVarNameAndDim
(
2015-03-19 11:33:15 -04:00
UStruct * Scope ,
FToken & VarProperty ,
EVariableCategory : : Type VariableCategory
2014-03-14 14:13:41 -04:00
)
{
check ( Scope ) ;
2015-03-19 11:33:15 -04:00
EObjectFlags ObjectFlags = RF_Public ;
if ( VariableCategory = = EVariableCategory : : Member & & CurrentAccessSpecifier = = ACCESS_Private )
{
ObjectFlags = RF_NoFlags ;
}
2015-03-19 13:27:48 -04:00
const TCHAR * HintText = GetHintText ( VariableCategory ) ;
2015-01-20 09:33:54 -05:00
AddModuleRelativePathToMetadata ( Scope , VarProperty . MetaData ) ;
2014-03-14 14:13:41 -04:00
// Get variable name.
2015-03-20 12:52:40 -04:00
if ( VariableCategory = = EVariableCategory : : Return )
2014-03-14 14:13:41 -04:00
{
// Hard-coded variable name, such as with return value.
VarProperty . TokenType = TOKEN_Identifier ;
2015-03-20 12:52:40 -04:00
FCString : : Strcpy ( VarProperty . Identifier , TEXT ( " ReturnValue " ) ) ;
2014-03-14 14:13:41 -04:00
}
else
{
FToken VarToken ;
if ( ! GetIdentifier ( VarToken ) )
{
FError : : Throwf ( TEXT ( " Missing variable name " ) ) ;
}
VarProperty . TokenType = TOKEN_Identifier ;
FCString : : Strcpy ( VarProperty . Identifier , VarToken . Identifier ) ;
}
// Check to see if the variable is deprecated, and if so set the flag
{
FString VarName ( VarProperty . Identifier ) ;
int32 DeprecatedIndex = VarName . Find ( TEXT ( " _DEPRECATED " ) ) ;
if ( DeprecatedIndex ! = INDEX_NONE )
{
if ( DeprecatedIndex ! = VarName . Len ( ) - 11 )
{
FError : : Throwf ( TEXT ( " Deprecated variables must end with _DEPRECATED " ) ) ;
}
// Warn if a deprecated property is visible
if ( VarProperty . PropertyFlags & ( CPF_Edit | CPF_EditConst | CPF_BlueprintVisible | CPF_BlueprintReadOnly ) )
{
UE_LOG ( LogCompile , Warning , TEXT ( " %s: Deprecated property '%s' should not be marked as visible or editable " ) , HintText , * VarName ) ;
}
VarProperty . PropertyFlags | = CPF_Deprecated ;
VarName = VarName . Mid ( 0 , DeprecatedIndex ) ;
FCString : : Strcpy ( VarProperty . Identifier , * VarName ) ;
}
}
// Make sure it doesn't conflict.
int32 OuterContextCount = 0 ;
UField * Existing = FindField ( Scope , VarProperty . Identifier , true , UField : : StaticClass ( ) , NULL ) ;
if ( Existing ! = NULL )
{
if ( Existing - > GetOuter ( ) = = Scope )
{
FError : : Throwf ( TEXT ( " %s: '%s' already defined " ) , HintText , VarProperty . Identifier ) ;
}
else if ( ( Cast < UFunction > ( Scope ) ! = NULL | | Cast < UClass > ( Scope ) ! = NULL ) // declaring class member or function local/parm
& & Cast < UFunction > ( Existing ) = = NULL // and the existing field isn't a function
& & Cast < UClass > ( Existing - > GetOuter ( ) ) ! = NULL ) // and the existing field is a class member (don't care about locals in other functions)
{
// don't allow it to obscure class properties either
if ( Existing - > IsA ( UScriptStruct : : StaticClass ( ) ) )
{
FError : : Throwf ( TEXT ( " %s: '%s' conflicts with struct defined in %s'%s' " ) , HintText , VarProperty . Identifier , ( OuterContextCount > 0 ) ? TEXT ( " 'within' class " ) : TEXT ( " " ) , * Existing - > GetOuter ( ) - > GetName ( ) ) ;
}
else
{
// if this is a property and one of them is deprecated, ignore it since it will be removed soon
UProperty * ExistingProp = Cast < UProperty > ( Existing ) ;
if ( ExistingProp = = NULL
| | ( ! ExistingProp - > HasAnyPropertyFlags ( CPF_Deprecated ) & & ( VarProperty . PropertyFlags & CPF_Deprecated ) = = 0 ) )
{
FError : : Throwf ( TEXT ( " %s: '%s' conflicts with previously defined field in %s'%s' " ) , HintText , VarProperty . Identifier , ( OuterContextCount > 0 ) ? TEXT ( " 'within' class " ) : TEXT ( " " ) , * Existing - > GetOuter ( ) - > GetName ( ) ) ;
}
}
}
}
// Get optional dimension immediately after name.
FToken Dimensions ;
if ( MatchSymbol ( TEXT ( " [ " ) ) )
{
2015-03-20 13:26:37 -04:00
if ( VariableCategory = = EVariableCategory : : Return )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Arrays aren't allowed in this context " ) ) ;
}
2015-04-21 10:25:59 -04:00
if ( VarProperty . ArrayType = = EArrayType : : Dynamic | | VarProperty . MapKeyProp . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2015-04-21 10:25:59 -04:00
FError : : Throwf ( TEXT ( " Static arrays of containers are not allowed " ) ) ;
2014-03-14 14:13:41 -04:00
}
if ( VarProperty . IsBool ( ) )
{
FError : : Throwf ( TEXT ( " Bool arrays are not allowed " ) ) ;
}
// Ignore how the actual array dimensions are actually defined - we'll calculate those with the compiler anyway.
if ( ! GetRawToken ( Dimensions , TEXT ( ' ] ' ) ) )
{
FError : : Throwf ( TEXT ( " %s %s: Missing ']' " ) , HintText , VarProperty . Identifier ) ;
}
// Only static arrays are declared with []. Dynamic arrays use TArray<> instead.
VarProperty . ArrayType = EArrayType : : Static ;
UEnum * Enum = NULL ;
if ( * Dimensions . String )
{
UEnum : : LookupEnumNameSlow ( Dimensions . String , & Enum ) ;
}
if ( ! Enum )
{
// If the enum wasn't declared in this scope, then try to find it anywhere we can
Enum = FindObject < UEnum > ( ANY_PACKAGE , Dimensions . String ) ;
}
if ( Enum )
{
// set the ArraySizeEnum if applicable
VarProperty . MetaData . Add ( " ArraySizeEnum " , Enum - > GetPathName ( ) ) ;
}
MatchSymbol ( TEXT ( " ] " ) ) ;
}
// Try gathering metadata for member fields
2015-03-20 13:11:34 -04:00
if ( VariableCategory = = EVariableCategory : : Member )
2014-03-14 14:13:41 -04:00
{
ParseFieldMetaData ( VarProperty . MetaData , VarProperty . Identifier ) ;
AddFormattedPrevCommentAsTooltipMetaData ( VarProperty . MetaData ) ;
}
2014-04-23 20:18:55 -04:00
// validate UFunction parameters
2015-03-20 13:11:34 -04:00
else
2014-04-23 20:18:55 -04:00
{
// UFunctions with a smart pointer as input parameter wont compile anyway, because of missing P_GET_... macro.
// UFunctions with a smart pointer as return type will crash when called via blueprint, because they are not supported in VM.
// WeakPointer is supported by VM as return type (see UObject::execLetWeakObjPtr), but there is no P_GET_... macro for WeakPointer.
if ( ( VarProperty . Type = = CPT_LazyObjectReference ) | | ( VarProperty . Type = = CPT_AssetObjectReference ) )
{
FError : : Throwf ( TEXT ( " UFunctions cannot take a smart pointer (LazyPtr, AssetPtr, etc) as a parameter. " ) ) ;
}
}
2014-03-14 14:13:41 -04:00
// If this is the first time seeing the property name, then flag it for replace instead of add
const EFindName FindFlag = VarProperty . PropertyFlags & CPF_Config ? GetFindFlagForPropertyName ( VarProperty . Identifier ) : FNAME_Add ;
// create the FName for the property, splitting (ie Unnamed_3 -> Unnamed,3)
FName PropertyName ( VarProperty . Identifier , FindFlag , true ) ;
// Add property.
UProperty * NewProperty = NULL ;
{
UProperty * Prev = NULL ;
for ( TFieldIterator < UProperty > It ( Scope , EFieldIteratorFlags : : ExcludeSuper ) ; It ; + + It )
{
Prev = * It ;
}
2015-04-21 10:25:59 -04:00
UArrayProperty * Array = nullptr ;
UMapProperty * Map = nullptr ;
UProperty * NewMapKeyProperty = nullptr ;
UObject * NewScope = Scope ;
int32 ArrayDim = 1 ; // 1 = not a static array, 2 = static array
2014-03-14 14:13:41 -04:00
if ( VarProperty . ArrayType = = EArrayType : : Dynamic )
{
2015-04-21 10:25:59 -04:00
Array = new ( EC_InternalUseOnlyConstructor , Scope , PropertyName , ObjectFlags ) UArrayProperty ( FObjectInitializer ( ) ) ;
2014-03-14 14:13:41 -04:00
NewScope = Array ;
ObjectFlags = RF_Public ;
}
else if ( VarProperty . ArrayType = = EArrayType : : Static )
{
ArrayDim = 2 ;
}
2015-04-21 10:25:59 -04:00
else if ( VarProperty . MapKeyProp . IsValid ( ) )
{
Map = new ( EC_InternalUseOnlyConstructor , Scope , PropertyName , ObjectFlags ) UMapProperty ( FObjectInitializer ( ) ) ;
NewScope = Map ;
ObjectFlags = RF_Public ;
NewMapKeyProperty = CreateVariableProperty ( * VarProperty . MapKeyProp , NewScope , * ( PropertyName . ToString ( ) + TEXT ( " _Key " ) ) , ObjectFlags , VariableCategory ) ;
}
2014-03-14 14:13:41 -04:00
2015-03-31 07:03:34 -04:00
NewProperty = CreateVariableProperty ( VarProperty , NewScope , PropertyName , ObjectFlags , VariableCategory ) ;
2014-03-14 14:13:41 -04:00
2015-04-21 10:25:59 -04:00
auto PropagateFlags = [ ] ( uint64 FlagsToPropagate , FPropertyBase & From , UProperty * To ) {
2014-03-14 14:13:41 -04:00
// Copy some of the property flags to the inner property.
2015-04-21 10:25:59 -04:00
To - > PropertyFlags | = ( From . PropertyFlags & FlagsToPropagate ) ;
2014-09-24 09:09:32 -04:00
2015-04-21 10:25:59 -04:00
// Copy some of the property flags to the array property.
if ( To - > PropertyFlags & ( CPF_ContainsInstancedReference | CPF_InstancedReference ) )
{
From . PropertyFlags | = CPF_ContainsInstancedReference ;
From . PropertyFlags & = ~ ( CPF_InstancedReference | CPF_PersistentInstance ) ; //this was propagated to the inner
if ( To - > PropertyFlags & CPF_PersistentInstance )
2014-09-24 09:09:32 -04:00
{
TMap < FName , FString > MetaData ;
AddEditInlineMetaData ( MetaData ) ;
2015-04-21 10:25:59 -04:00
AddMetaDataToClassData ( To , From . MetaData ) ;
2014-09-24 09:09:32 -04:00
}
2014-03-14 14:13:41 -04:00
}
2015-04-21 10:25:59 -04:00
} ;
if ( Array )
{
Array - > Inner = NewProperty ;
PropagateFlags ( CPF_PropagateToArrayInner , VarProperty , NewProperty ) ;
2014-03-14 14:13:41 -04:00
NewProperty = Array ;
}
2015-04-21 10:25:59 -04:00
if ( Map )
{
Map - > KeyProp = NewMapKeyProperty ;
Map - > ValueProp = NewProperty ;
PropagateFlags ( CPF_PropagateToMapKey , * VarProperty . MapKeyProp , NewMapKeyProperty ) ;
PropagateFlags ( CPF_PropagateToMapValue , VarProperty , NewProperty ) ;
NewProperty = Map ;
}
2014-03-14 14:13:41 -04:00
NewProperty - > ArrayDim = ArrayDim ;
if ( ArrayDim = = 2 )
{
GArrayDimensions . Add ( NewProperty , Dimensions . String ) ;
}
NewProperty - > PropertyFlags = VarProperty . PropertyFlags ;
if ( Prev ! = NULL )
{
NewProperty - > Next = Prev - > Next ;
Prev - > Next = NewProperty ;
}
else
{
NewProperty - > Next = Scope - > Children ;
Scope - > Children = NewProperty ;
}
}
VarProperty . TokenProperty = NewProperty ;
2015-01-20 09:33:54 -05:00
GScriptHelper . FindClassData ( Scope ) - > AddProperty ( VarProperty ) ;
2014-03-14 14:13:41 -04:00
// if we had any metadata, add it to the class
AddMetaDataToClassData ( VarProperty . TokenProperty , VarProperty . MetaData ) ;
return NewProperty ;
}
/*-----------------------------------------------------------------------------
Statement compiler .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// Compile a declaration in Token. Returns 1 if compiled, 0 if not.
//
2015-01-20 09:33:54 -05:00
bool FHeaderParser : : CompileDeclaration ( FClasses & AllClasses , FUnrealSourceFile & SourceFile , FToken & Token )
2014-03-14 14:13:41 -04:00
{
EAccessSpecifier AccessSpecifier = ParseAccessProtectionSpecifier ( Token ) ;
if ( AccessSpecifier )
{
if ( ! IsAllowedInThisNesting ( ALLOW_VarDecl ) & & ! IsAllowedInThisNesting ( ALLOW_Function ) )
{
FError : : Throwf ( TEXT ( " Access specifier %s not allowed here. " ) , Token . Identifier ) ;
}
2015-03-17 06:02:26 -04:00
check ( TopNest - > NestType = = NEST_Class | | TopNest - > NestType = = NEST_Interface | | TopNest - > NestType = = NEST_NativeInterface ) ;
2014-03-14 14:13:41 -04:00
CurrentAccessSpecifier = AccessSpecifier ;
}
2014-06-26 01:14:00 -04:00
else if ( Token . Matches ( TEXT ( " class " ) ) & & ( TopNest - > NestType = = NEST_GlobalScope ) )
2014-03-14 14:13:41 -04:00
{
// Make sure the previous class ended with valid nesting.
if ( bEncounteredNewStyleClass_UnmatchedBrackets )
2014-06-26 01:14:00 -04:00
{
FError : : Throwf ( TEXT ( " Missing } at end of class " ) ) ;
}
2014-03-14 14:13:41 -04:00
// Start parsing the second class
bEncounteredNewStyleClass_UnmatchedBrackets = true ;
2015-03-17 06:02:26 -04:00
CurrentAccessSpecifier = ACCESS_Private ;
2015-01-20 09:33:54 -05:00
if ( ! TryParseIInterfaceClass ( AllClasses ) )
{
bEncounteredNewStyleClass_UnmatchedBrackets = false ;
UngetToken ( Token ) ;
return SkipDeclaration ( Token ) ;
}
2014-03-14 14:13:41 -04:00
}
2015-03-17 06:02:26 -04:00
else if ( Token . Matches ( TEXT ( " GENERATED_IINTERFACE_BODY " ) ) | | ( Token . Matches ( TEXT ( " GENERATED_BODY " ) ) & & TopNest - > NestType = = NEST_NativeInterface ) )
2014-10-10 04:34:56 -04:00
{
2015-03-17 06:02:26 -04:00
if ( TopNest - > NestType ! = NEST_NativeInterface )
2014-10-10 04:34:56 -04:00
{
2015-03-17 06:02:26 -04:00
FError : : Throwf ( TEXT ( " %s must occur inside the native interface definition " ) , Token . Identifier ) ;
2014-10-10 04:34:56 -04:00
}
RequireSymbol ( TEXT ( " ( " ) , Token . Identifier ) ;
2015-04-10 06:02:22 -04:00
CompileVersionDeclaration ( SourceFile , GetCurrentClass ( ) ) ;
2014-10-10 04:34:56 -04:00
RequireSymbol ( TEXT ( " ) " ) , Token . Identifier ) ;
2015-01-20 09:33:54 -05:00
auto * ClassData = GetCurrentClassData ( ) ;
ClassData - > GeneratedBodyMacroAccessSpecifier = CurrentAccessSpecifier ;
2015-03-17 06:02:26 -04:00
ClassData - > SetInterfaceGeneratedBodyLine ( InputLine ) ;
2015-01-20 09:33:54 -05:00
2015-03-13 08:17:47 -04:00
bClassHasGeneratedIInterfaceBody = true ;
2015-03-17 06:02:26 -04:00
if ( Token . Matches ( TEXT ( " GENERATED_IINTERFACE_BODY " ) ) )
{
CurrentAccessSpecifier = ACCESS_Public ;
}
2015-04-10 06:02:22 -04:00
if ( Token . Matches ( TEXT ( " GENERATED_BODY " ) ) )
{
ClassDefinitionRanges [ GetCurrentClass ( ) ] . bHasGeneratedBody = true ;
}
2014-03-14 14:13:41 -04:00
}
2015-03-17 06:02:26 -04:00
else if ( Token . Matches ( TEXT ( " GENERATED_UINTERFACE_BODY " ) ) | | ( Token . Matches ( TEXT ( " GENERATED_BODY " ) ) & & TopNest - > NestType = = NEST_Interface ) )
2014-03-14 14:13:41 -04:00
{
if ( TopNest - > NestType ! = NEST_Interface )
{
FError : : Throwf ( TEXT ( " %s must occur inside the interface definition " ) , Token . Identifier ) ;
}
RequireSymbol ( TEXT ( " ( " ) , Token . Identifier ) ;
2015-04-10 06:02:22 -04:00
CompileVersionDeclaration ( SourceFile , GetCurrentClass ( ) ) ;
2014-03-14 14:13:41 -04:00
RequireSymbol ( TEXT ( " ) " ) , Token . Identifier ) ;
2015-03-17 06:02:26 -04:00
auto * ClassData = GetCurrentClassData ( ) ;
ClassData - > GeneratedBodyMacroAccessSpecifier = CurrentAccessSpecifier ;
ClassData - > SetGeneratedBodyLine ( InputLine ) ;
2015-01-20 09:33:54 -05:00
2015-03-13 08:17:47 -04:00
bClassHasGeneratedUInterfaceBody = true ;
2015-03-17 06:02:26 -04:00
if ( Token . Matches ( TEXT ( " GENERATED_UINTERFACE_BODY " ) ) )
{
CurrentAccessSpecifier = ACCESS_Public ;
}
}
else if ( Token . Matches ( TEXT ( " GENERATED_UCLASS_BODY " ) ) | | ( Token . Matches ( TEXT ( " GENERATED_BODY " ) ) & & TopNest - > NestType = = NEST_Class ) )
{
if ( TopNest - > NestType ! = NEST_Class )
{
FError : : Throwf ( TEXT ( " %s must occur inside the class definition " ) , Token . Identifier ) ;
}
auto * ClassData = GetCurrentClassData ( ) ;
if ( Token . Matches ( TEXT ( " GENERATED_BODY " ) ) )
{
if ( ! ClassDefinitionRanges . Contains ( GetCurrentClass ( ) ) )
{
ClassDefinitionRanges . Add ( GetCurrentClass ( ) , ClassDefinitionRange ( ) ) ;
}
ClassDefinitionRanges [ GetCurrentClass ( ) ] . bHasGeneratedBody = true ;
ClassData - > GeneratedBodyMacroAccessSpecifier = CurrentAccessSpecifier ;
}
else
{
CurrentAccessSpecifier = ACCESS_Public ;
}
RequireSymbol ( TEXT ( " ( " ) , Token . Identifier ) ;
2015-04-10 06:02:22 -04:00
CompileVersionDeclaration ( SourceFile , GetCurrentClass ( ) ) ;
2015-03-17 06:02:26 -04:00
RequireSymbol ( TEXT ( " ) " ) , Token . Identifier ) ;
ClassData - > SetGeneratedBodyLine ( InputLine ) ;
bClassHasGeneratedBody = true ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " UCLASS " ) , ESearchCase : : CaseSensitive ) & & ( TopNest - > Allow & ALLOW_Class ) )
{
bHaveSeenUClass = true ;
2015-01-20 09:33:54 -05:00
bEncounteredNewStyleClass_UnmatchedBrackets = true ;
2014-04-23 18:42:01 -04:00
CompileClassDeclaration ( AllClasses ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " UINTERFACE " ) ) & & ( TopNest - > Allow & ALLOW_Class ) )
{
bHaveSeenUClass = true ;
bEncounteredNewStyleClass_UnmatchedBrackets = true ;
2014-04-23 18:42:01 -04:00
CompileInterfaceDeclaration ( AllClasses ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " UFUNCTION " ) , ESearchCase : : CaseSensitive ) )
{
2015-01-20 09:33:54 -05:00
CompileFunctionDeclaration ( SourceFile , AllClasses ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " UDELEGATE " ) ) )
{
2015-01-20 09:33:54 -05:00
CompileDelegateDeclaration ( SourceFile , AllClasses , Token . Identifier , EDelegateSpecifierAction : : Parse ) ;
2014-03-14 14:13:41 -04:00
}
else if ( IsValidDelegateDeclaration ( Token ) ) // Legacy delegate parsing - it didn't need a UDELEGATE
{
2015-01-20 09:33:54 -05:00
CompileDelegateDeclaration ( SourceFile , AllClasses , Token . Identifier ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " UPROPERTY " ) , ESearchCase : : CaseSensitive ) )
{
2015-01-20 09:33:54 -05:00
CheckAllow ( TEXT ( " 'Member variable declaration' " ) , ALLOW_VarDecl ) ;
check ( TopNest - > NestType = = NEST_Class ) ;
2014-03-14 14:13:41 -04:00
2015-01-28 09:41:47 -05:00
CompileVariableDeclaration ( AllClasses , GetCurrentClass ( ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " UENUM " ) ) )
{
// Enumeration definition.
2015-01-20 09:33:54 -05:00
CompileEnum ( SourceFile ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " USTRUCT " ) ) )
{
// Struct definition.
2015-01-20 09:33:54 -05:00
CompileStructDeclaration ( AllClasses , SourceFile ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " # " ) ) )
{
// Compiler directive.
2015-01-20 09:33:54 -05:00
CompileDirective ( AllClasses , SourceFile ) ;
2014-03-14 14:13:41 -04:00
}
else if ( bEncounteredNewStyleClass_UnmatchedBrackets & & Token . Matches ( TEXT ( " } " ) ) )
{
2015-01-20 09:33:54 -05:00
if ( ClassDefinitionRanges . Contains ( GetCurrentClass ( ) ) )
2014-12-11 11:49:41 -05:00
{
2015-01-20 09:33:54 -05:00
ClassDefinitionRanges [ GetCurrentClass ( ) ] . End = & Input [ InputPos ] ;
2014-12-11 11:49:41 -05:00
}
2014-03-14 14:13:41 -04:00
MatchSemi ( ) ;
// Closing brace for class declaration
//@TODO: This is a very loose approximation of what we really need to do
// Instead, the whole statement-consumer loop should be in a nest
bEncounteredNewStyleClass_UnmatchedBrackets = false ;
2015-01-20 09:33:54 -05:00
UClass * CurrentClass = GetCurrentClass ( ) ;
2014-03-14 14:13:41 -04:00
// Pop nesting here to allow other non UClass declarations in the header file.
2015-01-20 09:33:54 -05:00
if ( CurrentClass - > ClassFlags & CLASS_Interface )
2014-03-14 14:13:41 -04:00
{
2015-03-17 06:02:26 -04:00
checkf ( TopNest - > NestType = = NEST_Interface | | TopNest - > NestType = = NEST_NativeInterface , TEXT ( " Unexpected end of interface block. " ) ) ;
PopNest ( TopNest - > NestType , TEXT ( " 'Interface' " ) ) ;
2015-01-20 09:33:54 -05:00
PostPopNestInterface ( AllClasses , CurrentClass ) ;
2015-03-13 08:17:47 -04:00
// Ensure the UINTERFACE classes have a GENERATED_UINTERFACE_BODY declaration
if ( bHaveSeenUClass & & ! bClassHasGeneratedUInterfaceBody )
{
FError : : Throwf ( TEXT ( " Expected a GENERATED_UINTERFACE_BODY() at the start of class " ) ) ;
}
// Ensure the non-UINTERFACE interface classes have a GENERATED_IINTERFACE_BODY declaration
if ( ! bHaveSeenUClass & & ! bClassHasGeneratedIInterfaceBody )
{
FError : : Throwf ( TEXT ( " Expected a GENERATED_IINTERFACE_BODY() at the start of class " ) ) ;
}
2014-03-14 14:13:41 -04:00
}
else
{
2015-01-20 09:33:54 -05:00
PopNest ( NEST_Class , TEXT ( " 'Class' " ) ) ;
PostPopNestClass ( CurrentClass ) ;
2014-03-14 14:13:41 -04:00
// Ensure classes have a GENERATED_UCLASS_BODY declaration
if ( bHaveSeenUClass & & ! bClassHasGeneratedBody )
{
FError : : Throwf ( TEXT ( " Expected a GENERATED_UCLASS_BODY() at the start of class " ) ) ;
}
}
2015-01-20 09:33:54 -05:00
2015-03-13 08:17:47 -04:00
bHaveSeenUClass = false ;
bClassHasGeneratedBody = false ;
bClassHasGeneratedUInterfaceBody = false ;
bClassHasGeneratedIInterfaceBody = false ;
2015-01-20 09:33:54 -05:00
GetCurrentScope ( ) - > AddType ( CurrentClass ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( TEXT ( " ; " ) ) )
{
2015-01-20 09:33:54 -05:00
if ( GetToken ( Token ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Extra ';' before '%s' " ) , Token . Identifier ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Extra ';' before end of file " ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2015-03-17 06:02:26 -04:00
else if ( bEncounteredNewStyleClass_UnmatchedBrackets & & IsInAClass ( ) & & GetCurrentClass ( ) & &
(
Token . Matches ( NameLookupCPP . GetNameCPP ( GetCurrentClass ( ) ) ) | |
2015-03-17 14:48:41 -04:00
( FString ( Token . Identifier ) . EndsWith ( " _API " ) & & GetToken ( Token ) & & Token . Matches ( NameLookupCPP . GetNameCPP ( GetCurrentClass ( ) ) ) )
2015-03-17 06:02:26 -04:00
) )
2014-10-10 04:34:56 -04:00
{
2015-01-20 09:33:54 -05:00
if ( ! TryToMatchConstructorParameterList ( Token ) )
2014-10-10 04:34:56 -04:00
{
return SkipDeclaration ( Token ) ;
}
}
2014-03-14 14:13:41 -04:00
else
{
// Ignore C++ declaration / function definition.
return SkipDeclaration ( Token ) ;
}
return true ;
}
bool FHeaderParser : : SkipDeclaration ( FToken & Token )
{
// Store the current value of PrevComment so it can be restored after we parsed everything.
FString OldPrevComment ( PrevComment ) ;
// Consume all tokens until the end of declaration/definition has been found.
2015-04-22 14:47:12 -04:00
int32 NestedScopes = 0 ;
2014-03-14 14:13:41 -04:00
// Check if this is a class/struct declaration in which case it can be followed by member variable declaration.
bool bPossiblyClassDeclaration = Token . Matches ( TEXT ( " class " ) ) | | Token . Matches ( TEXT ( " struct " ) ) ;
// (known) macros can end without ; or } so use () to find the end of the declaration.
// However, we don't want to use it with DECLARE_FUNCTION, because we need it to be treated like a function.
bool bMacroDeclaration = ProbablyAMacro ( Token . Identifier ) & & ! Token . Matches ( " DECLARE_FUNCTION " ) ;
bool bEndOfDeclarationFound = false ;
bool bDefinitionFound = false ;
const TCHAR * OpeningBracket = bMacroDeclaration ? TEXT ( " ( " ) : TEXT ( " { " ) ;
const TCHAR * ClosingBracket = bMacroDeclaration ? TEXT ( " ) " ) : TEXT ( " } " ) ;
bool bRetestCurrentToken = false ;
while ( bRetestCurrentToken | | GetToken ( Token ) )
{
// If we find parentheses at top-level and we think it's a class declaration then it's more likely
// to be something like: class UThing* GetThing();
2015-04-22 14:47:12 -04:00
if ( bPossiblyClassDeclaration & & NestedScopes = = 0 & & Token . Matches ( TEXT ( " ( " ) ) )
2014-03-14 14:13:41 -04:00
{
bPossiblyClassDeclaration = false ;
}
bRetestCurrentToken = false ;
2015-04-22 14:47:12 -04:00
if ( Token . Matches ( TEXT ( " ; " ) ) & & NestedScopes = = 0 )
2014-03-14 14:13:41 -04:00
{
bEndOfDeclarationFound = true ;
break ;
}
2014-05-29 17:20:50 -04:00
if ( Token . Matches ( OpeningBracket ) )
2014-03-14 14:13:41 -04:00
{
// This is a function definition or class declaration.
bDefinitionFound = true ;
2015-04-22 14:47:12 -04:00
NestedScopes + + ;
2014-03-14 14:13:41 -04:00
}
else if ( Token . Matches ( ClosingBracket ) )
{
2015-04-22 14:47:12 -04:00
NestedScopes - - ;
if ( NestedScopes = = 0 )
2014-03-14 14:13:41 -04:00
{
bEndOfDeclarationFound = true ;
break ;
}
2014-05-29 17:20:50 -04:00
2015-04-22 14:47:12 -04:00
if ( NestedScopes < 0 )
2014-05-29 17:20:50 -04:00
{
2014-08-18 04:57:34 -04:00
FError : : Throwf ( TEXT ( " Unexpected '}'. Did you miss a semi-colon? " ) ) ;
2014-05-29 17:20:50 -04:00
}
2014-03-14 14:13:41 -04:00
}
2015-04-22 14:47:12 -04:00
else if ( bMacroDeclaration & & NestedScopes = = 0 )
2014-03-14 14:13:41 -04:00
{
bMacroDeclaration = false ;
OpeningBracket = TEXT ( " { " ) ;
ClosingBracket = TEXT ( " } " ) ;
bRetestCurrentToken = true ;
}
}
if ( bEndOfDeclarationFound )
{
// Member variable declaration after class declaration (see bPossiblyClassDeclaration).
if ( bPossiblyClassDeclaration & & bDefinitionFound )
{
// Should syntax errors be also handled when someone declares a variable after function definition?
// Consume the variable name.
FToken VariableName ;
if ( ! GetToken ( VariableName , true ) )
{
return false ;
}
if ( VariableName . TokenType ! = TOKEN_Identifier )
{
// Not a variable name.
UngetToken ( VariableName ) ;
}
2014-08-18 04:57:34 -04:00
else if ( ! SafeMatchSymbol ( TEXT ( " ; " ) ) )
{
FError : : Throwf ( * FString : : Printf ( TEXT ( " Unexpected '%s'. Did you miss a semi-colon? " ) , VariableName . Identifier ) ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-08-18 04:57:34 -04:00
2014-03-14 14:13:41 -04:00
// C++ allows any number of ';' after member declaration/definition.
while ( SafeMatchSymbol ( TEXT ( " ; " ) ) ) ;
}
PrevComment = OldPrevComment ;
// clear the current value for comment
//ClearComment();
// Successfully consumed C++ declaration unless mismatched pair of brackets has been found.
2015-04-22 14:47:12 -04:00
return NestedScopes = = 0 & & bEndOfDeclarationFound ;
2014-03-14 14:13:41 -04:00
}
bool FHeaderParser : : SafeMatchSymbol ( const TCHAR * Match )
{
FToken Token ;
// Remember the position before the next token (this can include comments before the next symbol).
FScriptLocation LocationBeforeNextSymbol ;
InitScriptLocation ( LocationBeforeNextSymbol ) ;
if ( GetToken ( Token , /*bNoConsts=*/ true ) )
{
if ( Token . TokenType = = TOKEN_Symbol & & ! FCString : : Stricmp ( Token . Identifier , Match ) )
{
return true ;
}
UngetToken ( Token ) ;
}
// Return to the stored position.
ReturnToLocation ( LocationBeforeNextSymbol ) ;
return false ;
}
2015-01-20 09:33:54 -05:00
FClass * FHeaderParser : : ParseClassNameDeclaration ( FClasses & AllClasses , FString & DeclaredClassName , FString & RequiredAPIMacroIfPresent )
2014-03-14 14:13:41 -04:00
{
ParseNameWithPotentialAPIMacroPrefix ( /*out*/ DeclaredClassName , /*out*/ RequiredAPIMacroIfPresent , TEXT ( " class " ) ) ;
2015-01-20 09:33:54 -05:00
FClass * FoundClass = AllClasses . FindClass ( * GetClassNameWithPrefixRemoved ( * DeclaredClassName ) ) ;
check ( FoundClass ) ;
GScriptHelper . AddClassData ( FoundClass ) ;
2014-03-14 14:13:41 -04:00
// Get parent class.
bool bSpecifiesParentClass = false ;
if ( MatchSymbol ( TEXT ( " : " ) ) )
{
RequireIdentifier ( TEXT ( " public " ) , TEXT ( " class inheritance " ) ) ;
bSpecifiesParentClass = true ;
}
2015-01-06 14:15:32 -05:00
// Add class cast flag
2015-01-20 09:33:54 -05:00
FoundClass - > ClassCastFlags | = ClassCastFlagMap : : Get ( ) . GetCastFlag ( DeclaredClassName ) ;
2015-01-06 14:15:32 -05:00
2014-03-14 14:13:41 -04:00
if ( bSpecifiesParentClass )
{
// Set the base class.
2014-04-23 18:42:01 -04:00
UClass * TempClass = GetQualifiedClass ( AllClasses , TEXT ( " 'extends' " ) ) ;
2014-03-14 14:13:41 -04:00
// a class cannot 'extends' an interface, use 'implements'
if ( TempClass - > ClassFlags & CLASS_Interface )
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Class '%s' cannot extend interface '%s', use 'implements' " ) , * FoundClass - > GetName ( ) , * TempClass - > GetName ( ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
UClass * SuperClass = FoundClass - > GetSuperClass ( ) ;
2014-03-14 14:13:41 -04:00
if ( SuperClass = = NULL )
{
2015-01-20 09:33:54 -05:00
FoundClass - > SetSuperStruct ( TempClass ) ;
2014-03-14 14:13:41 -04:00
}
else if ( SuperClass ! = TempClass )
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " %s's superclass must be %s, not %s " ) , * FoundClass - > GetPathName ( ) , * SuperClass - > GetPathName ( ) , * TempClass - > GetPathName ( ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
FoundClass - > ClassCastFlags | = FoundClass - > GetSuperClass ( ) - > ClassCastFlags ;
2014-03-14 14:13:41 -04:00
// Handle additional inherited interface classes
while ( MatchSymbol ( TEXT ( " , " ) ) )
{
RequireIdentifier ( TEXT ( " public " ) , TEXT ( " Interface inheritance must be public " ) ) ;
FToken Token ;
if ( ! GetIdentifier ( Token , true ) )
FError : : Throwf ( TEXT ( " Failed to get interface class identifier " ) ) ;
FString InterfaceName = Token . Identifier ;
// Handle templated native classes
if ( MatchSymbol ( TEXT ( " < " ) ) )
{
InterfaceName + = TEXT ( ' < ' ) ;
2015-04-22 14:47:12 -04:00
int32 NestedScopes = 1 ;
while ( NestedScopes )
2014-03-14 14:13:41 -04:00
{
if ( ! GetToken ( Token ) )
FError : : Throwf ( TEXT ( " Unexpected end of file " ) ) ;
if ( Token . TokenType = = TOKEN_Symbol )
{
if ( ! FCString : : Strcmp ( Token . Identifier , TEXT ( " < " ) ) )
{
2015-04-22 14:47:12 -04:00
+ + NestedScopes ;
2014-03-14 14:13:41 -04:00
}
else if ( ! FCString : : Strcmp ( Token . Identifier , TEXT ( " > " ) ) )
{
2015-04-22 14:47:12 -04:00
- - NestedScopes ;
2014-03-14 14:13:41 -04:00
}
}
InterfaceName + = Token . Identifier ;
}
}
2015-01-20 09:33:54 -05:00
HandleOneInheritedClass ( AllClasses , FoundClass , * InterfaceName ) ;
2014-03-14 14:13:41 -04:00
}
}
2015-01-20 09:33:54 -05:00
else if ( FoundClass - > GetSuperClass ( ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " class: missing 'Extends %s' " ) , * FoundClass - > GetSuperClass ( ) - > GetName ( ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
return FoundClass ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
void FHeaderParser : : HandleOneInheritedClass ( FClasses & AllClasses , UClass * Class , FString InterfaceName )
2014-03-14 14:13:41 -04:00
{
// Check for UInterface derived interface inheritance
2014-04-23 18:42:01 -04:00
if ( UClass * Interface = AllClasses . FindScriptClass ( InterfaceName ) )
2014-03-14 14:13:41 -04:00
{
// Try to find the interface
if ( ! Interface - > HasAnyClassFlags ( CLASS_Interface ) )
{
FError : : Throwf ( TEXT ( " Implements: Class %s is not an interface; Can only inherit from non-UObjects or UInterface derived interfaces " ) , * Interface - > GetName ( ) ) ;
}
// Propagate the inheritable ClassFlags
Class - > ClassFlags | = ( Interface - > ClassFlags ) & CLASS_ScriptInherit ;
new ( Class - > Interfaces ) FImplementedInterface ( Interface , 0 , false ) ;
if ( Interface - > HasAnyClassFlags ( CLASS_Native ) )
{
2015-01-20 09:33:54 -05:00
GScriptHelper . FindClassData ( Class ) - > AddInheritanceParent ( Interface ) ;
2014-03-14 14:13:41 -04:00
}
}
else
{
// Non-UObject inheritance
2015-01-20 09:33:54 -05:00
GScriptHelper . FindClassData ( Class ) - > AddInheritanceParent ( InterfaceName ) ;
}
}
/**
* Setups basic class settings after parsing .
*/
void PostParsingClassSetup ( UClass * Class )
{
// Cleanup after first pass.
FHeaderParser : : ComputeFunctionParametersSize ( Class ) ;
// Set all optimization ClassFlags based on property types
for ( TFieldIterator < UProperty > It ( Class , EFieldIteratorFlags : : ExcludeSuper ) ; It ; + + It )
{
if ( ( It - > PropertyFlags & CPF_Config ) ! = 0 )
{
Class - > ClassFlags | = CLASS_Config ;
}
if ( It - > ContainsInstancedObjectProperty ( ) )
{
Class - > ClassFlags | = CLASS_HasInstancedReference ;
}
}
// Class needs to specify which ini file is going to be used if it contains config variables.
if ( ( Class - > ClassFlags & CLASS_Config ) & & ( Class - > ClassConfigName = = NAME_None ) )
{
// Inherit config setting from base class.
Class - > ClassConfigName = Class - > GetSuperClass ( ) ? Class - > GetSuperClass ( ) - > ClassConfigName : NAME_None ;
if ( Class - > ClassConfigName = = NAME_None )
{
FError : : Throwf ( TEXT ( " Classes with config / globalconfig member variables need to specify config file. " ) ) ;
Class - > ClassConfigName = NAME_Engine ;
}
2014-03-14 14:13:41 -04:00
}
}
/**
* Compiles a class declaration .
*/
2014-04-23 18:42:01 -04:00
void FHeaderParser : : CompileClassDeclaration ( FClasses & AllClasses )
2014-03-14 14:13:41 -04:00
{
// Start of a class block.
2015-01-20 09:33:54 -05:00
CheckAllow ( TEXT ( " 'class' " ) , ALLOW_Class ) ;
// New-style UCLASS() syntax
TMap < FName , FString > MetaData ;
TArray < FPropertySpecifier > SpecifiersFound ;
ReadSpecifierSetInsideMacro ( SpecifiersFound , TEXT ( " Class " ) , MetaData ) ;
auto PrologFinishLine = InputLine ;
AddFormattedPrevCommentAsTooltipMetaData ( MetaData ) ;
// New style files have the class name / extends afterwards
RequireIdentifier ( TEXT ( " class " ) , TEXT ( " Class declaration " ) ) ;
SkipDeprecatedMacroIfNecessary ( ) ;
FString DeclaredClassName ;
FString RequiredAPIMacroIfPresent ;
FClass * Class = ParseClassNameDeclaration ( AllClasses , /*out*/ DeclaredClassName , /*out*/ RequiredAPIMacroIfPresent ) ;
2015-03-10 06:14:19 -04:00
check ( Class ) ;
2015-04-21 05:03:44 -04:00
TSharedRef < FClassDeclarationMetaData > ClassDeclarationData = GClassDeclarations . FindChecked ( Class - > GetFName ( ) ) ;
2015-03-10 06:14:19 -04:00
2015-01-20 09:33:54 -05:00
ClassDefinitionRanges . Add ( Class , ClassDefinitionRange ( & Input [ InputPos ] , nullptr ) ) ;
2015-03-10 06:14:19 -04:00
check ( Class - > ClassFlags = = 0 | | ( Class - > ClassFlags & ClassDeclarationData - > ClassFlags ) ! = 0 ) ;
2015-01-20 09:33:54 -05:00
Class - > ClassFlags | = CLASS_Parsed ;
PushNest ( ENestType : : NEST_Class , Class ) ;
2015-03-10 06:14:19 -04:00
2015-04-22 14:47:12 -04:00
const uint32 PrevClassFlags = Class - > ClassFlags ;
2015-01-20 09:33:54 -05:00
ResetClassData ( ) ;
// Verify class variables haven't been filled in
check ( Class - > Children = = NULL ) ;
check ( Class - > Next = = NULL ) ;
check ( Class - > NetFields . Num ( ) = = 0 ) ;
// Make sure our parent classes is parsed.
for ( UClass * Temp = Class - > GetSuperClass ( ) ; Temp ; Temp = Temp - > GetSuperClass ( ) )
{
if ( ! ( Temp - > ClassFlags & ( CLASS_Parsed | CLASS_Intrinsic ) ) )
{
FError : : Throwf ( TEXT ( " '%s' can't be compiled: Parent class '%s' has errors " ) , * Class - > GetName ( ) , * Temp - > GetName ( ) ) ;
}
}
2014-03-14 14:13:41 -04:00
2015-04-21 05:03:44 -04:00
// Merge with categories inherited from the parent.
ClassDeclarationData - > MergeClassCategories ( Class ) ;
2014-03-14 14:13:41 -04:00
// Class attributes.
2014-06-02 07:02:29 -04:00
FClassMetaData * ClassData = GScriptHelper . FindClassData ( Class ) ;
2014-03-14 14:13:41 -04:00
check ( ClassData ) ;
2015-01-20 09:33:54 -05:00
ClassData - > SetPrologLine ( PrologFinishLine ) ;
2014-03-14 14:13:41 -04:00
2015-04-22 14:47:12 -04:00
ClassDeclarationData - > MergeAndValidateClassFlags ( DeclaredClassName , PrevClassFlags , Class , AllClasses ) ;
2014-03-14 14:13:41 -04:00
Class - > SetFlags ( RF_Native ) ;
2015-04-21 05:03:44 -04:00
// Class metadata
2015-03-10 06:14:19 -04:00
if ( ClassDeclarationData - > ClassGroupNames . Num ( ) ) { MetaData . Add ( " ClassGroupNames " , FString : : Join ( ClassDeclarationData - > ClassGroupNames , TEXT ( " " ) ) ) ; }
if ( ClassDeclarationData - > AutoCollapseCategories . Num ( ) ) { MetaData . Add ( " AutoCollapseCategories " , FString : : Join ( ClassDeclarationData - > AutoCollapseCategories , TEXT ( " " ) ) ) ; }
if ( ClassDeclarationData - > HideCategories . Num ( ) ) { MetaData . Add ( " HideCategories " , FString : : Join ( ClassDeclarationData - > HideCategories , TEXT ( " " ) ) ) ; }
if ( ClassDeclarationData - > ShowSubCatgories . Num ( ) ) { MetaData . Add ( " ShowCategories " , FString : : Join ( ClassDeclarationData - > ShowSubCatgories , TEXT ( " " ) ) ) ; }
if ( ClassDeclarationData - > HideFunctions . Num ( ) ) { MetaData . Add ( " HideFunctions " , FString : : Join ( ClassDeclarationData - > HideFunctions , TEXT ( " " ) ) ) ; }
if ( ClassDeclarationData - > AutoExpandCategories . Num ( ) ) { MetaData . Add ( " AutoExpandCategories " , FString : : Join ( ClassDeclarationData - > AutoExpandCategories , TEXT ( " " ) ) ) ; }
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
AddIncludePathToMetadata ( Class , MetaData ) ;
AddModuleRelativePathToMetadata ( Class , MetaData ) ;
2014-03-14 14:13:41 -04:00
// Register the metadata
AddMetaDataToClassData ( Class , MetaData ) ;
// Handle the start of the rest of the class
RequireSymbol ( TEXT ( " { " ) , TEXT ( " 'Class' " ) ) ;
// Make visible outside the package.
2015-01-20 09:33:54 -05:00
Class - > ClearFlags ( RF_Transient ) ;
2014-03-14 14:13:41 -04:00
check ( Class - > HasAnyFlags ( RF_Public ) ) ;
check ( Class - > HasAnyFlags ( RF_Standalone ) ) ;
// Copy properties from parent class.
if ( Class - > GetSuperClass ( ) )
{
2015-01-20 09:33:54 -05:00
Class - > SetPropertiesSize ( Class - > GetSuperClass ( ) - > GetPropertiesSize ( ) ) ;
2014-03-14 14:13:41 -04:00
}
// auto-create properties for all of the VFTables needed for the multiple inheritances
// get the inheritance parents
auto & InheritanceParents = ClassData - > GetInheritanceParents ( ) ;
// for all base class types, make a VfTable property
for ( int32 ParentIndex = InheritanceParents . Num ( ) - 1 ; ParentIndex > = 0 ; ParentIndex - - )
{
// if this base class corresponds to an interface class, assign the vtable UProperty in the class's Interfaces map now...
if ( UClass * InheritedInterface = InheritanceParents [ ParentIndex ] . InterfaceClass )
{
if ( FImplementedInterface * Found = Class - > Interfaces . FindByPredicate ( [ = ] ( const FImplementedInterface & Impl ) { return Impl . Class = = InheritedInterface ; } ) )
{
Found - > PointerOffset = 1 ;
}
else
{
Class - > Interfaces . Add ( FImplementedInterface ( InheritedInterface , 1 , false ) ) ;
}
}
}
}
2015-01-20 09:33:54 -05:00
FClass * FHeaderParser : : ParseInterfaceNameDeclaration ( FClasses & AllClasses , FString & DeclaredInterfaceName , FString & RequiredAPIMacroIfPresent )
2014-03-14 14:13:41 -04:00
{
ParseNameWithPotentialAPIMacroPrefix ( /*out*/ DeclaredInterfaceName , /*out*/ RequiredAPIMacroIfPresent , TEXT ( " interface " ) ) ;
2015-01-20 09:33:54 -05:00
FClass * FoundClass = AllClasses . FindClass ( * GetClassNameWithPrefixRemoved ( * DeclaredInterfaceName ) ) ;
if ( FoundClass = = nullptr )
{
return nullptr ;
}
2014-03-14 14:13:41 -04:00
// Get super interface
bool bSpecifiesParentClass = MatchSymbol ( TEXT ( " : " ) ) ;
if ( ! bSpecifiesParentClass )
2015-01-20 09:33:54 -05:00
{
return FoundClass ;
}
2014-03-14 14:13:41 -04:00
RequireIdentifier ( TEXT ( " public " ) , TEXT ( " class inheritance " ) ) ;
// verify if our super class is an interface class
// the super class should have been marked as CLASS_Interface at the importing stage, if it were an interface
2014-04-23 18:42:01 -04:00
UClass * TempClass = GetQualifiedClass ( AllClasses , TEXT ( " 'extends' " ) ) ;
2014-03-14 14:13:41 -04:00
if ( ! ( TempClass - > ClassFlags & CLASS_Interface ) )
{
// UInterface is special and actually extends from UObject, which isn't an interface
if ( DeclaredInterfaceName ! = TEXT ( " UInterface " ) )
FError : : Throwf ( TEXT ( " Interface class '%s' cannot inherit from non-interface class '%s' " ) , * DeclaredInterfaceName , * TempClass - > GetName ( ) ) ;
}
2015-01-20 09:33:54 -05:00
UClass * SuperClass = FoundClass - > GetSuperClass ( ) ;
2014-03-14 14:13:41 -04:00
if ( SuperClass = = NULL )
{
2015-01-20 09:33:54 -05:00
FoundClass - > SetSuperStruct ( TempClass ) ;
2014-03-14 14:13:41 -04:00
}
else if ( SuperClass ! = TempClass )
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " %s's superclass must be %s, not %s " ) , * FoundClass - > GetPathName ( ) , * SuperClass - > GetPathName ( ) , * TempClass - > GetPathName ( ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
return FoundClass ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
bool FHeaderParser : : TryParseIInterfaceClass ( FClasses & AllClasses )
2014-03-14 14:13:41 -04:00
{
FString ErrorMsg ( TEXT ( " C++ interface mix-in class declaration " ) ) ;
// 'class' was already matched by the caller
// Get a class name
FString DeclaredInterfaceName ;
FString RequiredAPIMacroIfPresent ;
2015-01-20 09:33:54 -05:00
if ( ParseInterfaceNameDeclaration ( AllClasses , /*out*/ DeclaredInterfaceName , /*out*/ RequiredAPIMacroIfPresent ) = = nullptr )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
return false ;
}
if ( MatchSymbol ( TEXT ( " ; " ) ) )
{
// Forward declaration.
return false ;
}
if ( DeclaredInterfaceName [ 0 ] ! = ' I ' )
{
return false ;
}
UClass * FoundClass = nullptr ;
if ( ( FoundClass = AllClasses . FindClass ( * DeclaredInterfaceName . Mid ( 1 ) ) ) = = nullptr )
{
return false ;
2014-03-14 14:13:41 -04:00
}
// Continue parsing the second class as if it were a part of the first (for reflection data purposes, it is)
RequireSymbol ( TEXT ( " { " ) , * ErrorMsg ) ;
// Push the interface class nesting again.
2015-03-17 06:02:26 -04:00
PushNest ( NEST_NativeInterface , FoundClass ) ;
2015-01-20 09:33:54 -05:00
return true ;
2014-03-14 14:13:41 -04:00
}
/**
* compiles Java or C # style interface declaration
*/
2014-04-23 18:42:01 -04:00
void FHeaderParser : : CompileInterfaceDeclaration ( FClasses & AllClasses )
2014-03-14 14:13:41 -04:00
{
// Start of an interface block. Since Interfaces and Classes are always at the same nesting level,
// whereever a class declaration is allowed, an interface declaration is also allowed.
CheckAllow ( TEXT ( " 'interface' " ) , ALLOW_Class ) ;
FString DeclaredInterfaceName ;
FString RequiredAPIMacroIfPresent ;
TMap < FName , FString > MetaData ;
// Build up a list of interface specifiers
TArray < FPropertySpecifier > SpecifiersFound ;
// New-style UINTERFACE() syntax
ReadSpecifierSetInsideMacro ( SpecifiersFound , TEXT ( " Interface " ) , MetaData ) ;
2015-01-20 09:33:54 -05:00
auto PrologFinishLine = InputLine ;
2014-03-14 14:13:41 -04:00
// New style files have the interface name / extends afterwards
RequireIdentifier ( TEXT ( " class " ) , TEXT ( " Interface declaration " ) ) ;
2015-01-20 09:33:54 -05:00
FClass * InterfaceClass = ParseInterfaceNameDeclaration ( AllClasses , /*out*/ DeclaredInterfaceName , /*out*/ RequiredAPIMacroIfPresent ) ;
2015-03-17 06:02:26 -04:00
ClassDefinitionRanges . Add ( InterfaceClass , ClassDefinitionRange ( & Input [ InputPos ] , nullptr ) ) ;
2014-03-14 14:13:41 -04:00
// Record that this interface is RequiredAPI if the CORE_API style macro was present
if ( ! RequiredAPIMacroIfPresent . IsEmpty ( ) )
{
2015-01-20 09:33:54 -05:00
InterfaceClass - > ClassFlags | = CLASS_RequiredAPI ;
2014-03-14 14:13:41 -04:00
}
// Set the appropriate interface class flags
2015-01-20 09:33:54 -05:00
InterfaceClass - > ClassFlags | = CLASS_Interface | CLASS_Abstract ;
2015-04-02 16:31:18 -04:00
if ( InterfaceClass - > GetSuperClass ( ) ! = NULL )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
InterfaceClass - > ClassCastFlags | = InterfaceClass - > GetSuperClass ( ) - > ClassCastFlags ;
2014-03-14 14:13:41 -04:00
}
// All classes that are parsed are expected to be native
2015-01-20 09:33:54 -05:00
if ( InterfaceClass - > GetSuperClass ( ) & & ! InterfaceClass - > GetSuperClass ( ) - > HasAnyClassFlags ( CLASS_Native ) )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Native classes cannot extend non-native classes " ) ) ;
}
2015-01-20 09:33:54 -05:00
InterfaceClass - > SetFlags ( RF_Native ) ;
InterfaceClass - > ClassFlags | = CLASS_Native ;
2014-03-14 14:13:41 -04:00
// Process all of the interface specifiers
for ( TArray < FPropertySpecifier > : : TIterator SpecifierIt ( SpecifiersFound ) ; SpecifierIt ; + + SpecifierIt )
{
const FString & Specifier = SpecifierIt - > Key ;
2014-04-28 05:03:10 -04:00
if ( Specifier = = TEXT ( " DependsOn " ) )
2014-03-14 14:13:41 -04:00
{
2015-03-10 06:24:24 -04:00
FError : : Throwf ( TEXT ( " The dependsOn specifier is deprecated. Please use #include \" ClassHeaderFilename.h \" instead. " ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " MinimalAPI " ) )
{
2015-01-20 09:33:54 -05:00
InterfaceClass - > ClassFlags | = CLASS_MinimalAPI ;
2014-03-14 14:13:41 -04:00
}
else if ( Specifier = = TEXT ( " ConversionRoot " ) )
{
MetaData . Add ( FName ( TEXT ( " IsConversionRoot " ) ) , " true " ) ;
}
else
{
FError : : Throwf ( TEXT ( " Unknown interface specifier '%s' " ) , * Specifier ) ;
}
}
// All classes must start with a valid Unreal prefix
2015-01-20 09:33:54 -05:00
const FString ExpectedInterfaceName = InterfaceClass - > GetNameWithPrefix ( EEnforceInterfacePrefix : : U ) ;
2014-03-14 14:13:41 -04:00
if ( DeclaredInterfaceName ! = ExpectedInterfaceName )
{
FError : : Throwf ( TEXT ( " Interface name '%s' is invalid, the first class should be identified as '%s' " ) , * DeclaredInterfaceName , * ExpectedInterfaceName ) ;
}
// Try parsing metadata for the interface
2015-01-20 09:33:54 -05:00
FClassMetaData * ClassData = GScriptHelper . AddClassData ( InterfaceClass ) ;
2014-03-14 14:13:41 -04:00
check ( ClassData ) ;
2015-01-20 09:33:54 -05:00
ClassData - > SetPrologLine ( PrologFinishLine ) ;
2014-03-14 14:13:41 -04:00
// Register the metadata
2015-01-20 09:33:54 -05:00
AddMetaDataToClassData ( InterfaceClass , MetaData ) ;
2014-03-14 14:13:41 -04:00
// Handle the start of the rest of the interface
RequireSymbol ( TEXT ( " { " ) , TEXT ( " 'Class' " ) ) ;
// Make visible outside the package.
2015-01-20 09:33:54 -05:00
InterfaceClass - > ClearFlags ( RF_Transient ) ;
check ( InterfaceClass - > HasAnyFlags ( RF_Public ) ) ;
check ( InterfaceClass - > HasAnyFlags ( RF_Standalone ) ) ;
2014-03-14 14:13:41 -04:00
// Push the interface class nesting.
// we need a more specific set of allow flags for NEST_Interface, only function declaration is allowed, no other stuff are allowed
2015-01-20 09:33:54 -05:00
PushNest ( NEST_Interface , InterfaceClass ) ;
2014-03-14 14:13:41 -04:00
}
// Returns true if the token is a dynamic delegate declaration
bool FHeaderParser : : IsValidDelegateDeclaration ( const FToken & Token ) const
{
FString TokenStr ( Token . Identifier ) ;
return ( Token . TokenType = = TOKEN_Identifier ) & & TokenStr . StartsWith ( TEXT ( " DECLARE_DYNAMIC_ " ) ) ;
}
// Parse the parameter list of a function or delegate declaration
2014-04-23 18:42:01 -04:00
void FHeaderParser : : ParseParameterList ( FClasses & AllClasses , UFunction * Function , bool bExpectCommaBeforeName , TMap < FName , FString > * MetaData )
2014-03-14 14:13:41 -04:00
{
// Get parameter list.
if ( MatchSymbol ( TEXT ( " ) " ) ) )
return ;
FAdvancedDisplayParameterHandler AdvancedDisplay ( MetaData ) ;
do
{
// Get parameter type.
FToken Property ( CPT_None ) ;
2015-03-19 11:33:15 -04:00
EVariableCategory : : Type VariableCategory = ( Function - > FunctionFlags & FUNC_Net ) ? EVariableCategory : : ReplicatedParameter : EVariableCategory : : RegularParameter ;
2015-04-10 14:19:15 -04:00
GetVarType ( AllClasses , GetCurrentScope ( ) , Property , ~ ( CPF_ParmFlags | CPF_AutoWeak | CPF_RepSkip | CPF_UObjectWrapper ) , NULL , EPropertyDeclarationStyle : : None , VariableCategory ) ;
2014-03-14 14:13:41 -04:00
Property . PropertyFlags | = CPF_Parm ;
if ( bExpectCommaBeforeName )
{
RequireSymbol ( TEXT ( " , " ) , TEXT ( " Delegate definitions require a , between the parameter type and parameter name " ) ) ;
}
2015-03-20 13:26:37 -04:00
UProperty * Prop = GetVarNameAndDim ( Function , Property , VariableCategory ) ;
2014-03-14 14:13:41 -04:00
Function - > NumParms + + ;
if ( AdvancedDisplay . CanMarkMore ( ) & & AdvancedDisplay . ShouldMarkParameter ( Prop - > GetName ( ) ) )
{
Prop - > PropertyFlags | = CPF_AdvancedDisplay ;
}
// Check parameters.
if ( ( Function - > FunctionFlags & FUNC_Net ) )
{
if ( ! ( Function - > FunctionFlags & FUNC_NetRequest ) )
{
if ( Property . PropertyFlags & CPF_OutParm )
FError : : Throwf ( TEXT ( " Replicated functions cannot contain out parameters " ) ) ;
if ( Property . PropertyFlags & CPF_RepSkip )
FError : : Throwf ( TEXT ( " Only service request functions cannot contain NoReplication parameters " ) ) ;
if ( ( Prop - > GetClass ( ) - > ClassCastFlags & CASTCLASS_UDelegateProperty ) ! = 0 )
FError : : Throwf ( TEXT ( " Replicated functions cannot contain delegate parameters (this would be insecure) " ) ) ;
if ( Property . Type = = CPT_String & & Property . RefQualifier ! = ERefQualifier : : ConstRef & & Prop - > ArrayDim = = 1 )
FError : : Throwf ( TEXT ( " Replicated FString parameters must be passed by const reference " ) ) ;
if ( Property . ArrayType = = EArrayType : : Dynamic & & Property . RefQualifier ! = ERefQualifier : : ConstRef & & Prop - > ArrayDim = = 1 )
FError : : Throwf ( TEXT ( " Replicated TArray parameters must be passed by const reference " ) ) ;
}
else
{
if ( ! ( Property . PropertyFlags & CPF_RepSkip ) & & ( Property . PropertyFlags & CPF_OutParm ) )
FError : : Throwf ( TEXT ( " Service request functions cannot contain out parameters, unless marked NotReplicated " ) ) ;
if ( ! ( Property . PropertyFlags & CPF_RepSkip ) & & ( Prop - > GetClass ( ) - > ClassCastFlags & CASTCLASS_UDelegateProperty ) ! = 0 )
FError : : Throwf ( TEXT ( " Service request functions cannot contain delegate parameters, unless marked NotReplicated " ) ) ;
}
}
// Default value.
if ( MatchSymbol ( TEXT ( " = " ) ) )
{
// Skip past the native specified default value; we make no attempt to parse it
FToken SkipToken ;
int32 ParenthesisNestCount = 0 ;
int32 StartPos = - 1 ;
int32 EndPos = - 1 ;
while ( GetToken ( SkipToken ) )
{
if ( StartPos = = - 1 )
{
StartPos = SkipToken . StartPos ;
}
if ( ParenthesisNestCount = = 0
& & ( SkipToken . Matches ( TEXT ( " ) " ) ) | | SkipToken . Matches ( TEXT ( " , " ) ) ) )
{
EndPos = SkipToken . StartPos ;
// went too far
UngetToken ( SkipToken ) ;
break ;
}
if ( SkipToken . Matches ( TEXT ( " ( " ) ) )
{
ParenthesisNestCount + + ;
}
else if ( SkipToken . Matches ( TEXT ( " ) " ) ) )
{
ParenthesisNestCount - - ;
}
}
// allow exec functions to be added to the metaData, this is so we can have default params for them.
const bool bStoreCppDefaultValueInMetaData = Function - > HasAnyFunctionFlags ( FUNC_BlueprintCallable | FUNC_Exec ) ;
if ( ( EndPos > - 1 ) & & bStoreCppDefaultValueInMetaData )
{
FString DefaultArgText ( EndPos - StartPos , Input + StartPos ) ;
FString Key ( TEXT ( " CPP_Default_ " ) ) ;
Key + = Prop - > GetName ( ) ;
FName KeyName = FName ( * Key ) ;
if ( ! MetaData - > Contains ( KeyName ) )
{
FString InnerDefaultValue ;
const bool bDefaultValueParsed = DefaultValueStringCppFormatToInnerFormat ( Prop , DefaultArgText , InnerDefaultValue ) ;
if ( ! bDefaultValueParsed )
FError : : Throwf ( TEXT ( " C++ Default parameter not parsed: %s \" %s \" " ) , * Prop - > GetName ( ) , * DefaultArgText ) ;
if ( InnerDefaultValue . IsEmpty ( ) )
{
static int32 SkippedCounter = 0 ;
UE_LOG ( LogCompile , Verbose , TEXT ( " C++ Default parameter skipped/empty [%i]: %s \" %s \" " ) , SkippedCounter , * Prop - > GetName ( ) , * DefaultArgText ) ;
+ + SkippedCounter ;
}
else
{
MetaData - > Add ( KeyName , InnerDefaultValue ) ;
UE_LOG ( LogCompile , Verbose , TEXT ( " C++ Default parameter parsed: %s \" %s \" -> \" %s \" " ) , * Prop - > GetName ( ) , * DefaultArgText , * InnerDefaultValue ) ;
}
}
}
}
} while ( MatchSymbol ( TEXT ( " , " ) ) ) ;
RequireSymbol ( TEXT ( " ) " ) , TEXT ( " parameter list " ) ) ;
}
2015-01-20 09:33:54 -05:00
void FHeaderParser : : CompileDelegateDeclaration ( FUnrealSourceFile & SourceFile , FClasses & AllClasses , const TCHAR * DelegateIdentifier , EDelegateSpecifierAction : : Type SpecifierAction )
2014-03-14 14:13:41 -04:00
{
TMap < FName , FString > MetaData ;
2015-01-20 09:33:54 -05:00
AddModuleRelativePathToMetadata ( SourceFile , MetaData ) ;
2014-03-14 14:13:41 -04:00
FFuncInfo FuncInfo ;
// If this is a UDELEGATE, parse the specifiers first
FString DelegateMacro ;
if ( SpecifierAction = = EDelegateSpecifierAction : : Parse )
{
TArray < FPropertySpecifier > SpecifiersFound ;
ReadSpecifierSetInsideMacro ( SpecifiersFound , TEXT ( " Delegate " ) , MetaData ) ;
ProcessFunctionSpecifiers ( FuncInfo , SpecifiersFound ) ;
// Get the next token and ensure it looks like a delegate
FToken Token ;
GetToken ( Token ) ;
if ( ! IsValidDelegateDeclaration ( Token ) )
FError : : Throwf ( TEXT ( " Unexpected token following UDELEGATE(): %s " ) , Token . Identifier ) ;
DelegateMacro = Token . Identifier ;
}
else
{
DelegateMacro = DelegateIdentifier ;
}
// Make sure we can have a delegate declaration here
const TCHAR * CurrentScopeName = TEXT ( " Delegate Declaration " ) ;
CheckAllow ( CurrentScopeName , ALLOW_TypeDecl ) ; //@TODO: UCREMOVAL: After the switch: Make this require global scope
// Break the delegate declaration macro down into parts
const bool bHasReturnValue = DelegateMacro . Contains ( TEXT ( " _RetVal " ) ) ;
const bool bDeclaredConst = DelegateMacro . Contains ( TEXT ( " _Const " ) ) ;
const bool bIsMulticast = DelegateMacro . Contains ( TEXT ( " _MULTICAST " ) ) ;
// Determine the parameter count
const FString * FoundParamCount = DelegateParameterCountStrings . FindByPredicate ( [ & ] ( const FString & Str ) { return DelegateMacro . Contains ( Str ) ; } ) ;
// Try reconstructing the string to make sure it matches our expectations
FString ExpectedOriginalString = FString : : Printf ( TEXT ( " DECLARE_DYNAMIC%s_DELEGATE%s%s%s " ) ,
bIsMulticast ? TEXT ( " _MULTICAST " ) : TEXT ( " " ) ,
bHasReturnValue ? TEXT ( " _RetVal " ) : TEXT ( " " ) ,
FoundParamCount ? * * FoundParamCount : TEXT ( " " ) ,
bDeclaredConst ? TEXT ( " _Const " ) : TEXT ( " " ) ) ;
if ( DelegateMacro ! = ExpectedOriginalString )
{
FError : : Throwf ( TEXT ( " Unable to parse delegate declaration; expected '%s' but found '%s'. " ) , * ExpectedOriginalString , * DelegateMacro ) ;
}
// Multi-cast delegate function signatures are not allowed to have a return value
if ( bHasReturnValue & & bIsMulticast )
{
FError : : Throwf ( TEXT ( " Multi-cast delegates function signatures must not return a value " ) ) ;
}
// Delegate signature
FuncInfo . FunctionFlags | = FUNC_Public | FUNC_Delegate ;
if ( bIsMulticast )
{
FuncInfo . FunctionFlags | = FUNC_MulticastDelegate ;
}
// Now parse the macro body
RequireSymbol ( TEXT ( " ( " ) , CurrentScopeName ) ;
// Parse the return value type
FToken ReturnType ( CPT_None ) ;
if ( bHasReturnValue )
{
2015-03-19 13:27:48 -04:00
GetVarType ( AllClasses , GetCurrentScope ( ) , ReturnType , 0 , NULL , EPropertyDeclarationStyle : : None , EVariableCategory : : Return ) ;
2014-03-14 14:13:41 -04:00
RequireSymbol ( TEXT ( " , " ) , CurrentScopeName ) ;
}
2015-04-20 06:19:21 -04:00
// Skip whitespaces to get InputPos exactly on beginning of function name.
while ( FChar : : IsWhitespace ( PeekChar ( ) ) ) { GetChar ( ) ; }
FuncInfo . InputPos = InputPos ;
2014-03-14 14:13:41 -04:00
// Get the delegate name
if ( ! GetIdentifier ( FuncInfo . Function ) )
{
FError : : Throwf ( TEXT ( " Missing name for %s " ) , CurrentScopeName ) ;
}
// If this is a delegate function then go ahead and mangle the name so we don't collide with
// actual functions or properties
{
//@TODO: UCREMOVAL: Eventually this mangling shouldn't occur
// Remove the leading F
FString Name ( FuncInfo . Function . Identifier ) ;
if ( ! Name . StartsWith ( TEXT ( " F " ) ) )
{
FError : : Throwf ( TEXT ( " Delegate type declarations must start with F " ) ) ;
}
Name = Name . Mid ( 1 ) ;
// Append the signature goo
Name + = HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ;
// Replace the name
FCString : : Strcpy ( FuncInfo . Function . Identifier , * Name ) ;
}
2015-03-17 06:02:26 -04:00
FuncInfo . MacroLine = InputLine ;
2015-01-20 09:33:54 -05:00
auto * DelegateSignatureFunction = CreateDelegateFunction ( FuncInfo ) ;
GScriptHelper . AddClassData ( DelegateSignatureFunction ) ;
2014-03-14 14:13:41 -04:00
DelegateSignatureFunction - > FunctionFlags | = FuncInfo . FunctionFlags ;
FuncInfo . FunctionReference = DelegateSignatureFunction ;
FuncInfo . SetFunctionNames ( ) ;
2015-01-20 09:33:54 -05:00
FFunctionData : : Add ( FuncInfo ) ;
if ( FuncInfo . FunctionReference - > HasAnyFunctionFlags ( FUNC_Delegate ) & & ! GetCurrentScope ( ) - > IsFileScope ( ) )
{
GetCurrentClassData ( ) - > MarkContainsDelegate ( ) ;
}
GetCurrentScope ( ) - > AddType ( DelegateSignatureFunction ) ;
2014-03-14 14:13:41 -04:00
// determine whether this function should be 'const'
if ( bDeclaredConst )
{
DelegateSignatureFunction - > FunctionFlags | = FUNC_Const ;
}
// Get parameter list.
if ( FoundParamCount )
{
RequireSymbol ( TEXT ( " , " ) , CurrentScopeName ) ;
2014-04-23 18:42:01 -04:00
ParseParameterList ( AllClasses , DelegateSignatureFunction , /*bExpectCommaBeforeName=*/ true ) ;
2014-03-14 14:13:41 -04:00
// Check the expected versus actual number of parameters
2014-09-29 04:23:44 -04:00
int32 ParamCount = FoundParamCount - DelegateParameterCountStrings . GetData ( ) + 1 ;
2014-03-14 14:13:41 -04:00
if ( DelegateSignatureFunction - > NumParms ! = ParamCount )
FError : : Throwf ( TEXT ( " Expected %d parameters but found %d parameters " ) , ParamCount , DelegateSignatureFunction - > NumParms ) ;
}
else
{
// Require the closing paren even with no parameter list
RequireSymbol ( TEXT ( " ) " ) , TEXT ( " Delegate Declaration " ) ) ;
}
// Create the return value property
if ( bHasReturnValue )
{
ReturnType . PropertyFlags | = CPF_Parm | CPF_OutParm | CPF_ReturnParm ;
2015-03-20 13:26:37 -04:00
UProperty * ReturnProp = GetVarNameAndDim ( DelegateSignatureFunction , ReturnType , EVariableCategory : : Return ) ;
2014-03-14 14:13:41 -04:00
DelegateSignatureFunction - > NumParms + + ;
}
// Try parsing metadata for the function
ParseFieldMetaData ( MetaData , * ( DelegateSignatureFunction - > GetName ( ) ) ) ;
AddFormattedPrevCommentAsTooltipMetaData ( MetaData ) ;
AddMetaDataToClassData ( DelegateSignatureFunction , MetaData ) ;
// Optionally consume a semicolon, it's not required for the delegate macro since it contains one internally
MatchSemi ( ) ;
// Bind the function.
DelegateSignatureFunction - > Bind ( ) ;
// End the nesting
2015-01-20 09:33:54 -05:00
PostPopFunctionDeclaration ( AllClasses , DelegateSignatureFunction ) ;
2014-03-14 14:13:41 -04:00
// Don't allow delegate signatures to be redefined.
2015-01-20 09:33:54 -05:00
auto FunctionIterator = GetCurrentScope ( ) - > GetTypeIterator < UFunction > ( ) ;
while ( FunctionIterator . MoveNext ( ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
UFunction * TestFunc = * FunctionIterator ;
2014-03-14 14:13:41 -04:00
if ( ( TestFunc - > GetFName ( ) = = DelegateSignatureFunction - > GetFName ( ) ) & & ( TestFunc ! = DelegateSignatureFunction ) )
{
FError : : Throwf ( TEXT ( " Can't override delegate signature function '%s' " ) , FuncInfo . Function . Identifier ) ;
}
}
}
/**
* Parses and compiles a function declaration
*/
2015-01-20 09:33:54 -05:00
void FHeaderParser : : CompileFunctionDeclaration ( FUnrealSourceFile & SourceFile , FClasses & AllClasses )
2014-03-14 14:13:41 -04:00
{
CheckAllow ( TEXT ( " 'Function' " ) , ALLOW_Function ) ;
TMap < FName , FString > MetaData ;
2015-01-20 09:33:54 -05:00
AddModuleRelativePathToMetadata ( SourceFile , MetaData ) ;
2014-03-14 14:13:41 -04:00
// New-style UFUNCTION() syntax
TArray < FPropertySpecifier > SpecifiersFound ;
ReadSpecifierSetInsideMacro ( SpecifiersFound , TEXT ( " Function " ) , MetaData ) ;
FScriptLocation FuncNameRetry ;
InitScriptLocation ( FuncNameRetry ) ;
2015-01-20 09:33:54 -05:00
if ( ! GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Native ) )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Should only be here for native classes! " ) ) ;
}
// Process all specifiers.
const TCHAR * TypeOfFunction = TEXT ( " function " ) ;
bool bAutomaticallyFinal = true ;
FFuncInfo FuncInfo ;
2015-03-17 06:02:26 -04:00
FuncInfo . MacroLine = InputLine ;
2014-03-14 14:13:41 -04:00
FuncInfo . FunctionFlags = FUNC_Native ;
2014-09-03 10:46:58 -04:00
// Infer the function's access level from the currently declared C++ access level
if ( CurrentAccessSpecifier = = ACCESS_Public )
{
FuncInfo . FunctionFlags | = FUNC_Public ;
}
else if ( CurrentAccessSpecifier = = ACCESS_Protected )
{
FuncInfo . FunctionFlags | = FUNC_Protected ;
}
else if ( CurrentAccessSpecifier = = ACCESS_Private )
{
FuncInfo . FunctionFlags | = FUNC_Private ;
FuncInfo . FunctionFlags | = FUNC_Final ;
// This is automatically final as well, but in a different way and for a different reason
bAutomaticallyFinal = false ;
}
else
{
FError : : Throwf ( TEXT ( " Unknown access level " ) ) ;
}
// non-static functions in a const class must be const themselves
2015-01-20 09:33:54 -05:00
if ( GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Const ) )
2014-09-03 10:46:58 -04:00
{
FuncInfo . FunctionFlags | = FUNC_Const ;
}
if ( MatchIdentifier ( TEXT ( " static " ) ) )
{
FuncInfo . FunctionFlags | = FUNC_Static ;
FuncInfo . FunctionExportFlags | = FUNCEXPORT_CppStatic ;
}
2014-03-14 14:13:41 -04:00
ProcessFunctionSpecifiers ( FuncInfo , SpecifiersFound ) ;
2015-01-20 09:33:54 -05:00
if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintPure ) & & GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Interface ) )
2014-03-14 14:13:41 -04:00
{
// Until pure interface casts are supported, we don't allow pures in interfaces
FError : : Throwf ( TEXT ( " BlueprintPure specifier is not allowed for interface functions " ) ) ;
}
if ( FuncInfo . FunctionFlags & FUNC_Net )
{
2014-06-13 08:09:04 -04:00
// Network replicated functions are always events, and are only final if sealed
2014-03-14 14:13:41 -04:00
TypeOfFunction = TEXT ( " event " ) ;
bAutomaticallyFinal = false ;
}
if ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent )
{
TypeOfFunction = ( FuncInfo . FunctionFlags & FUNC_Native ) ? TEXT ( " BlueprintNativeEvent " ) : TEXT ( " BlueprintImplementableEvent " ) ;
bAutomaticallyFinal = false ;
}
2014-04-02 18:09:23 -04:00
bool bSawVirtual = false ;
2014-03-14 14:13:41 -04:00
if ( MatchIdentifier ( TEXT ( " virtual " ) ) )
{
2014-04-02 18:09:23 -04:00
bSawVirtual = true ;
2014-03-14 14:13:41 -04:00
}
2014-10-07 15:19:27 -04:00
FString * InternalPtr = MetaData . Find ( " BlueprintInternalUseOnly " ) ; // FBlueprintMetadata::MD_BlueprintInternalUseOnly
const bool bDeprecated = MetaData . Contains ( " DeprecatedFunction " ) ; // FBlueprintMetadata::MD_DeprecatedFunction
const bool bHasMenuCategory = MetaData . Contains ( " Category " ) ; // FBlueprintMetadata::MD_FunctionCategory
const bool bInternalOnly = InternalPtr & & * InternalPtr = = TEXT ( " true " ) ;
2014-03-14 14:13:41 -04:00
// If this function is blueprint callable or blueprint pure, require a category
if ( ( FuncInfo . FunctionFlags & ( FUNC_BlueprintCallable | FUNC_BlueprintPure ) ) ! = 0 )
{
2014-05-14 15:10:36 -04:00
if ( ! bHasMenuCategory & & ! bInternalOnly & & ! bDeprecated )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Blueprint accessible functions must have a category specified " ) ) ;
}
}
2014-10-07 15:19:27 -04:00
// Verify interfaces with respect to their blueprint accessible functions
2015-01-20 09:33:54 -05:00
if ( GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Interface ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
const bool bCanImplementInBlueprints = ! GetCurrentClass ( ) - > HasMetaData ( TEXT ( " CannotImplementInterfaceInBlueprint " ) ) ; //FBlueprintMetadata::MD_CannotImplementInterfaceInBlueprint
2014-03-14 14:13:41 -04:00
if ( ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) ! = 0 )
{
2014-10-07 15:19:27 -04:00
// Ensure that blueprint events are only allowed in implementable interfaces. Internal only functions allowed
if ( ! bCanImplementInBlueprints & & ! bInternalOnly )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Interfaces that are not implementable in blueprints cannot have BlueprintImplementableEvent members. " ) ) ;
}
}
if ( ( ( FuncInfo . FunctionFlags & FUNC_BlueprintCallable ) ! = 0 ) & & ( ( ( ~ FuncInfo . FunctionFlags ) & FUNC_BlueprintEvent ) ! = 0 ) )
{
// Ensure that if this interface contains blueprint callable functions that are not blueprint defined, that it must be implemented natively
if ( bCanImplementInBlueprints )
{
FError : : Throwf ( TEXT ( " Blueprint implementable interfaces cannot contain BlueprintCallable functions that are not BlueprintImplementableEvents. Use CannotImplementInterfaceInBlueprint on the interface if you wish to keep this function. " ) ) ;
}
}
}
// Peek ahead to look for a CORE_API style DLL import/export token if present
{
FToken Token ;
if ( GetToken ( Token , true ) )
{
bool bThrowTokenBack = true ;
if ( Token . TokenType = = TOKEN_Identifier )
{
FString RequiredAPIMacroIfPresent ( Token . Identifier ) ;
if ( RequiredAPIMacroIfPresent . EndsWith ( TEXT ( " _API " ) ) )
{
//@TODO: Validate the module name for RequiredAPIMacroIfPresent
bThrowTokenBack = false ;
2015-01-20 09:33:54 -05:00
if ( GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_RequiredAPI ) )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " '%s' must not be used on methods of a class that is marked '%s' itself. " ) , * RequiredAPIMacroIfPresent , * RequiredAPIMacroIfPresent ) ;
}
2014-04-29 06:45:18 -04:00
FuncInfo . FunctionFlags | = FUNC_RequiredAPI ;
2014-03-14 14:13:41 -04:00
FuncInfo . FunctionExportFlags | = FUNCEXPORT_RequiredAPI ;
}
}
if ( bThrowTokenBack )
{
UngetToken ( Token ) ;
}
}
}
2014-04-02 18:09:23 -04:00
// Look for virtual again, in case there was an ENGINE_API token first
if ( MatchIdentifier ( TEXT ( " virtual " ) ) )
{
bSawVirtual = true ;
}
// Process the virtualness
if ( bSawVirtual )
{
2014-06-13 08:09:04 -04:00
// Remove the implicit final, the user can still specifying an explicit final at the end of the declaration
2014-04-02 18:09:23 -04:00
bAutomaticallyFinal = false ;
// if this is a BlueprintNativeEvent or BlueprintImplementableEvent in an interface, make sure it's not "virtual"
2015-04-21 12:46:26 -04:00
if ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent )
2014-04-02 18:09:23 -04:00
{
2015-04-21 12:46:26 -04:00
if ( GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Interface ) )
{
FError : : Throwf ( TEXT ( " BlueprintImplementableEvents in Interfaces must not be declared 'virtual' " ) ) ;
}
2014-04-02 18:09:23 -04:00
2015-04-21 12:46:26 -04:00
// if this is a BlueprintNativeEvent, make sure it's not "virtual"
else if ( FuncInfo . FunctionFlags & FUNC_Native )
{
FError : : Throwf ( TEXT ( " BlueprintNativeEvent functions must be non-virtual. " ) ) ;
}
2014-04-02 18:09:23 -04:00
2015-04-21 12:46:26 -04:00
else
{
2015-04-21 16:40:14 -04:00
//UE_LOG(LogCompile, Warning, TEXT("BlueprintImplementableEvents should not be virtual. Use BlueprintNativeEvent instead."));
2015-04-21 12:46:26 -04:00
}
}
2014-04-02 18:09:23 -04:00
}
else
{
// if this is a function in an Interface, it must be marked 'virtual' unless it's an event
2015-01-20 09:33:54 -05:00
if ( GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Interface ) & & ! ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent ) )
2014-04-02 18:09:23 -04:00
{
FError : : Throwf ( TEXT ( " Interface functions that are not BlueprintImplementableEvents must be declared 'virtual' " ) ) ;
}
}
// Handle the initial implicit/explicit final
2014-06-13 08:09:04 -04:00
// A user can still specify an explicit final after the parameter list as well.
2014-04-02 18:09:23 -04:00
if ( bAutomaticallyFinal | | FuncInfo . bSealedEvent )
{
FuncInfo . FunctionFlags | = FUNC_Final ;
FuncInfo . FunctionExportFlags | = FUNCEXPORT_Final ;
2015-01-20 09:33:54 -05:00
if ( GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Interface ) )
2014-04-02 18:09:23 -04:00
{
FError : : Throwf ( TEXT ( " Interface functions cannot be declared 'final' " ) ) ;
}
}
2014-03-14 14:13:41 -04:00
// Get return type.
FToken ReturnType ( CPT_None ) ;
// C++ style functions always have a return value type, even if it's void
2015-03-19 13:42:08 -04:00
bool bHasReturnValue = ! MatchIdentifier ( TEXT ( " void " ) ) ;
if ( bHasReturnValue )
2014-03-14 14:13:41 -04:00
{
2015-03-19 13:42:08 -04:00
GetVarType ( AllClasses , GetCurrentScope ( ) , ReturnType , 0 , NULL , EPropertyDeclarationStyle : : None , EVariableCategory : : Return ) ;
2014-03-14 14:13:41 -04:00
}
2015-04-20 06:19:21 -04:00
// Skip whitespaces to get InputPos exactly on beginning of function name.
while ( FChar : : IsWhitespace ( PeekChar ( ) ) ) { GetChar ( ) ; }
FuncInfo . InputPos = InputPos ;
2014-03-14 14:13:41 -04:00
// Get function or operator name.
if ( ! GetIdentifier ( FuncInfo . Function ) )
{
FError : : Throwf ( TEXT ( " Missing %s name " ) , TypeOfFunction ) ;
}
if ( ! MatchSymbol ( TEXT ( " ( " ) ) )
{
FError : : Throwf ( TEXT ( " Bad %s definition " ) , TypeOfFunction ) ;
}
if ( FuncInfo . FunctionFlags & FUNC_Net )
{
bool bIsNetService = ! ! ( FuncInfo . FunctionFlags & ( FUNC_NetRequest | FUNC_NetResponse ) ) ;
if ( bHasReturnValue & & ! bIsNetService )
FError : : Throwf ( TEXT ( " Replicated functions can't have return values " ) ) ;
if ( FuncInfo . RPCId > 0 )
{
if ( FString * ExistingFunc = UsedRPCIds . Find ( FuncInfo . RPCId ) )
FError : : Throwf ( TEXT ( " Function %s already uses identifier %d " ) , * * ExistingFunc , FuncInfo . RPCId ) ;
UsedRPCIds . Add ( FuncInfo . RPCId , FuncInfo . Function . Identifier ) ;
if ( FuncInfo . FunctionFlags & FUNC_NetResponse )
{
// Look for another function expecting this response
if ( FString * ExistingFunc = RPCsNeedingHookup . Find ( FuncInfo . RPCId ) )
{
// If this list isn't empty at end of class, throw error
RPCsNeedingHookup . Remove ( FuncInfo . RPCId ) ;
}
}
}
if ( FuncInfo . RPCResponseId > 0 )
{
// Look for an existing response function
FString * ExistingFunc = UsedRPCIds . Find ( FuncInfo . RPCResponseId ) ;
if ( ExistingFunc = = NULL )
{
// If this list isn't empty at end of class, throw error
RPCsNeedingHookup . Add ( FuncInfo . RPCResponseId , FuncInfo . Function . Identifier ) ;
}
}
}
2015-01-20 09:33:54 -05:00
auto * TopFunction = CreateFunction ( FuncInfo ) ;
GScriptHelper . AddClassData ( TopFunction ) ;
2014-03-14 14:13:41 -04:00
TopFunction - > FunctionFlags | = FuncInfo . FunctionFlags ;
FuncInfo . FunctionReference = TopFunction ;
FuncInfo . SetFunctionNames ( ) ;
2015-02-19 10:58:34 -05:00
GetCurrentScope ( ) - > AddType ( TopFunction ) ;
2015-01-20 09:33:54 -05:00
auto * StoredFuncData = FFunctionData : : Add ( FuncInfo ) ;
if ( FuncInfo . FunctionReference - > HasAnyFunctionFlags ( FUNC_Delegate ) )
{
GetCurrentClassData ( ) - > MarkContainsDelegate ( ) ;
}
2014-03-14 14:13:41 -04:00
// Get parameter list.
2014-04-23 18:42:01 -04:00
ParseParameterList ( AllClasses , TopFunction , false , & MetaData ) ;
2014-03-14 14:13:41 -04:00
// Get return type, if any.
if ( bHasReturnValue )
{
ReturnType . PropertyFlags | = CPF_Parm | CPF_OutParm | CPF_ReturnParm ;
2015-03-20 13:26:37 -04:00
UProperty * ReturnProp = GetVarNameAndDim ( TopFunction , ReturnType , EVariableCategory : : Return ) ;
2014-03-14 14:13:41 -04:00
TopFunction - > NumParms + + ;
}
// determine if there are any outputs for this function
bool bHasAnyOutputs = bHasReturnValue ;
if ( bHasAnyOutputs = = false )
{
for ( TFieldIterator < UProperty > It ( TopFunction ) ; It ; + + It )
{
UProperty const * const Param = * It ;
if ( ! ( Param - > PropertyFlags & CPF_ReturnParm ) & & ( Param - > PropertyFlags & CPF_OutParm ) )
{
bHasAnyOutputs = true ;
break ;
}
}
}
if ( ( bHasAnyOutputs = = false ) & & ( FuncInfo . FunctionFlags & ( FUNC_BlueprintPure ) ) )
{
FError : : Throwf ( TEXT ( " BlueprintPure specifier is not allowed for functions with no return value and no output parameters. " ) ) ;
}
// determine whether this function should be 'const'
if ( MatchIdentifier ( TEXT ( " const " ) ) )
{
if ( ( TopFunction - > FunctionFlags & ( FUNC_Native ) ) = = 0 )
{
// @TODO: UCREMOVAL Reconsider?
//FError::Throwf(TEXT("'const' may only be used for native functions"));
}
FuncInfo . FunctionFlags | = FUNC_Const ;
2014-09-02 16:02:17 -04:00
// @todo: the presence of const and one or more outputs does not guarantee that there are
// no side effects. On GCC and clang we could use __attribure__((pure)) or __attribute__((const))
// or we could just rely on the use marking things BlueprintPure. Either way, checking the C++
// const identifier to determine purity is not desirable. We should remove the following logic:
2014-03-14 14:13:41 -04:00
// If its a const BlueprintCallable function with some sort of output, mark it as BlueprintPure as well
if ( bHasAnyOutputs & & ( ( FuncInfo . FunctionFlags & FUNC_BlueprintCallable ) ! = 0 ) )
{
FuncInfo . FunctionFlags | = FUNC_BlueprintPure ;
}
}
// Try parsing metadata for the function
ParseFieldMetaData ( MetaData , * ( TopFunction - > GetName ( ) ) ) ;
AddFormattedPrevCommentAsTooltipMetaData ( MetaData ) ;
AddMetaDataToClassData ( TopFunction , MetaData ) ;
2015-03-30 14:13:41 -04:00
// 'final' and 'override' can appear in any order before an optional '= 0' pure virtual specifier
bool bFoundFinal = MatchIdentifier ( TEXT ( " final " ) ) ;
bool bFoundOverride = MatchIdentifier ( TEXT ( " override " ) ) ;
if ( ! bFoundFinal & & bFoundOverride )
{
bFoundFinal = MatchIdentifier ( TEXT ( " final " ) ) ;
}
2014-03-14 14:13:41 -04:00
// Handle C++ style functions being declared as abstract
if ( MatchSymbol ( TEXT ( " = " ) ) )
{
2015-01-20 09:33:54 -05:00
int32 ZeroValue = 1 ;
bool bGotZero = GetConstInt ( /*out*/ ZeroValue ) ;
bGotZero = bGotZero & & ( ZeroValue = = 0 ) ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
if ( ! bGotZero )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
FError : : Throwf ( TEXT ( " Expected 0 to indicate function is abstract " ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-06-13 08:09:04 -04:00
// Look for the final keyword to indicate this function is sealed
2015-03-30 14:13:41 -04:00
if ( bFoundFinal )
2014-03-14 14:13:41 -04:00
{
// This is a final (prebinding, non-overridable) function
FuncInfo . FunctionFlags | = FUNC_Final ;
FuncInfo . FunctionExportFlags | = FUNCEXPORT_Final ;
2015-01-20 09:33:54 -05:00
if ( GetCurrentClass ( ) - > HasAnyClassFlags ( CLASS_Interface ) )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Interface functions cannot be declared 'final' " ) ) ;
}
2014-09-03 10:46:58 -04:00
else if ( FuncInfo . FunctionFlags & FUNC_BlueprintEvent )
{
FError : : Throwf ( TEXT ( " Blueprint events cannot be declared 'final' " ) ) ;
}
2014-03-14 14:13:41 -04:00
}
// Make sure any new flags made it to the function
//@TODO: UCREMOVAL: Ideally the flags didn't get copied midway thru parsing the function declaration, and we could avoid this
TopFunction - > FunctionFlags | = FuncInfo . FunctionFlags ;
StoredFuncData - > UpdateFunctionData ( FuncInfo ) ;
// Verify parameter list and return type compatibility within the
// function, if any, that it overrides.
2015-01-20 09:33:54 -05:00
auto FunctionIterator = GetCurrentScope ( ) - > GetTypeIterator < UFunction > ( ) ;
while ( FunctionIterator . MoveNext ( ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
UFunction * Function = * FunctionIterator ;
if ( Function - > GetFName ( ) ! = TopFunction - > GetFName ( ) | | Function = = TopFunction )
continue ;
// Don't allow private functions to be redefined.
if ( Function - > FunctionFlags & FUNC_Private )
FError : : Throwf ( TEXT ( " Can't override private function '%s' " ) , FuncInfo . Function . Identifier ) ;
// see if they both either have a return value or don't
if ( ( TopFunction - > GetReturnProperty ( ) ! = NULL ) ! = ( Function - > GetReturnProperty ( ) ! = NULL ) )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " Redefinition of '%s %s' differs from original: return value mismatch " ) , TypeOfFunction , FuncInfo . Function . Identifier ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
// See if all parameters match.
if ( TopFunction - > NumParms ! = Function - > NumParms )
{
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " Redefinition of '%s %s' differs from original; different number of parameters " ) , TypeOfFunction , FuncInfo . Function . Identifier ) ;
}
// Check all individual parameters.
int32 Count = 0 ;
for ( TFieldIterator < UProperty > CurrentFuncParam ( TopFunction ) , SuperFuncParam ( Function ) ; Count < Function - > NumParms ; + + CurrentFuncParam , + + SuperFuncParam , + + Count )
{
if ( ! FPropertyBase ( * CurrentFuncParam ) . MatchesType ( FPropertyBase ( * SuperFuncParam ) , 1 ) )
{
if ( CurrentFuncParam - > PropertyFlags & CPF_ReturnParm )
{
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " Redefinition of %s %s differs only by return type " ) , TypeOfFunction , FuncInfo . Function . Identifier ) ;
}
else
{
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " Redefinition of '%s %s' differs from original " ) , TypeOfFunction , FuncInfo . Function . Identifier ) ;
}
break ;
}
else if ( CurrentFuncParam - > HasAnyPropertyFlags ( CPF_OutParm ) ! = SuperFuncParam - > HasAnyPropertyFlags ( CPF_OutParm ) )
{
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " Redefinition of '%s %s' differs from original - 'out' mismatch on parameter %i " ) , TypeOfFunction , FuncInfo . Function . Identifier , Count + 1 ) ;
}
else if ( CurrentFuncParam - > HasAnyPropertyFlags ( CPF_ReferenceParm ) ! = SuperFuncParam - > HasAnyPropertyFlags ( CPF_ReferenceParm ) )
{
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " Redefinition of '%s %s' differs from original - 'ref' mismatch on parameter %i " ) , TypeOfFunction , FuncInfo . Function . Identifier , Count + 1 ) ;
}
}
if ( Count < TopFunction - > NumParms )
{
continue ;
}
// if super version is event, overridden version must be defined as event (check before inheriting FUNC_Event)
if ( ( Function - > FunctionFlags & FUNC_Event ) & & ! ( FuncInfo . FunctionFlags & FUNC_Event ) )
{
FError : : Throwf ( TEXT ( " Superclass version is defined as an event so '%s' should be! " ) , FuncInfo . Function . Identifier ) ;
}
// Function flags to copy from parent.
FuncInfo . FunctionFlags | = ( Function - > FunctionFlags & FUNC_FuncInherit ) ;
// Make sure the replication conditions aren't being redefined
if ( ( FuncInfo . FunctionFlags & FUNC_NetFuncFlags ) ! = ( Function - > FunctionFlags & FUNC_NetFuncFlags ) )
{
FError : : Throwf ( TEXT ( " Redefinition of replication conditions for function '%s' " ) , FuncInfo . Function . Identifier ) ;
}
FuncInfo . FunctionFlags | = ( Function - > FunctionFlags & FUNC_NetFuncFlags ) ;
// Are we overriding a function?
if ( TopFunction = = Function - > GetOuter ( ) )
{
// Duplicate.
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " Duplicate function '%s' " ) , * Function - > GetName ( ) ) ;
}
// Overriding an existing function.
else if ( Function - > FunctionFlags & FUNC_Final )
{
ReturnToLocation ( FuncNameRetry ) ;
FError : : Throwf ( TEXT ( " %s: Can't override a 'final' function " ) , * Function - > GetName ( ) ) ;
}
// Native function overrides should be done in CPP text, not in a UFUNCTION() declaration (you can't change flags, and it'd otherwise be a burden to keep them identical)
else if ( Cast < UClass > ( TopFunction - > GetOuter ( ) ) ! = NULL )
{
//ReturnToLocation(FuncNameRetry);
FError : : Throwf ( TEXT ( " %s: An override of a function cannot have a UFUNCTION() declaration above it; it will use the same parameters as the original base declaration. " ) , * Function - > GetName ( ) ) ;
}
// Balk if required specifiers differ.
if ( ( Function - > FunctionFlags & FUNC_FuncOverrideMatch ) ! = ( FuncInfo . FunctionFlags & FUNC_FuncOverrideMatch ) )
{
FError : : Throwf ( TEXT ( " Function '%s' specifiers differ from original " ) , * Function - > GetName ( ) ) ;
}
// Here we have found the original.
TopFunction - > SetSuperStruct ( Function ) ;
break ;
2014-03-14 14:13:41 -04:00
}
// Bind the function.
TopFunction - > Bind ( ) ;
// Make sure that the replication flags set on an overridden function match the parent function
if ( UFunction * SuperFunc = TopFunction - > GetSuperFunction ( ) )
{
if ( ( TopFunction - > FunctionFlags & FUNC_NetFuncFlags ) ! = ( SuperFunc - > FunctionFlags & FUNC_NetFuncFlags ) )
{
FError : : Throwf ( TEXT ( " Overridden function '%s': Cannot specify different replication flags when overriding a function. " ) , * TopFunction - > GetName ( ) ) ;
}
}
// if this function is an RPC in state scope, verify that it is an override
// this is required because the networking code only checks the class for RPCs when initializing network data, not any states within it
if ( ( TopFunction - > FunctionFlags & FUNC_Net ) & & ( TopFunction - > GetSuperFunction ( ) = = NULL ) & & Cast < UClass > ( TopFunction - > GetOuter ( ) ) = = NULL )
{
FError : : Throwf ( TEXT ( " Function '%s': Base implementation of RPCs cannot be in a state. Add a stub outside state scope. " ) , * TopFunction - > GetName ( ) ) ;
}
2014-08-05 14:16:02 -04:00
if ( TopFunction - > FunctionFlags & ( FUNC_BlueprintCallable | FUNC_BlueprintEvent ) )
{
for ( TFieldIterator < UProperty > It ( TopFunction ) ; It ; + + It )
{
UProperty const * const Param = * It ;
if ( Param - > ArrayDim > 1 )
{
2014-08-07 12:57:45 -04:00
FError : : Throwf ( TEXT ( " Static array cannot be exposed to blueprint. Function: %s Parameter %s \n " ) , * TopFunction - > GetName ( ) , * Param - > GetName ( ) ) ;
2014-08-05 14:16:02 -04:00
}
2015-03-27 15:02:56 -04:00
2015-04-01 20:59:46 -04:00
if ( ! IsPropertySupportedByBlueprint ( Param , false ) )
2015-03-27 15:02:56 -04:00
{
FError : : Throwf ( TEXT ( " Type '%s' is not supported by blueprint. Function: %s Parameter %s \n " ) , * Param - > GetCPPType ( ) , * TopFunction - > GetName ( ) , * Param - > GetName ( ) ) ;
}
2014-08-05 14:16:02 -04:00
}
}
2014-03-14 14:13:41 -04:00
// Just declaring a function, so end the nesting.
2015-01-20 09:33:54 -05:00
PostPopFunctionDeclaration ( AllClasses , TopFunction ) ;
2014-03-14 14:13:41 -04:00
2015-03-30 14:13:41 -04:00
// See what's coming next
FToken Token ;
if ( ! GetToken ( Token ) )
{
FError : : Throwf ( TEXT ( " Unexpected end of file " ) ) ;
}
2014-03-14 14:13:41 -04:00
// Optionally consume a semicolon
2015-03-30 14:13:41 -04:00
// This is optional to allow inline function definitions
if ( Token . TokenType = = TOKEN_Symbol & & ! FCString : : Stricmp ( Token . Identifier , TEXT ( " ; " ) ) )
{
// Do nothing (consume it)
}
else if ( Token . TokenType = = TOKEN_Symbol & & ! FCString : : Stricmp ( Token . Identifier , TEXT ( " { " ) ) )
{
// Skip inline function bodies
UngetToken ( Token ) ;
SkipDeclaration ( Token ) ;
}
else
{
// Put the token back so we can continue parsing as normal
UngetToken ( Token ) ;
}
2014-03-14 14:13:41 -04:00
}
/** Parses optional metadata text. */
void FHeaderParser : : ParseFieldMetaData ( TMap < FName , FString > & MetaData , const TCHAR * FieldName )
{
FToken PropertyMetaData ;
bool bMetadataPresent = false ;
if ( MatchIdentifier ( TEXT ( " UMETA " ) ) )
{
bMetadataPresent = true ;
RequireSymbol ( TEXT ( " ( " ) , * FString : : Printf ( TEXT ( " ' %s metadata' " ) , FieldName ) ) ;
if ( ! GetRawTokenRespectingQuotes ( PropertyMetaData , TCHAR ( ' ) ' ) ) )
{
FError : : Throwf ( TEXT ( " '%s': No metadata specified " ) , FieldName ) ;
}
RequireSymbol ( TEXT ( " ) " ) , * FString : : Printf ( TEXT ( " ' %s metadata' " ) , FieldName ) ) ;
}
if ( bMetadataPresent )
{
// parse apart the string
TArray < FString > Pairs ;
//@TODO: UCREMOVAL: Convert to property token reading
// break apart on | to get to the key/value pairs
FString NewData ( PropertyMetaData . String ) ;
bool bInString = false ;
int32 LastStartIndex = 0 ;
int32 CharIndex ;
for ( CharIndex = 0 ; CharIndex < NewData . Len ( ) ; + + CharIndex )
{
TCHAR Ch = NewData . GetCharArray ( ) [ CharIndex ] ;
if ( Ch = = ' " ' )
{
bInString = ! bInString ;
}
if ( ( Ch = = ' , ' ) & & ! bInString )
{
if ( LastStartIndex ! = CharIndex )
{
Pairs . Add ( NewData . Mid ( LastStartIndex , CharIndex - LastStartIndex ) ) ;
}
LastStartIndex = CharIndex + 1 ;
}
}
if ( LastStartIndex ! = CharIndex )
{
Pairs . Add ( NewData . Mid ( LastStartIndex , CharIndex - LastStartIndex ) ) ;
}
// go over all pairs
for ( int32 PairIndex = 0 ; PairIndex < Pairs . Num ( ) ; PairIndex + + )
{
// break the pair into a key and a value
FString Token = Pairs [ PairIndex ] ;
FString Key = Token ;
// by default, not value, just a key (allowed)
FString Value ;
// look for a value after an =
int32 Equals = Token . Find ( TEXT ( " = " ) ) ;
// if we have an =, break up the string
if ( Equals ! = - 1 )
{
Key = Token . Left ( Equals ) ;
Value = Token . Right ( ( Token . Len ( ) - Equals ) - 1 ) ;
}
InsertMetaDataPair ( MetaData , Key , Value ) ;
}
}
}
bool FHeaderParser : : IsBitfieldProperty ( )
{
bool bIsBitfield = false ;
// The current token is the property type (uin32, uint16, etc).
// Check the property name and then check for ':'
FToken TokenVarName ;
if ( GetToken ( TokenVarName , /*bNoConsts=*/ true ) )
{
FToken Token ;
if ( GetToken ( Token , /*bNoConsts=*/ true ) )
{
if ( Token . TokenType = = TOKEN_Symbol & & FCString : : Stricmp ( Token . Identifier , TEXT ( " : " ) ) = = 0 )
{
bIsBitfield = true ;
}
UngetToken ( Token ) ;
}
UngetToken ( TokenVarName ) ;
}
return bIsBitfield ;
}
void FHeaderParser : : ValidatePropertyIsDeprecatedIfNecessary ( FPropertyBase & VarProperty , FToken * OuterPropertyType )
{
// check to see if we have a UClassProperty using a deprecated class
if ( VarProperty . MetaClass ! = NULL & & VarProperty . MetaClass - > HasAnyClassFlags ( CLASS_Deprecated ) & & ! ( VarProperty . PropertyFlags & CPF_Deprecated ) & &
( OuterPropertyType = = NULL | | ! ( OuterPropertyType - > PropertyFlags & CPF_Deprecated ) ) )
{
FError : : Throwf ( TEXT ( " Property is using a deprecated class: %s. Property should be marked deprecated as well. " ) , * VarProperty . MetaClass - > GetPathName ( ) ) ;
}
// check to see if we have a UObjectProperty using a deprecated class.
// PropertyClass is part of a union, so only check PropertyClass if this token represents an object property
if ( ( VarProperty . Type = = CPT_ObjectReference | | VarProperty . Type = = CPT_WeakObjectReference | | VarProperty . Type = = CPT_LazyObjectReference | | VarProperty . Type = = CPT_AssetObjectReference ) & & VarProperty . PropertyClass ! = NULL
& & VarProperty . PropertyClass - > HasAnyClassFlags ( CLASS_Deprecated ) // and the object class being used has been deprecated
& & ( VarProperty . PropertyFlags & CPF_Deprecated ) = = 0 // and this property isn't marked deprecated as well
& & ( OuterPropertyType = = NULL | | ! ( OuterPropertyType - > PropertyFlags & CPF_Deprecated ) ) ) // and this property isn't in an array that was marked deprecated either
{
FError : : Throwf ( TEXT ( " Property is using a deprecated class: %s. Property should be marked deprecated as well. " ) , * VarProperty . PropertyClass - > GetPathName ( ) ) ;
}
}
2014-04-23 20:18:55 -04:00
struct FExposeOnSpawnValidator
{
// Keep this function synced with UEdGraphSchema_K2::FindSetVariableByNameFunction
static bool IsSupported ( const FPropertyBase & Property )
{
bool ProperNativeType = false ;
switch ( Property . Type )
{
case CPT_Int :
case CPT_Byte :
case CPT_Float :
case CPT_Bool :
case CPT_ObjectReference :
case CPT_String :
2014-10-02 14:23:34 -04:00
case CPT_Text :
2014-04-23 20:18:55 -04:00
case CPT_Name :
case CPT_Vector :
case CPT_Rotation :
ProperNativeType = true ;
}
if ( ! ProperNativeType & & ( CPT_Struct = = Property . Type ) & & Property . Struct )
{
static const FName BlueprintTypeName ( TEXT ( " BlueprintType " ) ) ;
ProperNativeType | = Property . Struct - > GetBoolMetaData ( BlueprintTypeName ) ;
}
return ProperNativeType ;
}
} ;
2015-01-28 09:41:47 -05:00
void FHeaderParser : : CompileVariableDeclaration ( FClasses & AllClasses , UStruct * Struct )
2014-03-14 14:13:41 -04:00
{
uint64 DisallowFlags = CPF_ParmFlags ;
uint64 EdFlags = 0 ;
// Get variable type.
FPropertyBase OriginalProperty ( CPT_None ) ;
2014-09-30 09:10:54 -04:00
FIndexRange TypeRange ;
2015-03-19 13:27:48 -04:00
GetVarType ( AllClasses , & FScope : : GetTypeScope ( Struct ) . Get ( ) , OriginalProperty , DisallowFlags , /*OuterPropertyType=*/ NULL , EPropertyDeclarationStyle : : UPROPERTY , EVariableCategory : : Member , & TypeRange ) ;
2014-03-14 14:13:41 -04:00
OriginalProperty . PropertyFlags | = EdFlags ;
FString * Category = OriginalProperty . MetaData . Find ( " Category " ) ;
2015-01-28 09:41:47 -05:00
// First check if the category was specified at all and if the property was exposed to the editor.
if ( ! Category & & ( OriginalProperty . PropertyFlags & ( CPF_Edit | CPF_BlueprintVisible ) ) )
2014-03-14 14:13:41 -04:00
{
2015-01-28 09:41:47 -05:00
static const FString AbsoluteEngineDir = FPaths : : ConvertRelativePathToFull ( FPaths : : EngineDir ( ) ) ;
FString SourceFilename = GetCurrentSourceFile ( ) - > GetFilename ( ) ;
FPaths : : NormalizeFilename ( SourceFilename ) ;
if ( Struct - > GetOutermost ( ) ! = nullptr & & ! SourceFilename . StartsWith ( AbsoluteEngineDir ) )
2014-03-14 14:13:41 -04:00
{
2015-01-28 09:41:47 -05:00
OriginalProperty . MetaData . Add ( " Category " , Struct - > GetFName ( ) . ToString ( ) ) ;
Category = OriginalProperty . MetaData . Find ( " Category " ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-28 09:41:47 -05:00
else
{
FError : : Throwf ( TEXT ( " Property is exposed to the editor or blueprints but has no Category specified. " ) ) ;
}
}
2014-03-14 14:13:41 -04:00
2015-01-28 09:41:47 -05:00
// Validate that pointer properties are not interfaces (which are not GC'd and so will cause runtime errors)
if ( OriginalProperty . PointerType = = EPointerType : : Native & & OriginalProperty . Struct - > IsChildOf ( UInterface : : StaticClass ( ) ) )
{
// Get the name of the type, removing the asterisk representing the pointer
FString TypeName = FString ( TypeRange . Count , Input + TypeRange . StartIndex ) . Trim ( ) . TrimTrailing ( ) . LeftChop ( 1 ) . TrimTrailing ( ) ;
FError : : Throwf ( TEXT ( " UPROPERTY pointers cannot be interfaces - did you mean TScriptInterface<%s>? " ) , * TypeName ) ;
2014-03-14 14:13:41 -04:00
}
// If the category was specified explicitly, it wins
if ( Category & & ! ( OriginalProperty . PropertyFlags & ( CPF_Edit | CPF_BlueprintVisible | CPF_BlueprintAssignable | CPF_BlueprintCallable ) ) )
{
FError : : Throwf ( TEXT ( " Property has a Category set but is not exposed to the editor or Blueprints with EditAnywhere, BlueprintReadWrite, VisibleAnywhere, BlueprintReadOnly, BlueprintAssignable, BlueprintCallable keywords. \r \n " ) ) ;
}
// Make sure that editblueprint variables are editable
if ( ! ( OriginalProperty . PropertyFlags & CPF_Edit ) )
{
if ( OriginalProperty . PropertyFlags & CPF_DisableEditOnInstance )
{
FError : : Throwf ( TEXT ( " Property cannot have 'DisableEditOnInstance' without being editable " ) ) ;
}
if ( OriginalProperty . PropertyFlags & CPF_DisableEditOnTemplate )
{
FError : : Throwf ( TEXT ( " Property cannot have 'DisableEditOnTemplate' without being editable " ) ) ;
}
}
// Validate.
if ( OriginalProperty . PropertyFlags & CPF_ParmFlags )
{
FError : : Throwf ( TEXT ( " Illegal type modifiers in member variable declaration " ) ) ;
}
2014-04-23 20:18:55 -04:00
if ( FString * ExposeOnSpawnValue = OriginalProperty . MetaData . Find ( TEXT ( " ExposeOnSpawn " ) ) )
{
if ( ( * ExposeOnSpawnValue = = TEXT ( " true " ) ) & & ! FExposeOnSpawnValidator : : IsSupported ( OriginalProperty ) )
{
FError : : Throwf ( TEXT ( " ExposeOnSpawn - Property cannoty be exposed " ) ) ;
}
}
2014-03-14 14:13:41 -04:00
// Process all variables of this type.
TArray < UProperty * > NewProperties ;
do
{
FToken Property = OriginalProperty ;
2015-03-20 13:26:37 -04:00
UProperty * NewProperty = GetVarNameAndDim ( Struct , Property , EVariableCategory : : Member ) ;
2014-03-14 14:13:41 -04:00
// Optionally consume the :1 at the end of a bitfield boolean declaration
if ( Property . IsBool ( ) & & MatchSymbol ( TEXT ( " : " ) ) )
{
int32 BitfieldSize = 0 ;
if ( ! GetConstInt ( /*out*/ BitfieldSize ) | | ( BitfieldSize ! = 1 ) )
{
FError : : Throwf ( TEXT ( " Bad or missing bitfield size for '%s', must be 1. " ) , * NewProperty - > GetName ( ) ) ;
}
}
// Deprecation validation
ValidatePropertyIsDeprecatedIfNecessary ( Property , NULL ) ;
if ( TopNest - > NestType ! = NEST_FunctionDeclaration )
{
if ( NewProperties . Num ( ) )
{
FError : : Throwf ( TEXT ( " Comma delimited properties cannot be converted %s.%s \n " ) , * Struct - > GetName ( ) , * NewProperty - > GetName ( ) ) ;
}
}
NewProperties . Add ( NewProperty ) ;
// we'll need any metadata tags we parsed later on when we call ConvertEOLCommentToTooltip() so the tags aren't clobbered
OriginalProperty . MetaData = Property . MetaData ;
if ( NewProperty - > HasAnyPropertyFlags ( CPF_RepNotify ) )
{
NewProperty - > RepNotifyFunc = OriginalProperty . RepNotifyName ;
}
if ( UScriptStruct * StructBeingBuilt = Cast < UScriptStruct > ( Struct ) )
{
if ( NewProperty - > ContainsInstancedObjectProperty ( ) )
{
StructBeingBuilt - > StructFlags = EStructFlags ( StructBeingBuilt - > StructFlags | STRUCT_HasInstancedReference ) ;
}
}
2014-08-05 14:16:02 -04:00
if ( NewProperty - > HasAnyPropertyFlags ( CPF_BlueprintVisible ) & & ( NewProperty - > ArrayDim > 1 ) )
{
2014-12-03 07:51:35 -05:00
FError : : Throwf ( TEXT ( " Static array cannot be exposed to blueprint %s.%s " ) , * Struct - > GetName ( ) , * NewProperty - > GetName ( ) ) ;
2014-08-05 14:16:02 -04:00
}
2015-04-01 20:59:46 -04:00
if ( NewProperty - > HasAnyPropertyFlags ( CPF_BlueprintVisible ) & & ! IsPropertySupportedByBlueprint ( NewProperty , true ) )
2015-03-27 15:02:56 -04:00
{
FError : : Throwf ( TEXT ( " Type '%s' is not supported by blueprint. %s.%s " ) , * NewProperty - > GetCPPType ( ) , * Struct - > GetName ( ) , * NewProperty - > GetName ( ) ) ;
}
2014-03-14 14:13:41 -04:00
} while ( MatchSymbol ( TEXT ( " , " ) ) ) ;
2014-08-08 09:42:56 -04:00
// Optional member initializer.
if ( MatchSymbol ( TEXT ( " = " ) ) )
{
// Skip past the specified member initializer; we make no attempt to parse it
FToken SkipToken ;
while ( GetToken ( SkipToken ) )
{
if ( SkipToken . Matches ( TEXT ( " ; " ) ) )
{
// went too far
UngetToken ( SkipToken ) ;
break ;
}
}
}
2014-03-14 14:13:41 -04:00
// Expect a semicolon.
RequireSymbol ( TEXT ( " ; " ) , TEXT ( " 'variable declaration' " ) ) ;
}
//
// Compile a statement: Either a declaration or a command.
// Returns 1 if success, 0 if end of file.
//
2015-01-20 09:33:54 -05:00
bool FHeaderParser : : CompileStatement ( FClasses & AllClasses , FUnrealSourceFile & SourceFile )
2014-03-14 14:13:41 -04:00
{
// Get a token and compile it.
FToken Token ;
if ( ! GetToken ( Token , true ) )
{
// End of file.
return false ;
}
2015-01-20 09:33:54 -05:00
else if ( ! CompileDeclaration ( AllClasses , SourceFile , Token ) )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " '%s': Bad command or expression " ) , Token . Identifier ) ;
}
return true ;
}
//
// Compute the function parameter size and save the return offset
//
//@TODO: UCREMOVAL: Need to rename ComputeFunctionParametersSize to reflect the additional work it's doing
void FHeaderParser : : ComputeFunctionParametersSize ( UClass * Class )
{
// Recurse with all child states in this class.
for ( TFieldIterator < UFunction > FuncIt ( Class , EFieldIteratorFlags : : ExcludeSuper ) ; FuncIt ; + + FuncIt )
{
UFunction * ThisFunction = * FuncIt ;
// Fix up any structs that were used as a parameter in a delegate before being defined
if ( ThisFunction - > HasAnyFunctionFlags ( FUNC_Delegate ) )
{
for ( TFieldIterator < UProperty > It ( ThisFunction ) ; It ; + + It )
{
UProperty * Param = * It ;
if ( UStructProperty * StructProp = Cast < UStructProperty > ( Param ) )
{
if ( StructProp - > Struct - > StructFlags & STRUCT_HasInstancedReference )
{
StructProp - > PropertyFlags | = CPF_ContainsInstancedReference ;
}
}
}
ThisFunction - > StaticLink ( true ) ;
}
// Compute the function parameter size, propagate some flags to the outer function, and save the return offset
// Must be done in a second phase, as StaticLink resets various fields again!
ThisFunction - > ParmsSize = 0 ;
for ( TFieldIterator < UProperty > It ( ThisFunction ) ; It ; + + It )
{
UProperty * Param = * It ;
if ( ! ( Param - > PropertyFlags & CPF_ReturnParm ) & & ( Param - > PropertyFlags & CPF_OutParm ) )
{
ThisFunction - > FunctionFlags | = FUNC_HasOutParms ;
}
if ( UStructProperty * StructProp = Cast < UStructProperty > ( Param ) )
{
if ( StructProp - > Struct - > HasDefaults ( ) )
{
ThisFunction - > FunctionFlags | = FUNC_HasDefaults ;
}
}
}
}
}
/*-----------------------------------------------------------------------------
Code skipping .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Skip over code , honoring { and } pairs .
*
* @ param NestCount number of nest levels to consume . if 0 , consumes a single statement
* @ param ErrorTag text to use in error message if EOF is encountered before we ' ve done
*/
void FHeaderParser : : SkipStatements ( int32 NestCount , const TCHAR * ErrorTag )
{
FToken Token ;
int32 OriginalNestCount = NestCount ;
while ( GetToken ( Token , true ) )
{
if ( Token . Matches ( TEXT ( " { " ) ) )
{
NestCount + + ;
}
else if ( Token . Matches ( TEXT ( " } " ) ) )
{
NestCount - - ;
}
else if ( Token . Matches ( TEXT ( " ; " ) ) & & OriginalNestCount = = 0 )
{
break ;
}
if ( NestCount < OriginalNestCount | | NestCount < 0 )
break ;
}
if ( NestCount > 0 )
{
FError : : Throwf ( TEXT ( " Unexpected end of file at end of %s " ) , ErrorTag ) ;
}
else if ( NestCount < 0 )
{
FError : : Throwf ( TEXT ( " Extraneous closing brace found in %s " ) , ErrorTag ) ;
}
}
/*-----------------------------------------------------------------------------
Main script compiling routine .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// Finalize any script-exposed functions in the specified class
//
void FHeaderParser : : FinalizeScriptExposedFunctions ( UClass * Class )
{
// Finalize all of the children introduced in this class
for ( TFieldIterator < UStruct > ChildIt ( Class , EFieldIteratorFlags : : ExcludeSuper ) ; ChildIt ; + + ChildIt )
{
UStruct * ChildStruct = * ChildIt ;
if ( UFunction * Function = Cast < UFunction > ( ChildStruct ) )
{
// Add this function to the function map of it's parent class
Class - > AddFunctionToFunctionMap ( Function ) ;
}
else if ( ChildStruct - > IsA ( UScriptStruct : : StaticClass ( ) ) )
{
// Ignore embedded structs
}
else
{
UE_LOG ( LogCompile , Warning , TEXT ( " Unknown and unexpected child named %s of type %s in %s \n " ) , * ChildStruct - > GetName ( ) , * ChildStruct - > GetClass ( ) - > GetName ( ) , * Class - > GetName ( ) ) ;
check ( false ) ;
}
}
}
//
// Parses the header associated with the specified class.
2014-04-23 18:36:17 -04:00
// Returns result enumeration.
2014-03-14 14:13:41 -04:00
//
2015-01-20 09:33:54 -05:00
ECompilationResult : : Type FHeaderParser : : ParseHeader ( FClasses & AllClasses , FUnrealSourceFile & SourceFile )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
if ( SourceFile . IsParsed ( ) )
{
return ECompilationResult : : Succeeded ;
}
SourceFile . MarkAsParsed ( ) ;
2014-03-14 14:13:41 -04:00
// Early-out if this class has previously failed some aspect of parsing
2015-01-20 09:33:54 -05:00
if ( FailedFilesAnnotation . Get ( & SourceFile ) )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:36:17 -04:00
return ECompilationResult : : OtherCompilationError ;
2014-03-14 14:13:41 -04:00
}
// Reset the parser to begin a new class
bEncounteredNewStyleClass_UnmatchedBrackets = false ;
bSpottedAutogeneratedHeaderInclude = false ;
bHaveSeenUClass = false ;
bClassHasGeneratedBody = false ;
2015-03-13 08:17:47 -04:00
bClassHasGeneratedUInterfaceBody = false ;
bClassHasGeneratedIInterfaceBody = false ;
2014-03-14 14:13:41 -04:00
2014-04-23 18:36:17 -04:00
ECompilationResult : : Type Result = ECompilationResult : : OtherCompilationError ;
2014-03-14 14:13:41 -04:00
// Message.
if ( FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " VERBOSE " ) ) )
{
// Message.
2015-01-20 09:33:54 -05:00
Warn - > Logf ( TEXT ( " Parsing %s " ) , * SourceFile . GetFilename ( ) ) ;
2014-03-14 14:13:41 -04:00
}
// Init compiler variables.
2015-01-20 09:33:54 -05:00
ResetParser ( * SourceFile . GetContent ( ) ) ;
2014-03-14 14:13:41 -04:00
// Init nesting.
NestLevel = 0 ;
TopNest = NULL ;
2015-01-20 09:33:54 -05:00
PushNest ( NEST_GlobalScope , nullptr , & SourceFile ) ;
2014-03-14 14:13:41 -04:00
// C++ classes default to private access level
CurrentAccessSpecifier = ACCESS_Private ;
// Try to compile it, and catch any errors.
bool bEmptyFile = true ;
2015-01-20 09:33:54 -05:00
// Tells if this header defines no-export classes only.
bool bNoExportClassesOnly = true ;
2014-03-14 14:13:41 -04:00
# if !PLATFORM_EXCEPTIONS_DISABLED
try
# endif
{
// Parse entire program.
2015-01-20 09:33:54 -05:00
while ( CompileStatement ( AllClasses , SourceFile ) )
2014-03-14 14:13:41 -04:00
{
bEmptyFile = false ;
// Clear out the previous comment in anticipation of the next statement.
ClearComment ( ) ;
StatementsParsed + + ;
}
2015-01-20 09:33:54 -05:00
PopNest ( NEST_GlobalScope , TEXT ( " Global scope " ) ) ;
auto ScopeTypeIterator = SourceFile . GetScope ( ) - > GetTypeIterator ( ) ;
while ( ScopeTypeIterator . MoveNext ( ) )
{
auto * Type = * ScopeTypeIterator ;
if ( ! Type - > IsA < UScriptStruct > ( ) & & ! Type - > IsA < UClass > ( ) )
{
continue ;
}
UStruct * Struct = Cast < UStruct > ( Type ) ;
// now validate all delegate variables declared in the class
TMap < FName , UFunction * > DelegateCache ;
FixupDelegateProperties ( AllClasses , Struct , FScope : : GetTypeScope ( Struct ) . Get ( ) , DelegateCache ) ;
}
2014-03-14 14:13:41 -04:00
// Precompute info for runtime optimization.
LinesParsed + = InputLine ;
if ( RPCsNeedingHookup . Num ( ) > 0 )
{
FString ErrorMsg ( TEXT ( " Request functions missing response pairs: \r \n " ) ) ;
for ( TMap < int32 , FString > : : TConstIterator It ( RPCsNeedingHookup ) ; It ; + + It )
{
ErrorMsg + = FString : : Printf ( TEXT ( " %s missing id %d \r \n " ) , * It . Value ( ) , It . Key ( ) ) ;
}
RPCsNeedingHookup . Empty ( ) ;
FError : : Throwf ( * ErrorMsg ) ;
}
// Make sure the compilation ended with valid nesting.
if ( bEncounteredNewStyleClass_UnmatchedBrackets )
{
FError : : Throwf ( TEXT ( " Missing } at end of class " ) ) ;
}
2015-01-20 09:33:54 -05:00
if ( NestLevel = = 1 )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Internal nest inconsistency " ) ) ;
}
2015-01-20 09:33:54 -05:00
else if ( NestLevel > 2 )
2014-03-14 14:13:41 -04:00
{
FError : : Throwf ( TEXT ( " Unexpected end of script in '%s' block " ) , NestTypeName ( TopNest - > NestType ) ) ;
}
// First-pass success.
2014-04-23 18:36:17 -04:00
Result = ECompilationResult : : Succeeded ;
2015-01-20 09:33:54 -05:00
for ( auto * Class : SourceFile . GetDefinedClasses ( ) )
{
PostParsingClassSetup ( Class ) ;
// Clean up and exit.
Class - > Bind ( ) ;
// Finalize functions
if ( Result = = ECompilationResult : : Succeeded )
{
FinalizeScriptExposedFunctions ( Class ) ;
}
bNoExportClassesOnly = bNoExportClassesOnly & & Class - > HasAnyClassFlags ( CLASS_NoExport ) ;
}
check ( SourceFile . IsParsed ( ) ) ;
2015-01-20 13:06:35 -05:00
if ( ! bSpottedAutogeneratedHeaderInclude & & ! bEmptyFile & & ! bNoExportClassesOnly )
{
const FString ExpectedHeaderName = SourceFile . GetGeneratedHeaderFilename ( ) ;
FError : : Throwf ( TEXT ( " Expected an include at the top of the header: '#include \" %s \" ' " ) , * ExpectedHeaderName ) ;
}
2014-03-14 14:13:41 -04:00
}
# if !PLATFORM_EXCEPTIONS_DISABLED
catch ( TCHAR * ErrorMsg )
{
2015-01-20 13:06:35 -05:00
if ( NestLevel = = 0 )
{
// Pushing nest so there is a file context for this error.
PushNest ( NEST_GlobalScope , nullptr , & SourceFile ) ;
}
2014-03-14 14:13:41 -04:00
// Handle compiler error.
{
TGuardValue < ELogTimes : : Type > DisableLogTimes ( GPrintLogTimes , ELogTimes : : None ) ;
2015-01-20 09:33:54 -05:00
FString FormattedErrorMessageWithContext = FString : : Printf ( TEXT ( " %s: %s " ) , * GetContext ( ) , ErrorMsg ) ;
2014-03-14 14:13:41 -04:00
UE_LOG ( LogCompile , Log , TEXT ( " %s " ) , * FormattedErrorMessageWithContext ) ;
2015-01-20 09:33:54 -05:00
Warn - > Log ( ELogVerbosity : : Error , ErrorMsg ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-20 09:33:54 -05:00
FailedFilesAnnotation . Set ( & SourceFile ) ;
2014-04-23 18:36:17 -04:00
Result = GCompilationResult ;
2014-03-14 14:13:41 -04:00
}
# endif
2014-04-23 18:36:17 -04:00
return Result ; //@TODO: UCREMOVAL: This function is always returning succeeded even on a compiler error; should this continue?
2014-03-14 14:13:41 -04:00
}
/*-----------------------------------------------------------------------------
Global functions .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-01-20 09:33:54 -05:00
ECompilationResult : : Type FHeaderParser : : ParseRestOfModulesSourceFiles ( FClasses & AllClasses , UPackage * ModulePackage , FHeaderParser & HeaderParser )
2014-03-14 14:13:41 -04:00
{
2015-01-20 09:33:54 -05:00
for ( auto & Pair : GUnrealSourceFilesMap )
{
FUnrealSourceFile & SourceFile = Pair . Value . Get ( ) ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
if ( SourceFile . GetPackage ( ) = = ModulePackage & & ( ! SourceFile . IsParsed ( ) | | SourceFile . GetDefinedClassesCount ( ) = = 0 ) )
{
ECompilationResult : : Type Result ;
if ( ( Result = ParseHeaders ( AllClasses , HeaderParser , SourceFile , true ) ) ! = ECompilationResult : : Succeeded )
{
return Result ;
}
}
}
return ECompilationResult : : Succeeded ;
}
// Parse Class's annotated headers and optionally its child classes.
ECompilationResult : : Type FHeaderParser : : ParseHeaders ( FClasses & AllClasses , FHeaderParser & HeaderParser , FUnrealSourceFile & SourceFile , bool bParseSubclasses )
{
ECompilationResult : : Type Result = ECompilationResult : : Succeeded ;
if ( SourceFile . AreDependenciesResolved ( ) )
{
return Result ;
}
SourceFile . MarkDependenciesResolved ( ) ;
TArray < FUnrealSourceFile * > SourceFilesRequired ;
for ( auto & Include : SourceFile . GetIncludes ( ) )
{
if ( Include . GetId ( ) = = " Object.h " )
{
continue ;
}
FUnrealSourceFile * DepFile = Include . Resolve ( ) ;
if ( DepFile )
{
SourceFilesRequired . Add ( DepFile ) ;
}
}
auto Classes = SourceFile . GetDefinedClasses ( ) ;
for ( auto * Class : Classes )
{
for ( auto * ParentClass = Class - > GetSuperClass ( ) ; ParentClass & & ! ParentClass - > HasAnyClassFlags ( CLASS_Parsed | CLASS_Intrinsic ) ; ParentClass = ParentClass - > GetSuperClass ( ) )
{
SourceFilesRequired . Add ( & GTypeDefinitionInfoMap [ ParentClass ] - > GetUnrealSourceFile ( ) ) ;
}
}
for ( auto * RequiredFile : SourceFilesRequired )
{
SourceFile . GetScope ( ) - > IncludeScope ( & RequiredFile - > GetScope ( ) . Get ( ) ) ;
ECompilationResult : : Type SuperClassParseResult = ParseHeaders ( AllClasses , HeaderParser , * RequiredFile , true ) ;
if ( SuperClassParseResult = = ECompilationResult : : Succeeded )
{
continue ;
}
SuperClassParseResult = ParseHeaders ( AllClasses , HeaderParser , * RequiredFile , false ) ;
if ( SuperClassParseResult ! = ECompilationResult : : Succeeded )
{
Result = SuperClassParseResult ;
break ;
}
}
// Parse the file
{
ECompilationResult : : Type OneFileResult = HeaderParser . ParseHeader ( AllClasses , SourceFile ) ;
for ( auto * Class : Classes )
{
Class - > ClassFlags | = CLASS_Parsed ;
}
if ( OneFileResult ! = ECompilationResult : : Succeeded )
{
// if we couldn't parse this file fail.
return OneFileResult ;
}
}
2014-03-14 14:13:41 -04:00
// Success.
2014-04-23 18:36:17 -04:00
return Result ;
2014-03-14 14:13:41 -04:00
}
bool FHeaderParser : : DependentClassNameFromHeader ( const TCHAR * HeaderFilename , FString & OutClassName )
{
FString DependentClassName ( HeaderFilename ) ;
const int32 ExtensionIndex = DependentClassName . Find ( TEXT ( " . " ) ) ;
if ( ExtensionIndex ! = INDEX_NONE )
{
// Generate UHeaderName name for this header.
OutClassName = FString ( TEXT ( " U " ) ) + FPaths : : GetBaseFilename ( * DependentClassName ) ;
return true ;
}
return false ;
}
2015-01-20 09:33:54 -05:00
/**
* Gets source files ordered by UCLASSes inheritance .
*
* @ param CurrentPackage Current package .
* @ param AllClasses Current class tree .
*
* @ returns Array of source files .
*/
TArray < FUnrealSourceFile * > GetSourceFilesWithInheritanceOrdering ( UPackage * CurrentPackage , FClasses & AllClasses )
{
TArray < FUnrealSourceFile * > SourceFiles ;
auto Classes = AllClasses . GetClassesInPackage ( ) ;
// First add source files with the inheritance order.
for ( auto * Class : Classes )
{
auto * DefinitionInfoPtr = GTypeDefinitionInfoMap . Find ( Class ) ;
if ( DefinitionInfoPtr = = nullptr )
{
continue ;
}
auto & SourceFile = ( * DefinitionInfoPtr ) - > GetUnrealSourceFile ( ) ;
if ( ! SourceFiles . Contains ( & SourceFile )
& & SourceFile . GetScope ( ) - > ContainsTypes ( ) )
{
SourceFiles . Add ( & SourceFile ) ;
}
}
// Then add the rest.
for ( auto & Pair : GUnrealSourceFilesMap )
{
auto & SourceFile = Pair . Value . Get ( ) ;
if ( SourceFile . GetPackage ( ) = = CurrentPackage
& & ! SourceFiles . Contains ( & SourceFile )
& & SourceFile . GetScope ( ) - > ContainsTypes ( ) )
{
SourceFiles . Add ( & SourceFile ) ;
}
}
return SourceFiles ;
}
2014-03-14 14:13:41 -04:00
// Begins the process of exporting C++ class declarations for native classes in the specified package
2015-03-02 06:44:04 -05:00
void FHeaderParser : : ExportNativeHeaders (
UPackage * CurrentPackage ,
FClasses & AllClasses ,
bool bAllowSaveExportedHeaders
# if WITH_HOT_RELOAD_CTORS
, bool bExportVTableConstructors
# endif // WITH_HOT_RELOAD_CTORS
)
2014-03-14 14:13:41 -04:00
{
// Build a list of header filenames
TArray < FString > ClassHeaderFilenames ;
2015-03-27 18:10:52 -04:00
new ( ClassHeaderFilenames ) FString ( ) ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
auto SourceFiles = GetSourceFilesWithInheritanceOrdering ( CurrentPackage , AllClasses ) ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
if ( SourceFiles . Num ( ) > 0 )
2014-03-14 14:13:41 -04:00
{
const static bool bQuiet = ! FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " VERBOSE " ) ) ;
if ( CurrentPackage ! = NULL )
{
if ( bQuiet )
{
UE_LOG ( LogCompile , Log , TEXT ( " Exporting native class declarations for %s " ) , * CurrentPackage - > GetName ( ) ) ;
}
else
{
UE_LOG ( LogCompile , Warning , TEXT ( " Exporting native class declarations for %s " ) , * CurrentPackage - > GetName ( ) ) ;
}
}
else
{
if ( bQuiet )
{
UE_LOG ( LogCompile , Log , TEXT ( " Exporting native class declarations " ) ) ;
}
else
{
UE_LOG ( LogCompile , Warning , TEXT ( " Exporting native class declarations " ) ) ;
}
}
// Export native class definitions to package header files.
2015-03-02 06:44:04 -05:00
FNativeClassHeaderGenerator (
CurrentPackage ,
SourceFiles ,
AllClasses ,
bAllowSaveExportedHeaders
# if WITH_HOT_RELOAD_CTORS
, bExportVTableConstructors
# endif // WITH_HOT_RELOAD_CTORS
) ;
2014-03-14 14:13:41 -04:00
}
}
FHeaderParser : : FHeaderParser ( FFeedbackContext * InWarn )
: FBaseParser ( )
, Warn ( InWarn )
, bSpottedAutogeneratedHeaderInclude ( false )
, TopNest ( NULL )
{
FScriptLocation : : Compiler = this ;
// This should be moved to some sort of config
StructsWithNoPrefix . Add ( " uint64 " ) ;
StructsWithNoPrefix . Add ( " uint32 " ) ;
StructsWithNoPrefix . Add ( " double " ) ;
StructsWithTPrefix . Add ( " IndirectArray " ) ;
StructsWithTPrefix . Add ( " BitArray " ) ;
StructsWithTPrefix . Add ( " SparseArray " ) ;
StructsWithTPrefix . Add ( " Set " ) ;
StructsWithTPrefix . Add ( " Map " ) ;
StructsWithTPrefix . Add ( " MultiMap " ) ;
StructsWithTPrefix . Add ( " SharedPtr " ) ;
// List of legal delegate parameter counts
DelegateParameterCountStrings . Add ( TEXT ( " _OneParam " ) ) ;
DelegateParameterCountStrings . Add ( TEXT ( " _TwoParams " ) ) ;
DelegateParameterCountStrings . Add ( TEXT ( " _ThreeParams " ) ) ;
DelegateParameterCountStrings . Add ( TEXT ( " _FourParams " ) ) ;
DelegateParameterCountStrings . Add ( TEXT ( " _FiveParams " ) ) ;
DelegateParameterCountStrings . Add ( TEXT ( " _SixParams " ) ) ;
DelegateParameterCountStrings . Add ( TEXT ( " _SevenParams " ) ) ;
DelegateParameterCountStrings . Add ( TEXT ( " _EightParams " ) ) ;
2015-04-10 06:02:22 -04:00
FString Version ;
if ( GConfig - > GetString ( TEXT ( " GeneratedCodeVersion " ) , TEXT ( " UnrealHeaderTool " ) , Version , GEngineIni ) )
{
DefaultGeneratedCodeVersion = ToGeneratedCodeVersion ( Version ) ;
}
2014-03-14 14:13:41 -04:00
}
// Throws if a specifier value wasn't provided
void FHeaderParser : : RequireSpecifierValue ( const FPropertySpecifier & Specifier , bool bRequireExactlyOne )
{
if ( Specifier . Values . Num ( ) = = 0 )
{
FError : : Throwf ( TEXT ( " The specifier '%s' must be given a value " ) , * Specifier . Key ) ;
}
else if ( ( Specifier . Values . Num ( ) ! = 1 ) & & bRequireExactlyOne )
{
FError : : Throwf ( TEXT ( " The specifier '%s' must be given exactly one value " ) , * Specifier . Key ) ;
}
}
// Throws if a specifier value wasn't provided
FString FHeaderParser : : RequireExactlyOneSpecifierValue ( const FPropertySpecifier & Specifier )
{
RequireSpecifierValue ( Specifier , /*bRequireExactlyOne*/ true ) ;
return Specifier . Values [ 0 ] ;
}
2014-04-29 06:45:18 -04:00
// Exports the class to all vailable plugins
2014-05-22 01:20:24 -04:00
void ExportClassToScriptPlugins ( UClass * Class , const FManifestModule & Module , IScriptGeneratorPluginInterface & ScriptPlugin )
2014-04-29 06:45:18 -04:00
{
2015-01-20 09:33:54 -05:00
auto DefinitionInfoRef = GTypeDefinitionInfoMap . Find ( Class ) ;
if ( DefinitionInfoRef = = nullptr )
{
const FString Empty = TEXT ( " " ) ;
ScriptPlugin . ExportClass ( Class , Empty , Empty , false ) ;
}
else
{
auto & SourceFile = ( * DefinitionInfoRef ) - > GetUnrealSourceFile ( ) ;
ScriptPlugin . ExportClass ( Class , SourceFile . GetFilename ( ) , SourceFile . GetGeneratedFilename ( ) , SourceFile . HasChanged ( ) ) ;
}
2014-04-29 06:45:18 -04:00
}
2014-05-22 01:20:24 -04:00
2014-04-29 06:45:18 -04:00
// Exports class tree to all available plugins
2014-05-22 01:20:24 -04:00
void ExportClassTreeToScriptPlugins ( const FClassTree * Node , const FManifestModule & Module , IScriptGeneratorPluginInterface & ScriptPlugin )
2014-04-29 06:45:18 -04:00
{
for ( int32 ChildIndex = 0 ; ChildIndex < Node - > NumChildren ( ) ; + + ChildIndex )
{
auto ChildNode = Node - > GetChild ( ChildIndex ) ;
2014-05-22 01:20:24 -04:00
ExportClassToScriptPlugins ( ChildNode - > GetClass ( ) , Module , ScriptPlugin ) ;
2014-04-29 06:45:18 -04:00
}
for ( int32 ChildIndex = 0 ; ChildIndex < Node - > NumChildren ( ) ; + + ChildIndex )
{
auto ChildNode = Node - > GetChild ( ChildIndex ) ;
2014-05-22 01:20:24 -04:00
ExportClassTreeToScriptPlugins ( ChildNode , Module , ScriptPlugin ) ;
2014-04-29 06:45:18 -04:00
}
}
2014-04-23 18:42:01 -04:00
// Parse all headers for classes that are inside CurrentPackage.
2015-03-02 06:44:04 -05:00
ECompilationResult : : Type FHeaderParser : : ParseAllHeadersInside (
FClasses & ModuleClasses ,
FFeedbackContext * Warn ,
UPackage * CurrentPackage ,
const FManifestModule & Module ,
TArray < IScriptGeneratorPluginInterface * > & ScriptPlugins
# if WITH_HOT_RELOAD_CTORS
, bool bExportVTableConstructors
# endif // WITH_HOT_RELOAD_CTORS
)
2014-03-14 14:13:41 -04:00
{
// Disable loading of objects outside of this package (or more exactly, objects which aren't UFields, CDO, or templates)
TGuardValue < bool > AutoRestoreVerifyObjectRefsFlag ( GVerifyObjectReferencesOnly , true ) ;
// Create the header parser and register it as the warning context.
// Note: This must be declared outside the try block, since the catch block will log into it.
FHeaderParser HeaderParser ( Warn ) ;
2015-04-10 06:02:22 -04:00
HeaderParser . CurrentlyParsedModule = & Module ;
2014-03-14 14:13:41 -04:00
Warn - > SetContext ( & HeaderParser ) ;
// Set up a filename for the error context if we don't even get as far parsing a class
2015-01-20 09:33:54 -05:00
HeaderParser . Filename = IFileManager : : Get ( ) . ConvertToAbsolutePathForExternalAppForRead ( * GTypeDefinitionInfoMap [ ModuleClasses . GetRootClass ( ) ] - > GetUnrealSourceFile ( ) . GetFilename ( ) ) ;
2014-03-14 14:13:41 -04:00
// Hierarchically parse all classes.
2014-04-23 18:36:17 -04:00
ECompilationResult : : Type Result = ECompilationResult : : Succeeded ;
2014-03-14 14:13:41 -04:00
# if !PLATFORM_EXCEPTIONS_DISABLED
try
# endif
{
2015-01-20 09:33:54 -05:00
for ( auto * SourceFilePtr : GPublicSourceFileSet )
{
FUnrealSourceFile & SourceFile = * SourceFilePtr ;
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
if ( SourceFile . GetPackage ( ) = = CurrentPackage & & ( ! SourceFile . IsParsed ( ) | | SourceFile . GetDefinedClassesCount ( ) = = 0 ) )
{
2015-04-22 14:47:12 -04:00
Result = ParseHeaders ( ModuleClasses , HeaderParser , SourceFile , true ) ;
if ( Result ! = ECompilationResult : : Succeeded )
2015-01-20 09:33:54 -05:00
{
return Result ;
}
}
}
if ( Result = = ECompilationResult : : Succeeded )
{
Result = FHeaderParser : : ParseRestOfModulesSourceFiles ( ModuleClasses , CurrentPackage , HeaderParser ) ;
}
2014-03-14 14:13:41 -04:00
// Export the autogenerated code wrappers
2014-04-23 18:36:17 -04:00
if ( Result = = ECompilationResult : : Succeeded )
2014-03-14 14:13:41 -04:00
{
// At this point all headers have been parsed and the header parser will
// no longer have up to date info about what's being done so unregister it
// from the feedback context.
Warn - > SetContext ( NULL ) ;
2015-03-27 17:16:51 -04:00
double ExportTime = 0.0 ;
{
FScopedDurationTimer Timer ( ExportTime ) ;
ExportNativeHeaders (
CurrentPackage ,
ModuleClasses ,
Module . SaveExportedHeaders
2015-03-02 06:44:04 -05:00
# if WITH_HOT_RELOAD_CTORS
2015-03-27 17:16:51 -04:00
, bExportVTableConstructors
2015-03-02 06:44:04 -05:00
# endif // WITH_HOT_RELOAD_CTORS
2015-03-27 17:16:51 -04:00
) ;
}
GHeaderCodeGenTime + = ExportTime ;
2014-03-14 14:13:41 -04:00
// Done with header generation
if ( HeaderParser . LinesParsed > 0 )
{
2015-03-27 17:16:51 -04:00
UE_LOG ( LogCompile , Log , TEXT ( " Success: Parsed %i line(s), %i statement(s) in %.2f secs. \r \n " ) , HeaderParser . LinesParsed , HeaderParser . StatementsParsed , ExportTime ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-03-27 17:16:51 -04:00
UE_LOG ( LogCompile , Log , TEXT ( " Success: Everything is up to date (in %.2f secs) " ) , ExportTime ) ;
2014-03-14 14:13:41 -04:00
}
}
}
# if !PLATFORM_EXCEPTIONS_DISABLED
catch ( TCHAR * ErrorMsg )
{
Warn - > Log ( ELogVerbosity : : Error , ErrorMsg ) ;
2014-04-23 18:36:17 -04:00
Result = GCompilationResult ;
2014-03-14 14:13:41 -04:00
}
# endif
// Unregister the header parser from the feedback context
Warn - > SetContext ( NULL ) ;
2014-04-29 06:45:18 -04:00
if ( Result = = ECompilationResult : : Succeeded & & ScriptPlugins . Num ( ) )
{
2015-03-27 17:16:51 -04:00
FScopedDurationTimer PluginTimeTracker ( GPluginOverheadTime ) ;
2014-06-02 07:02:29 -04:00
auto RootNode = & ModuleClasses . GetClassTree ( ) ;
2014-05-22 01:20:24 -04:00
for ( auto Plugin : ScriptPlugins )
{
2014-09-29 04:03:28 -04:00
if ( Plugin - > ShouldExportClassesForModule ( Module . Name , Module . ModuleType , Module . GeneratedIncludeDirectory ) )
2014-05-22 01:20:24 -04:00
{
ExportClassToScriptPlugins ( RootNode - > GetClass ( ) , Module , * Plugin ) ;
ExportClassTreeToScriptPlugins ( RootNode , Module , * Plugin ) ;
}
}
2014-04-29 06:45:18 -04:00
}
2014-04-23 18:36:17 -04:00
return Result ;
2014-03-14 14:13:41 -04:00
}
/**
* Returns True if the given class name includes a valid Unreal prefix and matches up with the given original class .
*
* @ param InNameToCheck - Name w / potential prefix to check
* @ param OriginalClassName - Name of class w / no prefix to check against
*/
bool FHeaderParser : : ClassNameHasValidPrefix ( const FString InNameToCheck , const FString OriginalClassName )
{
bool bIsLabledDeprecated ;
const FString ClassPrefix = GetClassPrefix ( InNameToCheck , bIsLabledDeprecated ) ;
// If the class is labeled deprecated, don't try to resolve it during header generation, valid results can't be guaranteed.
if ( bIsLabledDeprecated )
{
return true ;
}
if ( ClassPrefix . IsEmpty ( ) )
{
return false ;
}
FString TestString = FString : : Printf ( TEXT ( " %s%s " ) , * ClassPrefix , * OriginalClassName ) ;
const bool bNamesMatch = ( InNameToCheck = = * TestString ) ;
return bNamesMatch ;
}
void FHeaderParser : : ParseClassName ( const TCHAR * Temp , FString & ClassName )
{
// Skip leading whitespace
while ( FChar : : IsWhitespace ( * Temp ) )
{
+ + Temp ;
}
// Run thru characters (note: relying on later code to reject the name for a leading number, etc...)
const TCHAR * StringStart = Temp ;
while ( FChar : : IsAlnum ( * Temp ) | | FChar : : IsUnderscore ( * Temp ) )
{
+ + Temp ;
}
ClassName = FString ( Temp - StringStart , StringStart ) ;
if ( ClassName . EndsWith ( TEXT ( " _API " ) , ESearchCase : : CaseSensitive ) )
{
// RequiresAPI token for a given module
//@TODO: UCREMOVAL: Validate the module name
FString RequiresAPISymbol = ClassName ;
// Now get the real class name
ClassName . Empty ( ) ;
ParseClassName ( Temp , ClassName ) ;
}
}
// Performs a preliminary parse of the text in the specified buffer, pulling out useful information for the header generation process
2015-01-20 09:33:54 -05:00
void FHeaderParser : : SimplifiedClassParse ( const TCHAR * InBuffer , TArray < FSimplifiedParsingClassInfo > & OutParsedClassArray , TArray < FHeaderProvider > & DependentOn , FStringOutputDevice & ClassHeaderTextStrippedOfCppText )
2014-03-14 14:13:41 -04:00
{
FHeaderPreParser Parser ;
FString StrLine ;
FString ClassName ;
FString BaseClassName ;
// Two passes, preprocessor, then looking for the class stuff
// The layer of multi-line comment we are in.
int32 CommentDim = 0 ;
int32 CurrentLine = 0 ;
const TCHAR * Buffer = InBuffer ;
// Preprocessor pass
while ( FParse : : Line ( & Buffer , StrLine , true ) )
{
CurrentLine + + ;
const TCHAR * Str = * StrLine ;
bool bProcess = CommentDim < = 0 ; // for skipping nested multi-line comments
int32 BraceCount = 0 ;
2014-05-01 15:24:39 -04:00
if ( ! bProcess )
{
ClassHeaderTextStrippedOfCppText . Logf ( TEXT ( " %s \r \n " ) , * StrLine ) ;
continue ;
}
bool bIf = FParse : : Command ( & Str , TEXT ( " #if " ) ) ;
if ( bIf | | FParse : : Command ( & Str , TEXT ( " #ifdef " ) ) | | FParse : : Command ( & Str , TEXT ( " #ifndef " ) ) )
2014-03-14 14:13:41 -04:00
{
FStringOutputDevice TextDumpDummy ;
int32 PreprocessorNest = 1 ;
FStringOutputDevice * Target = NULL ;
FStringOutputDevice * SpacerTarget = NULL ;
bool bKeepPreprocessorDirectives = true ;
bool bNotCPP = false ;
bool bCPP = false ;
bool bUnknownDirective = false ;
2014-05-01 15:24:39 -04:00
if ( bIf & & FParse : : Command ( & Str , TEXT ( " CPP " ) ) )
2014-03-14 14:13:41 -04:00
{
Target = & TextDumpDummy ;
SpacerTarget = & ClassHeaderTextStrippedOfCppText ;
bCPP = true ;
}
2014-05-01 15:24:39 -04:00
else if ( bIf & & FParse : : Command ( & Str , TEXT ( " !CPP " ) ) )
2014-03-14 14:13:41 -04:00
{
Target = & ClassHeaderTextStrippedOfCppText ;
bKeepPreprocessorDirectives = false ;
bNotCPP = true ;
}
2015-03-02 06:44:04 -05:00
# if WITH_HOT_RELOAD_CTORS
else if ( bIf & & FParse : : Command ( & Str , TEXT ( " WITH_HOT_RELOAD " ) ) )
{
Target = & ClassHeaderTextStrippedOfCppText ;
bKeepPreprocessorDirectives = false ;
}
# endif // WITH_HOT_RELOAD_CTORS
2014-07-30 12:35:58 -04:00
else if ( bIf & & ( FParse : : Command ( & Str , TEXT ( " WITH_EDITORONLY_DATA " ) ) | | FParse : : Command ( & Str , TEXT ( " WITH_EDITOR " ) ) ) )
2014-03-14 14:13:41 -04:00
{
Target = & ClassHeaderTextStrippedOfCppText ;
bUnknownDirective = true ;
}
else
{
2014-05-01 15:24:39 -04:00
// Unknown directives or #ifdef or #ifndef are always treated as CPP
2014-03-14 14:13:41 -04:00
bUnknownDirective = true ;
Target = & TextDumpDummy ;
SpacerTarget = & ClassHeaderTextStrippedOfCppText ;
}
if ( bKeepPreprocessorDirectives )
{
Target - > Logf ( TEXT ( " %s \r \n " ) , * StrLine ) ;
}
else
{
Target - > Logf ( TEXT ( " \r \n " ) ) ;
}
if ( SpacerTarget ! = NULL )
{
// Make sure script line numbers don't get out of whack if there is an inline CPP block in there
SpacerTarget - > Logf ( TEXT ( " \r \n " ) ) ;
}
while ( ( PreprocessorNest > 0 ) & & FParse : : Line ( & Buffer , StrLine , 1 ) )
{
if ( SpacerTarget ! = NULL )
{
// Make sure script line numbers don't get out of whack if there is an inline CPP block in there
SpacerTarget - > Logf ( TEXT ( " \r \n " ) ) ;
}
CurrentLine + + ;
2015-04-22 14:47:12 -04:00
Str = * StrLine ;
2014-03-14 14:13:41 -04:00
bool bIsPrep = false ;
if ( FParse : : Command ( & Str , TEXT ( " #endif " ) ) )
{
PreprocessorNest - - ;
bIsPrep = true ;
}
2014-05-01 15:24:39 -04:00
else if ( FParse : : Command ( & Str , TEXT ( " #if " ) ) | | FParse : : Command ( & Str , TEXT ( " #ifdef " ) ) | | FParse : : Command ( & Str , TEXT ( " #ifndef " ) ) )
2014-03-14 14:13:41 -04:00
{
PreprocessorNest + + ;
bIsPrep = true ;
}
else if ( FParse : : Command ( & Str , TEXT ( " #elif " ) ) )
{
bIsPrep = true ;
}
else if ( PreprocessorNest = = 1 & & FParse : : Command ( & Str , TEXT ( " #else " ) ) )
{
if ( ! bUnknownDirective )
{
if ( ! bNotCPP & & ! bCPP )
{
FError : : Throwf ( TEXT ( " Bad preprocessor directive in metadata declaration: %s; Only 'CPP' can have ! or #else directives " ) , * ClassName ) ;
}
Swap ( bNotCPP , bCPP ) ;
if ( bCPP )
{
Target = & TextDumpDummy ;
SpacerTarget = & ClassHeaderTextStrippedOfCppText ;
bKeepPreprocessorDirectives = true ;
StrLine = TEXT ( " #if CPP \r \n " ) ;
}
else
{
bKeepPreprocessorDirectives = false ;
Target = & ClassHeaderTextStrippedOfCppText ;
SpacerTarget = NULL ;
}
}
bIsPrep = true ;
}
if ( bKeepPreprocessorDirectives | | ! bIsPrep )
{
Target - > Logf ( TEXT ( " %s \r \n " ) , * StrLine ) ;
}
else
{
Target - > Logf ( TEXT ( " \r \n " ) ) ;
}
}
}
2014-05-01 15:24:39 -04:00
else if ( FParse : : Command ( & Str , TEXT ( " #include " ) ) )
2014-03-14 14:13:41 -04:00
{
ClassHeaderTextStrippedOfCppText . Logf ( TEXT ( " %s \r \n " ) , * StrLine ) ;
}
else
{
ClassHeaderTextStrippedOfCppText . Logf ( TEXT ( " %s \r \n " ) , * StrLine ) ;
}
}
// now start over go look for the class
CommentDim = 0 ;
CurrentLine = 0 ;
Buffer = * ClassHeaderTextStrippedOfCppText ;
const TCHAR * StartOfLine = Buffer ;
bool bFoundGeneratedInclude = false ;
while ( FParse : : Line ( & Buffer , StrLine , true ) )
{
CurrentLine + + ;
const TCHAR * Str = * StrLine ;
bool bProcess = CommentDim < = 0 ; // for skipping nested multi-line comments
int32 BraceCount = 0 ;
if ( bProcess & & FParse : : Command ( & Str , TEXT ( " #if " ) ) )
{
}
else if ( bProcess & & FParse : : Command ( & Str , TEXT ( " #include " ) ) )
{
if ( bFoundGeneratedInclude )
{
FError : : Throwf ( TEXT ( " #include found after .generated.h file - the .generated.h file should always be the last #include in a header " ) ) ;
}
// Handle #include directives as if they were 'dependson' keywords.
FString DependsOnHeaderName = Str ;
bFoundGeneratedInclude = DependsOnHeaderName . Contains ( TEXT ( " .generated.h " ) ) ;
if ( ! bFoundGeneratedInclude & & DependsOnHeaderName . Len ( ) )
{
bool bIsQuotedInclude = DependsOnHeaderName [ 0 ] = = ' \" ' ;
int32 HeaderFilenameEnd = DependsOnHeaderName . Find ( bIsQuotedInclude ? TEXT ( " \" " ) : TEXT ( " > " ) , ESearchCase : : CaseSensitive , ESearchDir : : FromStart , 1 ) ;
if ( HeaderFilenameEnd ! = INDEX_NONE )
{
// Include the extension in the name so that we later know where this entry came from.
2015-01-20 09:33:54 -05:00
DependentOn . Add ( FHeaderProvider ( EHeaderProviderSourceType : : FileName , * FPaths : : GetCleanFilename ( DependsOnHeaderName . Mid ( 1 , HeaderFilenameEnd - 1 ) ) ) ) ;
2014-03-14 14:13:41 -04:00
}
}
}
else if ( bProcess & & FParse : : Command ( & Str , TEXT ( " #else " ) ) )
{
}
else if ( bProcess & & FParse : : Command ( & Str , TEXT ( " #elif " ) ) )
{
}
else if ( bProcess & & FParse : : Command ( & Str , TEXT ( " #endif " ) ) )
{
}
else
{
int32 Pos = INDEX_NONE ;
int32 EndPos = INDEX_NONE ;
int32 StrBegin = INDEX_NONE ;
int32 StrEnd = INDEX_NONE ;
bool bEscaped = false ;
for ( int32 CharPos = 0 ; CharPos < StrLine . Len ( ) ; CharPos + + )
{
if ( bEscaped )
{
bEscaped = false ;
}
else if ( StrLine [ CharPos ] = = TEXT ( ' \\ ' ) )
{
bEscaped = true ;
}
else if ( StrLine [ CharPos ] = = TEXT ( ' \" ' ) )
{
if ( StrBegin = = INDEX_NONE )
{
StrBegin = CharPos ;
}
else
{
StrEnd = CharPos ;
break ;
}
}
}
// Stub out the comments, ignoring anything inside literal strings.
Pos = StrLine . Find ( TEXT ( " // " ) ) ;
if ( Pos > = 0 )
{
if ( StrBegin = = INDEX_NONE | | Pos < StrBegin | | Pos > StrEnd )
StrLine = StrLine . Left ( Pos ) ;
if ( StrLine = = TEXT ( " " ) )
continue ;
}
// look for a / * ... * / block, ignoring anything inside literal strings
Pos = StrLine . Find ( TEXT ( " /* " ) ) ;
EndPos = StrLine . Find ( TEXT ( " */ " ) ) ;
if ( Pos > = 0 )
{
if ( StrBegin = = INDEX_NONE | | Pos < StrBegin | | Pos > StrEnd )
{
if ( EndPos ! = INDEX_NONE & & ( EndPos < StrBegin | | EndPos > StrEnd ) )
{
StrLine = StrLine . Left ( Pos ) + StrLine . Mid ( EndPos + 2 ) ;
EndPos = INDEX_NONE ;
}
else
{
StrLine = StrLine . Left ( Pos ) ;
CommentDim + + ;
}
}
bProcess = CommentDim < = 1 ;
}
if ( EndPos > = 0 )
{
if ( StrBegin = = INDEX_NONE | | EndPos < StrBegin | | EndPos > StrEnd )
{
StrLine = StrLine . Mid ( EndPos + 2 ) ;
CommentDim - - ;
}
bProcess = CommentDim < = 0 ;
}
if ( ! bProcess | | StrLine = = TEXT ( " " ) )
continue ;
Str = * StrLine ;
// Get class or interface name
2015-01-20 09:33:54 -05:00
if ( const TCHAR * UInterfaceMacroDecl = FCString : : Strfind ( Str , TEXT ( " UINTERFACE( " ) ) )
2014-03-14 14:13:41 -04:00
{
2015-03-10 12:06:49 -04:00
Parser . ParseClassDeclaration ( StartOfLine + ( UInterfaceMacroDecl - Str ) , CurrentLine , TEXT ( " UINTERFACE " ) , /*out*/ ClassName , /*out*/ BaseClassName , /*out*/ DependentOn , OutParsedClassArray ) ;
2015-01-20 09:33:54 -05:00
OutParsedClassArray . Add ( FSimplifiedParsingClassInfo ( MoveTemp ( ClassName ) , MoveTemp ( BaseClassName ) , CurrentLine , true ) ) ;
}
2014-03-14 14:13:41 -04:00
2015-01-20 09:33:54 -05:00
if ( const TCHAR * UClassMacroDecl = FCString : : Strfind ( Str , TEXT ( " UCLASS( " ) ) )
{
2015-03-10 12:06:49 -04:00
Parser . ParseClassDeclaration ( StartOfLine + ( UClassMacroDecl - Str ) , CurrentLine , TEXT ( " UCLASS " ) , /*out*/ ClassName , /*out*/ BaseClassName , /*out*/ DependentOn , OutParsedClassArray ) ;
2015-01-20 09:33:54 -05:00
OutParsedClassArray . Add ( FSimplifiedParsingClassInfo ( MoveTemp ( ClassName ) , MoveTemp ( BaseClassName ) , CurrentLine , false ) ) ;
2014-03-14 14:13:41 -04:00
}
}
StartOfLine = Buffer ;
}
}
/////////////////////////////////////////////////////
// FHeaderPreParser
2015-03-10 12:06:49 -04:00
void FHeaderPreParser : : ParseClassDeclaration ( const TCHAR * InputText , int32 InLineNumber , const TCHAR * StartingMatchID , FString & out_ClassName , FString & out_BaseClassName , TArray < FHeaderProvider > & out_RequiredIncludes , const TArray < FSimplifiedParsingClassInfo > & ParsedClassArray )
2014-03-14 14:13:41 -04:00
{
FString ErrorMsg = TEXT ( " Class declaration " ) ;
ResetParser ( InputText , InLineNumber ) ;
// Require 'UCLASS' or 'UINTERFACE'
RequireIdentifier ( StartingMatchID , * ErrorMsg ) ;
// New-style UCLASS() syntax
TMap < FName , FString > MetaData ;
TArray < FPropertySpecifier > SpecifiersFound ;
ReadSpecifierSetInsideMacro ( SpecifiersFound , ErrorMsg , MetaData ) ;
// Require 'class'
RequireIdentifier ( TEXT ( " class " ) , * ErrorMsg ) ;
// Read the class name
FString RequiredAPIMacroIfPresent ;
ParseNameWithPotentialAPIMacroPrefix ( /*out*/ out_ClassName , /*out*/ RequiredAPIMacroIfPresent , StartingMatchID ) ;
2015-03-10 06:14:19 -04:00
FName ClassNameWithoutPrefix ( * GetClassNameWithPrefixRemoved ( out_ClassName ) ) ;
auto DeclarationDataPtr = GClassDeclarations . Find ( ClassNameWithoutPrefix ) ;
if ( ! DeclarationDataPtr )
{
2015-03-10 06:24:24 -04:00
// Add class declaration meta data so that we can access class flags before the class is fully parsed
2015-03-10 06:14:19 -04:00
TSharedRef < FClassDeclarationMetaData > DeclarationData = MakeShareable ( new FClassDeclarationMetaData ( ) ) ;
DeclarationData - > MetaData = MetaData ;
2015-04-21 05:03:44 -04:00
DeclarationData - > ParseClassProperties ( SpecifiersFound , RequiredAPIMacroIfPresent ) ;
2015-03-10 06:14:19 -04:00
GClassDeclarations . Add ( ClassNameWithoutPrefix , DeclarationData ) ;
}
2014-03-14 14:13:41 -04:00
// Handle inheritance
if ( MatchSymbol ( TEXT ( " : " ) ) )
{
// Require 'public'
RequireIdentifier ( TEXT ( " public " ) , * ErrorMsg ) ;
// Inherits from something
FToken BaseClassNameToken ;
if ( ! GetIdentifier ( BaseClassNameToken , true ) )
{
FError : : Throwf ( TEXT ( " Expected a base class name " ) ) ;
}
out_BaseClassName = BaseClassNameToken . Identifier ;
2015-03-10 12:06:49 -04:00
AddDependencyIfNeeded ( ParsedClassArray , out_BaseClassName , out_RequiredIncludes ) ;
2014-03-14 14:13:41 -04:00
// Get additional inheritance links and rack them up as dependencies if they're UObject derived
while ( MatchSymbol ( TEXT ( " , " ) ) )
{
// Require 'public'
RequireIdentifier ( TEXT ( " public " ) , * ErrorMsg ) ;
FToken InterfaceClassNameToken ;
if ( ! GetIdentifier ( InterfaceClassNameToken , true ) )
{
FError : : Throwf ( TEXT ( " Expected an interface class name " ) ) ;
}
2015-03-10 12:06:49 -04:00
AddDependencyIfNeeded ( ParsedClassArray , FString ( InterfaceClassNameToken . Identifier ) , out_RequiredIncludes ) ;
2014-03-14 14:13:41 -04:00
}
}
}
2015-03-10 12:06:49 -04:00
void FHeaderPreParser : : AddDependencyIfNeeded ( const TArray < FSimplifiedParsingClassInfo > & ParsedClassArray , const FString & DependencyClassName , TArray < FHeaderProvider > & RequiredIncludes ) const
{
if ( ParsedClassArray . FindByPredicate ( [ & DependencyClassName ] ( const FSimplifiedParsingClassInfo & Info )
{
return Info . GetClassName ( ) = = DependencyClassName ;
} ) = = nullptr )
{
RequiredIncludes . Add ( FHeaderProvider ( EHeaderProviderSourceType : : ClassName , DependencyClassName . Mid ( 1 ) ) ) ;
}
}
2014-03-14 14:13:41 -04:00
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool FHeaderParser : : DefaultValueStringCppFormatToInnerFormat ( const UProperty * Property , const FString & CppForm , FString & OutForm )
{
OutForm = FString ( ) ;
if ( ! Property | | CppForm . IsEmpty ( ) )
2015-02-16 04:40:38 -05:00
{
2014-03-14 14:13:41 -04:00
return false ;
2015-02-16 04:40:38 -05:00
}
2014-03-14 14:13:41 -04:00
if ( Property - > IsA ( UClassProperty : : StaticClass ( ) ) | | Property - > IsA ( UObjectPropertyBase : : StaticClass ( ) ) )
2015-02-16 04:40:38 -05:00
{
2014-03-14 14:13:41 -04:00
return FDefaultValueHelper : : Is ( CppForm , TEXT ( " NULL " ) ) | | FDefaultValueHelper : : Is ( CppForm , TEXT ( " nullptr " ) ) | | FDefaultValueHelper : : Is ( CppForm , TEXT ( " 0 " ) ) ;
2015-02-16 04:40:38 -05:00
}
2014-03-14 14:13:41 -04:00
if ( ! Property - > IsA ( UStructProperty : : StaticClass ( ) ) )
{
if ( Property - > IsA ( UIntProperty : : StaticClass ( ) ) )
{
int32 Value ;
if ( FDefaultValueHelper : : ParseInt ( CppForm , Value ) )
{
OutForm = FString : : FromInt ( Value ) ;
}
}
else if ( Property - > IsA ( UByteProperty : : StaticClass ( ) ) )
{
const UEnum * Enum = CastChecked < UByteProperty > ( Property ) - > Enum ;
if ( NULL ! = Enum )
{
2014-07-31 03:32:31 -04:00
OutForm = FDefaultValueHelper : : GetUnqualifiedEnumValue ( FDefaultValueHelper : : RemoveWhitespaces ( CppForm ) ) ;
2014-03-14 14:13:41 -04:00
return ( INDEX_NONE ! = Enum - > FindEnumIndex ( * OutForm ) ) ;
}
int32 Value ;
if ( FDefaultValueHelper : : ParseInt ( CppForm , Value ) )
{
OutForm = FString : : FromInt ( Value ) ;
return ( 0 < = Value ) & & ( 255 > = Value ) ;
}
}
else if ( Property - > IsA ( UFloatProperty : : StaticClass ( ) ) )
{
float Value ;
if ( FDefaultValueHelper : : ParseFloat ( CppForm , Value ) )
{
OutForm = FString : : Printf ( TEXT ( " %f " ) , Value ) ;
}
}
else if ( Property - > IsA ( UDoubleProperty : : StaticClass ( ) ) )
{
double Value ;
if ( FDefaultValueHelper : : ParseDouble ( CppForm , Value ) )
{
OutForm = FString : : Printf ( TEXT ( " %f " ) , Value ) ;
}
}
else if ( Property - > IsA ( UBoolProperty : : StaticClass ( ) ) )
{
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " true " ) ) | |
FDefaultValueHelper : : Is ( CppForm , TEXT ( " false " ) ) )
{
OutForm = FDefaultValueHelper : : RemoveWhitespaces ( CppForm ) ;
}
}
else if ( Property - > IsA ( UNameProperty : : StaticClass ( ) ) )
{
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " NAME_None " ) ) )
{
OutForm = TEXT ( " None " ) ;
return true ;
}
return FDefaultValueHelper : : StringFromCppString ( CppForm , TEXT ( " FName " ) , OutForm ) ;
}
else if ( Property - > IsA ( UTextProperty : : StaticClass ( ) ) )
{
return FDefaultValueHelper : : StringFromCppString ( CppForm , TEXT ( " FText " ) , OutForm ) ;
}
else if ( Property - > IsA ( UStrProperty : : StaticClass ( ) ) )
{
return FDefaultValueHelper : : StringFromCppString ( CppForm , TEXT ( " FString " ) , OutForm ) ;
}
}
else
{
// Cache off the struct types, in case we need them later
2015-01-20 09:33:54 -05:00
UPackage * CoreUObjectPackage = UObject : : StaticClass ( ) - > GetOutermost ( ) ;
static const UScriptStruct * VectorStruct = FindObjectChecked < UScriptStruct > ( CoreUObjectPackage , TEXT ( " Vector " ) ) ;
static const UScriptStruct * Vector2DStruct = FindObjectChecked < UScriptStruct > ( CoreUObjectPackage , TEXT ( " Vector2D " ) ) ;
static const UScriptStruct * RotatorStruct = FindObjectChecked < UScriptStruct > ( CoreUObjectPackage , TEXT ( " Rotator " ) ) ;
static const UScriptStruct * LinearColorStruct = FindObjectChecked < UScriptStruct > ( CoreUObjectPackage , TEXT ( " LinearColor " ) ) ;
2015-04-16 18:43:00 -04:00
static const UScriptStruct * ColorStruct = FindObjectChecked < UScriptStruct > ( CoreUObjectPackage , TEXT ( " Color " ) ) ;
2014-03-14 14:13:41 -04:00
const UStructProperty * StructProperty = CastChecked < UStructProperty > ( Property ) ;
if ( StructProperty - > Struct = = VectorStruct )
{
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FVector::ZeroVector " ) ) )
{
return true ;
}
2014-05-09 07:41:26 -04:00
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FVector::UpVector " ) ) )
{
2014-05-16 05:01:25 -04:00
OutForm = FString : : Printf ( TEXT ( " %f,%f,%f " ) ,
2014-05-09 07:41:26 -04:00
FVector : : UpVector . X , FVector : : UpVector . Y , FVector : : UpVector . Z ) ;
}
2014-03-14 14:13:41 -04:00
FString Parameters ;
if ( FDefaultValueHelper : : GetParameters ( CppForm , TEXT ( " FVector " ) , Parameters ) )
{
if ( FDefaultValueHelper : : Is ( Parameters , TEXT ( " ForceInit " ) ) )
{
return true ;
}
FVector Vector ;
if ( FDefaultValueHelper : : ParseVector ( Parameters , Vector ) )
{
2014-05-16 05:01:25 -04:00
OutForm = FString : : Printf ( TEXT ( " %f,%f,%f " ) ,
2014-05-09 07:41:26 -04:00
Vector . X , Vector . Y , Vector . Z ) ;
2014-03-14 14:13:41 -04:00
}
}
}
else if ( StructProperty - > Struct = = RotatorStruct )
{
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FRotator::ZeroRotator " ) ) )
{
return true ;
}
FString Parameters ;
if ( FDefaultValueHelper : : GetParameters ( CppForm , TEXT ( " FRotator " ) , Parameters ) )
{
if ( FDefaultValueHelper : : Is ( Parameters , TEXT ( " ForceInit " ) ) )
{
return true ;
}
FRotator Rotator ;
if ( FDefaultValueHelper : : ParseRotator ( Parameters , Rotator ) )
{
2014-05-16 05:01:25 -04:00
OutForm = FString : : Printf ( TEXT ( " %f,%f,%f " ) ,
2014-05-09 07:41:26 -04:00
Rotator . Pitch , Rotator . Yaw , Rotator . Roll ) ;
2014-03-14 14:13:41 -04:00
}
}
}
else if ( StructProperty - > Struct = = Vector2DStruct )
{
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FVector2D::ZeroVector " ) ) )
{
return true ;
}
2014-05-09 07:41:26 -04:00
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FVector2D::UnitVector " ) ) )
{
OutForm = FString : : Printf ( TEXT ( " (X=%3.3f,Y=%3.3f) " ) ,
FVector2D : : UnitVector . X , FVector2D : : UnitVector . Y ) ;
}
2014-03-14 14:13:41 -04:00
FString Parameters ;
if ( FDefaultValueHelper : : GetParameters ( CppForm , TEXT ( " FVector2D " ) , Parameters ) )
{
if ( FDefaultValueHelper : : Is ( Parameters , TEXT ( " ForceInit " ) ) )
{
return true ;
}
FVector2D Vector2D ;
if ( FDefaultValueHelper : : ParseVector2D ( Parameters , Vector2D ) )
{
2014-05-09 07:41:26 -04:00
OutForm = FString : : Printf ( TEXT ( " (X=%3.3f,Y=%3.3f) " ) ,
Vector2D . X , Vector2D . Y ) ;
2014-03-14 14:13:41 -04:00
}
}
}
else if ( StructProperty - > Struct = = LinearColorStruct )
{
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::White " ) ) )
{
OutForm = FLinearColor : : White . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::Gray " ) ) )
{
OutForm = FLinearColor : : Gray . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::Black " ) ) )
{
OutForm = FLinearColor : : Black . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::Transparent " ) ) )
{
OutForm = FLinearColor : : Transparent . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::Red " ) ) )
{
OutForm = FLinearColor : : Red . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::Green " ) ) )
{
OutForm = FLinearColor : : Green . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::Blue " ) ) )
{
OutForm = FLinearColor : : Blue . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FLinearColor::Yellow " ) ) )
{
OutForm = FLinearColor : : Yellow . ToString ( ) ;
}
else
{
FString Parameters ;
if ( FDefaultValueHelper : : GetParameters ( CppForm , TEXT ( " FLinearColor " ) , Parameters ) )
{
if ( FDefaultValueHelper : : Is ( Parameters , TEXT ( " ForceInit " ) ) )
{
return true ;
}
FLinearColor Color ;
if ( FDefaultValueHelper : : ParseLinearColor ( Parameters , Color ) )
{
OutForm = Color . ToString ( ) ;
}
}
}
}
2015-04-16 18:43:00 -04:00
else if ( StructProperty - > Struct = = ColorStruct )
{
if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::White " ) ) )
{
OutForm = FColor : : White . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::Black " ) ) )
{
OutForm = FColor : : Black . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::Red " ) ) )
{
OutForm = FColor : : Red . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::Green " ) ) )
{
OutForm = FColor : : Green . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::Blue " ) ) )
{
OutForm = FColor : : Blue . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::Yellow " ) ) )
{
OutForm = FColor : : Yellow . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::Cyan " ) ) )
{
OutForm = FColor : : Cyan . ToString ( ) ;
}
else if ( FDefaultValueHelper : : Is ( CppForm , TEXT ( " FColor::Magenta " ) ) )
{
OutForm = FColor : : Magenta . ToString ( ) ;
}
else
{
FString Parameters ;
if ( FDefaultValueHelper : : GetParameters ( CppForm , TEXT ( " FColor " ) , Parameters ) )
{
if ( FDefaultValueHelper : : Is ( Parameters , TEXT ( " ForceInit " ) ) )
{
return true ;
}
FColor Color ;
if ( FDefaultValueHelper : : ParseColor ( Parameters , Color ) )
{
OutForm = Color . ToString ( ) ;
}
}
}
}
2014-03-14 14:13:41 -04:00
}
return ! OutForm . IsEmpty ( ) ;
}
2014-10-10 04:34:56 -04:00
bool FHeaderParser : : TryToMatchConstructorParameterList ( FToken Token )
{
FToken PotentialParenthesisToken ;
if ( ! GetToken ( PotentialParenthesisToken ) )
{
return false ;
}
if ( ! PotentialParenthesisToken . Matches ( TEXT ( " ( " ) ) )
{
UngetToken ( PotentialParenthesisToken ) ;
return false ;
}
2015-01-20 09:33:54 -05:00
auto * ClassData = GScriptHelper . FindClassData ( GetCurrentClass ( ) ) ;
2014-10-10 04:34:56 -04:00
2015-03-02 06:44:04 -05:00
bool bOICtor = false ;
# if WITH_HOT_RELOAD_CTORS
bool bVTCtor = false ;
# endif // WITH_HOT_RELOAD_CTORS
2014-10-10 04:34:56 -04:00
if ( ! ClassData - > bDefaultConstructorDeclared & & MatchSymbol ( TEXT ( " ) " ) ) )
{
ClassData - > bDefaultConstructorDeclared = true ;
}
2015-03-02 06:44:04 -05:00
else if ( ! ClassData - > bObjectInitializerConstructorDeclared
# if WITH_HOT_RELOAD_CTORS
| | ! ClassData - > bCustomVTableHelperConstructorDeclared
# endif // WITH_HOT_RELOAD_CTORS
)
2014-10-10 04:34:56 -04:00
{
2014-10-14 10:29:11 -04:00
FToken ObjectInitializerParamParsingToken ;
2014-10-10 04:34:56 -04:00
bool bIsConst = false ;
bool bIsRef = false ;
int32 ParenthesesNestingLevel = 1 ;
2014-10-14 10:29:11 -04:00
while ( ParenthesesNestingLevel & & GetToken ( ObjectInitializerParamParsingToken ) )
2014-10-10 04:34:56 -04:00
{
2014-10-14 10:29:11 -04:00
// Template instantiation or additional parameter excludes ObjectInitializer constructor.
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " , " ) ) | | ObjectInitializerParamParsingToken . Matches ( TEXT ( " < " ) ) )
2014-10-10 04:34:56 -04:00
{
2015-03-02 06:44:04 -05:00
bOICtor = false ;
# if WITH_HOT_RELOAD_CTORS
bVTCtor = false ;
# endif // WITH_HOT_RELOAD_CTORS
2014-10-10 04:34:56 -04:00
break ;
}
2014-10-14 10:29:11 -04:00
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " ( " ) ) )
2014-10-10 04:34:56 -04:00
{
ParenthesesNestingLevel + + ;
continue ;
}
2014-10-14 10:29:11 -04:00
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " ) " ) ) )
2014-10-10 04:34:56 -04:00
{
ParenthesesNestingLevel - - ;
continue ;
}
2014-10-14 10:29:11 -04:00
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " const " ) ) )
2014-10-10 04:34:56 -04:00
{
bIsConst = true ;
continue ;
}
2014-10-14 10:29:11 -04:00
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " & " ) ) )
2014-10-10 04:34:56 -04:00
{
bIsRef = true ;
continue ;
}
2014-10-14 10:29:11 -04:00
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " FObjectInitializer " ) )
| | ObjectInitializerParamParsingToken . Matches ( TEXT ( " FPostConstructInitializeProperties " ) ) // Deprecated, but left here, so it won't break legacy code.
)
2014-10-10 04:34:56 -04:00
{
2015-03-02 06:44:04 -05:00
bOICtor = true ;
2014-10-10 04:34:56 -04:00
}
2015-03-02 06:44:04 -05:00
# if WITH_HOT_RELOAD_CTORS
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " FVTableHelper " ) ) )
{
bVTCtor = true ;
}
# endif // WITH_HOT_RELOAD_CTORS
2014-10-10 04:34:56 -04:00
}
// Parse until finish.
2014-10-14 10:29:11 -04:00
while ( ParenthesesNestingLevel & & GetToken ( ObjectInitializerParamParsingToken ) )
2014-10-10 04:34:56 -04:00
{
2014-10-14 10:29:11 -04:00
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " ( " ) ) )
2014-10-10 04:34:56 -04:00
{
ParenthesesNestingLevel + + ;
continue ;
}
2014-10-14 10:29:11 -04:00
if ( ObjectInitializerParamParsingToken . Matches ( TEXT ( " ) " ) ) )
2014-10-10 04:34:56 -04:00
{
ParenthesesNestingLevel - - ;
continue ;
}
}
2015-03-02 06:44:04 -05:00
ClassData - > bObjectInitializerConstructorDeclared = ClassData - > bObjectInitializerConstructorDeclared | | ( bOICtor & & bIsRef & & bIsConst ) ;
# if WITH_HOT_RELOAD_CTORS
ClassData - > bCustomVTableHelperConstructorDeclared = ClassData - > bCustomVTableHelperConstructorDeclared | | ( bVTCtor & & bIsRef ) ;
# endif // WITH_HOT_RELOAD_CTORS
2014-10-10 04:34:56 -04:00
}
2015-03-02 06:44:04 -05:00
ClassData - > bConstructorDeclared =
# if WITH_HOT_RELOAD_CTORS
ClassData - > bConstructorDeclared | | ! bVTCtor ;
# else // WITH_HOT_RELOAD_CTORS
true ;
# endif // WITH_HOT_RELOAD_CTORS
2014-10-10 04:34:56 -04:00
// Optionally match semicolon.
if ( ! MatchSymbol ( TEXT ( " ; " ) ) )
{
// If not matched a semicolon, this is inline constructor definition. We have to skip it.
UngetToken ( Token ) ; // Resets input stream to the initial token.
GetToken ( Token ) ; // Re-gets the initial token to start constructor definition skip.
return SkipDeclaration ( Token ) ;
}
return true ;
}
2014-10-24 02:59:18 -04:00
void FHeaderParser : : SkipDeprecatedMacroIfNecessary ( )
{
if ( ! MatchIdentifier ( TEXT ( " DEPRECATED " ) ) )
{
return ;
}
FToken Token ;
// DEPRECATED(Version, "Message")
RequireSymbol ( TEXT ( " ( " ) , TEXT ( " DEPRECATED macro " ) ) ;
if ( GetToken ( Token ) & & ( Token . Type ! = CPT_Float | | Token . TokenType ! = TOKEN_Const ) )
{
FError : : Throwf ( TEXT ( " Expected engine version in DEPRECATED macro " ) ) ;
}
RequireSymbol ( TEXT ( " , " ) , TEXT ( " DEPRECATED macro " ) ) ;
if ( GetToken ( Token ) & & ( Token . Type ! = CPT_String | | Token . TokenType ! = TOKEN_Const ) )
{
FError : : Throwf ( TEXT ( " Expected deprecation message in DEPRECATED macro " ) ) ;
}
RequireSymbol ( TEXT ( " ) " ) , TEXT ( " DEPRECATED macro " ) ) ;
}
2015-01-20 09:33:54 -05:00
2015-04-10 06:02:22 -04:00
void FHeaderParser : : CompileVersionDeclaration ( FUnrealSourceFile & SourceFile , UStruct * Struct )
{
// Do nothing if we're at the end of file.
FToken Token ;
if ( ! GetToken ( Token , true , ESymbolParseOption : : Normal ) )
{
return ;
}
// Default version based on config file.
auto Version = DefaultGeneratedCodeVersion ;
// Overwrite with module-specific value if one was specified.
if ( CurrentlyParsedModule - > GeneratedCodeVersion ! = EGeneratedCodeVersion : : None )
{
Version = CurrentlyParsedModule - > GeneratedCodeVersion ;
}
if ( Token . TokenType = = ETokenType : : TOKEN_Symbol
& & ! FCString : : Stricmp ( Token . Identifier , TEXT ( " ) " ) ) )
{
SourceFile . GetGeneratedCodeVersions ( ) . FindOrAdd ( Struct ) = Version ;
UngetToken ( Token ) ;
return ;
}
// Overwrite with version specified by macro.
Version = ToGeneratedCodeVersion ( Token . Identifier ) ;
SourceFile . GetGeneratedCodeVersions ( ) . FindOrAdd ( Struct ) = Version ;
}
2015-01-20 09:33:54 -05:00
void FHeaderParser : : ResetClassData ( )
{
UClass * CurrentClass = GetCurrentClass ( ) ;
CurrentClass - > PropertiesSize = 0 ;
// Set class flags and within.
CurrentClass - > ClassFlags & = ~ CLASS_RecompilerClear ;
UClass * SuperClass = CurrentClass - > GetSuperClass ( ) ;
if ( SuperClass ! = NULL )
{
CurrentClass - > ClassFlags | = ( SuperClass - > ClassFlags ) & CLASS_ScriptInherit ;
CurrentClass - > ClassConfigName = SuperClass - > ClassConfigName ;
check ( SuperClass - > ClassWithin ) ;
if ( CurrentClass - > ClassWithin = = NULL )
{
CurrentClass - > ClassWithin = SuperClass - > ClassWithin ;
}
// Copy special categories from parent
if ( SuperClass - > HasMetaData ( TEXT ( " HideCategories " ) ) )
{
CurrentClass - > SetMetaData ( TEXT ( " HideCategories " ) , * SuperClass - > GetMetaData ( " HideCategories " ) ) ;
}
if ( SuperClass - > HasMetaData ( TEXT ( " ShowCategories " ) ) )
{
CurrentClass - > SetMetaData ( TEXT ( " ShowCategories " ) , * SuperClass - > GetMetaData ( " ShowCategories " ) ) ;
}
if ( SuperClass - > HasMetaData ( TEXT ( " HideFunctions " ) ) )
{
CurrentClass - > SetMetaData ( TEXT ( " HideFunctions " ) , * SuperClass - > GetMetaData ( " HideFunctions " ) ) ;
}
if ( SuperClass - > HasMetaData ( TEXT ( " AutoExpandCategories " ) ) )
{
CurrentClass - > SetMetaData ( TEXT ( " AutoExpandCategories " ) , * SuperClass - > GetMetaData ( " AutoExpandCategories " ) ) ;
}
if ( SuperClass - > HasMetaData ( TEXT ( " AutoCollapseCategories " ) ) )
{
CurrentClass - > SetMetaData ( TEXT ( " AutoCollapseCategories " ) , * SuperClass - > GetMetaData ( " AutoCollapseCategories " ) ) ;
}
}
check ( CurrentClass - > ClassWithin ) ;
}
void FHeaderParser : : PostPopNestClass ( UClass * CurrentClass )
{
// Validate all the rep notify events here, to make sure they're implemented
VerifyRepNotifyCallbacks ( CurrentClass ) ;
// Iterate over all the interfaces we claim to implement
for ( auto & Impl : CurrentClass - > Interfaces )
{
// And their super-classes
for ( UClass * Interface = Impl . Class ; Interface ; Interface = Interface - > GetSuperClass ( ) )
{
// If this interface is a common ancestor, skip it
if ( CurrentClass - > IsChildOf ( Interface ) )
continue ;
// So iterate over all functions this interface declares
for ( auto InterfaceFunction : TFieldRange < UFunction > ( Interface , EFieldIteratorFlags : : ExcludeSuper ) )
{
bool Implemented = false ;
// And try to find one that matches
for ( UFunction * ClassFunction : TFieldRange < UFunction > ( CurrentClass ) )
{
if ( ClassFunction - > GetFName ( ) ! = InterfaceFunction - > GetFName ( ) )
continue ;
if ( ( InterfaceFunction - > FunctionFlags & FUNC_Event ) & & ! ( ClassFunction - > FunctionFlags & FUNC_Event ) )
FError : : Throwf ( TEXT ( " Implementation of function '%s' must be declared as 'event' to match declaration in interface '%s' " ) , * ClassFunction - > GetName ( ) , * Interface - > GetName ( ) ) ;
if ( ( InterfaceFunction - > FunctionFlags & FUNC_Delegate ) & & ! ( ClassFunction - > FunctionFlags & FUNC_Delegate ) )
FError : : Throwf ( TEXT ( " Implementation of function '%s' must be declared as 'delegate' to match declaration in interface '%s' " ) , * ClassFunction - > GetName ( ) , * Interface - > GetName ( ) ) ;
// Making sure all the parameters match up correctly
Implemented = true ;
if ( ClassFunction - > NumParms ! = InterfaceFunction - > NumParms )
FError : : Throwf ( TEXT ( " Implementation of function '%s' conflicts with interface '%s' - different number of parameters (%i/%i) " ) , * InterfaceFunction - > GetName ( ) , * Interface - > GetName ( ) , ClassFunction - > NumParms , InterfaceFunction - > NumParms ) ;
int32 Count = 0 ;
for ( TFieldIterator < UProperty > It1 ( InterfaceFunction ) , It2 ( ClassFunction ) ; Count < ClassFunction - > NumParms ; + + It1 , + + It2 , Count + + )
{
if ( ! FPropertyBase ( * It1 ) . MatchesType ( FPropertyBase ( * It2 ) , 1 ) )
{
if ( It1 - > PropertyFlags & CPF_ReturnParm )
{
FError : : Throwf ( TEXT ( " Implementation of function '%s' conflicts only by return type with interface '%s' " ) , * InterfaceFunction - > GetName ( ) , * Interface - > GetName ( ) ) ;
}
else
{
FError : : Throwf ( TEXT ( " Implementation of function '%s' conflicts with interface '%s' - parameter %i '%s' " ) , * InterfaceFunction - > GetName ( ) , * Interface - > GetName ( ) , Count , * It1 - > GetName ( ) ) ;
}
}
}
}
// Delegate signature functions are simple stubs and aren't required to be implemented (they are not callable)
if ( InterfaceFunction - > FunctionFlags & FUNC_Delegate )
{
Implemented = true ;
}
// Verify that if this has blueprint-callable functions that are not implementable events, we've implemented them as a UFunction in the target class
if ( ! Implemented
& & ! Interface - > HasMetaData ( TEXT ( " CannotImplementInterfaceInBlueprint " ) ) // FBlueprintMetadata::MD_CannotImplementInterfaceInBlueprint
& & InterfaceFunction - > HasAnyFunctionFlags ( FUNC_BlueprintCallable )
& & ! InterfaceFunction - > HasAnyFunctionFlags ( FUNC_BlueprintEvent ) )
{
FError : : Throwf ( TEXT ( " Missing UFunction implementation of function '%s' from interface '%s'. This function needs a UFUNCTION() declaration. " ) , * InterfaceFunction - > GetName ( ) , * Interface - > GetName ( ) ) ;
}
}
}
}
}
void FHeaderParser : : PostPopFunctionDeclaration ( FClasses & AllClasses , UFunction * PoppedFunction )
{
//@TODO: UCREMOVAL: Move this code to occur at delegate var declaration, and force delegates to be declared before variables that use them
if ( ! GetCurrentScope ( ) - > IsFileScope ( ) & & GetCurrentClassData ( ) - > ContainsDelegates ( ) )
{
// now validate all delegate variables declared in the class
TMap < FName , UFunction * > DelegateCache ;
FixupDelegateProperties ( AllClasses , PoppedFunction , * GetCurrentScope ( ) , DelegateCache ) ;
}
}
void FHeaderParser : : PostPopNestInterface ( FClasses & AllClasses , UClass * CurrentInterface )
{
if ( GScriptHelper . FindClassData ( CurrentInterface ) - > ContainsDelegates ( ) )
{
TMap < FName , UFunction * > DelegateCache ;
FixupDelegateProperties ( AllClasses , CurrentInterface , FScope : : GetTypeScope ( ExactCast < UClass > ( CurrentInterface ) ) . Get ( ) , DelegateCache ) ;
}
}
template < class TFunctionType >
TFunctionType * CreateFunctionImpl ( const FFuncInfo & FuncInfo , UObject * Outer , FScope * CurrentScope )
{
// Allocate local property frame, push nesting level and verify
// uniqueness at this scope level.
{
auto TypeIterator = CurrentScope - > GetTypeIterator ( ) ;
while ( TypeIterator . MoveNext ( ) )
{
UField * Type = * TypeIterator ;
if ( Type - > GetFName ( ) = = FuncInfo . Function . Identifier )
{
FError : : Throwf ( TEXT ( " '%s' conflicts with '%s' " ) , FuncInfo . Function . Identifier , * Type - > GetFullName ( ) ) ;
}
}
}
2015-01-30 08:30:03 -05:00
TFunctionType * Function = new ( EC_InternalUseOnlyConstructor , Outer , FuncInfo . Function . Identifier , RF_Public ) TFunctionType ( FObjectInitializer ( ) , nullptr ) ;
2015-01-20 09:33:54 -05:00
Function - > RepOffset = MAX_uint16 ;
Function - > ReturnValueOffset = MAX_uint16 ;
Function - > FirstPropertyToInit = nullptr ;
2015-03-12 14:06:13 -04:00
if ( ! CurrentScope - > IsFileScope ( ) )
{
auto * Struct = ( ( FStructScope * ) CurrentScope ) - > GetStruct ( ) ;
Function - > Next = Struct - > Children ;
Struct - > Children = Function ;
}
2015-01-20 09:33:54 -05:00
return Function ;
}
UFunction * FHeaderParser : : CreateFunction ( const FFuncInfo & FuncInfo ) const
{
2015-03-12 14:06:13 -04:00
return CreateFunctionImpl < UFunction > ( FuncInfo , GetCurrentClass ( ) , GetCurrentScope ( ) ) ;
2015-01-20 09:33:54 -05:00
}
UDelegateFunction * FHeaderParser : : CreateDelegateFunction ( const FFuncInfo & FuncInfo ) const
{
return CreateFunctionImpl < UDelegateFunction > ( FuncInfo , IsInAClass ( ) ? ( UObject * ) GetCurrentClass ( ) : ( UObject * ) GetCurrentFileScope ( ) - > GetSourceFile ( ) - > GetPackage ( ) , GetCurrentScope ( ) ) ;
2015-03-10 06:14:19 -04:00
}