diff --git a/Engine/Source/Programs/UHTLite/Private/BaseParser.cpp b/Engine/Source/Programs/UHTLite/Private/BaseParser.cpp deleted file mode 100644 index 976a0e654e02..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/BaseParser.cpp +++ /dev/null @@ -1,1236 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "BaseParser.h" -#include "UnrealHeaderTool.h" -#include "UObject/NameTypes.h" -#include "UObject/ErrorException.h" - -#include "ParserHelper.h" - - -namespace -{ - namespace EMetadataValueArgument - { - enum Type - { - None, - Required, - Optional - }; - } - - namespace EMetadataValueAction - { - enum Type - { - Remove, - Add - }; - } - - struct FMetadataValueAction - { - FMetadataValueAction(const TCHAR* InMapping, const TCHAR* InDefaultValue, EMetadataValueAction::Type InValueAction) - : Mapping (InMapping) - , DefaultValue (InDefaultValue) - , ValueAction (InValueAction) - { - } - - FName Mapping; - FString DefaultValue; - EMetadataValueAction::Type ValueAction; - }; - - struct FMetadataKeyword - { - FMetadataKeyword(EMetadataValueArgument::Type InValueArgument) - : ValueArgument(InValueArgument) - { - } - - void InsertAddAction(const TCHAR* InMapping, const TCHAR* InDefaultValue) - { - ValueActions.Add(FMetadataValueAction(InMapping, InDefaultValue, EMetadataValueAction::Add)); - } - - void InsertRemoveAction(const TCHAR* InMapping) - { - ValueActions.Add(FMetadataValueAction(InMapping, TEXT(""), EMetadataValueAction::Remove)); - } - - void ApplyToMetadata(TMap& Metadata, const FString* Value = nullptr) - { - for (const FMetadataValueAction& ValueAction : ValueActions) - { - if (ValueAction.ValueAction == EMetadataValueAction::Add) - { - FBaseParser::InsertMetaDataPair(Metadata, ValueAction.Mapping, Value ? *Value : ValueAction.DefaultValue); - } - else - { - Metadata.Remove(ValueAction.Mapping); - } - } - } - - TArray ValueActions; - EMetadataValueArgument::Type ValueArgument; - }; - - FMetadataKeyword* GetMetadataKeyword(const TCHAR* Keyword) - { - static TMap Dictionary; - if (!Dictionary.Num()) - { - FMetadataKeyword& DisplayName = Dictionary.Add(TEXT("DisplayName"), EMetadataValueArgument::Required); - DisplayName.InsertAddAction(TEXT("DisplayName"), TEXT("")); - - FMetadataKeyword& FriendlyName = Dictionary.Add(TEXT("FriendlyName"), EMetadataValueArgument::Required); - FriendlyName.InsertAddAction(TEXT("FriendlyName"), TEXT("")); - - FMetadataKeyword& BlueprintInternalUseOnly = Dictionary.Add(TEXT("BlueprintInternalUseOnly"), EMetadataValueArgument::None); - BlueprintInternalUseOnly.InsertAddAction(TEXT("BlueprintInternalUseOnly"), TEXT("true")); - BlueprintInternalUseOnly.InsertAddAction(TEXT("BlueprintType"), TEXT("true")); - - FMetadataKeyword& BlueprintType = Dictionary.Add(TEXT("BlueprintType"), EMetadataValueArgument::None); - BlueprintType.InsertAddAction(TEXT("BlueprintType"), TEXT("true")); - - FMetadataKeyword& NotBlueprintType = Dictionary.Add(TEXT("NotBlueprintType"), EMetadataValueArgument::None); - NotBlueprintType.InsertAddAction(TEXT("NotBlueprintType"), TEXT("true")); - NotBlueprintType.InsertRemoveAction(TEXT("BlueprintType")); - - FMetadataKeyword& Blueprintable = Dictionary.Add(TEXT("Blueprintable"), EMetadataValueArgument::None); - Blueprintable.InsertAddAction(TEXT("IsBlueprintBase"), TEXT("true")); - Blueprintable.InsertAddAction(TEXT("BlueprintType"), TEXT("true")); - - FMetadataKeyword& CallInEditor = Dictionary.Add(TEXT("CallInEditor"), EMetadataValueArgument::None); - CallInEditor.InsertAddAction(TEXT("CallInEditor"), TEXT("true")); - - FMetadataKeyword& NotBlueprintable = Dictionary.Add(TEXT("NotBlueprintable"), EMetadataValueArgument::None); - NotBlueprintable.InsertAddAction (TEXT("IsBlueprintBase"), TEXT("false")); - NotBlueprintable.InsertRemoveAction(TEXT("BlueprintType")); - - FMetadataKeyword& Category = Dictionary.Add(TEXT("Category"), EMetadataValueArgument::Required); - Category.InsertAddAction(TEXT("Category"), TEXT("")); - - FMetadataKeyword& ExperimentalFeature = Dictionary.Add(TEXT("Experimental"), EMetadataValueArgument::None); - ExperimentalFeature.InsertAddAction(TEXT("DevelopmentStatus"), TEXT("Experimental")); - - FMetadataKeyword& EarlyAccessFeature = Dictionary.Add(TEXT("EarlyAccessPreview"), EMetadataValueArgument::None); - EarlyAccessFeature.InsertAddAction(TEXT("DevelopmentStatus"), TEXT("EarlyAccess")); - - FMetadataKeyword& DocumentationPolicy = Dictionary.Add(TEXT("DocumentationPolicy"), EMetadataValueArgument::None); - DocumentationPolicy.InsertAddAction(TEXT("DocumentationPolicy"), TEXT("Strict")); - - FMetadataKeyword& SparseClassDataType = Dictionary.Add(TEXT("SparseClassDataType"), EMetadataValueArgument::Required); - SparseClassDataType.InsertAddAction(TEXT("SparseClassDataType"), TEXT("")); - } - - return Dictionary.Find(Keyword); - } -} - -////////////////////////////////////////////////////////////////////////// -// FPropertySpecifier - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -FString FPropertySpecifier::ConvertToString() const -{ - FString Result; - - // Emit the specifier key - Result += Key; - - // Emit the values if there are any - if (Values.Num()) - { - Result += TEXT("="); - - if (Values.Num() == 1) - { - // One value goes on it's own - Result += Values[0]; - } - else - { - // More than one value goes in parens, separated by commas - Result += TEXT("("); - for (int32 ValueIndex = 0; ValueIndex < Values.Num(); ++ValueIndex) - { - if (ValueIndex > 0) - { - Result += TEXT(", "); - } - Result += Values[ValueIndex]; - } - Result += TEXT(")"); - } - } - - return Result; -} -*/ - -///////////////////////////////////////////////////// -// FBaseParser - -FBaseParser::FBaseParser() - : StatementsParsed(0) - , LinesParsed(0) -{ -} - -void FBaseParser::ResetParser(const TCHAR* SourceBuffer, int32 StartingLineNumber) -{ - Input = SourceBuffer; - InputLen = FCString::Strlen(Input); - InputPos = 0; - PrevPos = 0; - PrevLine = 1; - InputLine = StartingLineNumber; -} - -/*----------------------------------------------------------------------------- - Single-character processing. ------------------------------------------------------------------------------*/ - -// -// Get a single character from the input stream and return it, or 0=end. -// -TCHAR FBaseParser::GetChar(bool bLiteral) -{ - bool bInsideComment = false; - - PrevPos = InputPos; - PrevLine = InputLine; - -Loop: - const TCHAR c = Input[InputPos++]; - if (bInsideComment) - { - // Record the character as a comment. - PrevComment += c; - } - - if (c == TEXT('\n')) - { - InputLine++; - } - else if (!bLiteral) - { - const TCHAR NextChar = PeekChar(); - if ( c==TEXT('/') && NextChar==TEXT('*') ) - { - if (!bInsideComment) - { - ClearComment(); - // Record the slash and star. - PrevComment += c; - PrevComment += NextChar; - bInsideComment = true; - - // Move past the star. Do it only when not in comment, - // otherwise end of comment might be missed e.g. - // /*/ Comment /*/ - // ~~~~~~~~~~~~~^ Will report second /* as beginning of comment - // And throw error that end of file is found in comment. - InputPos++; - } - - goto Loop; - } - else if( c==TEXT('*') && NextChar==TEXT('/') ) - { - if (!bInsideComment) - { - ClearComment(); - FError::Throwf(TEXT("Unexpected '*/' outside of comment") ); - } - - /** Asterisk and slash always end comment. */ - bInsideComment = false; - - // Star already recorded; record the slash. - PrevComment += Input[InputPos]; - - InputPos++; - goto Loop; - } - } - - if (bInsideComment) - { - if (c == 0) - { - ClearComment(); - FError::Throwf(TEXT("End of class header encountered inside comment") ); - } - goto Loop; - } - return c; -} - -// -// Unget the previous character retrieved with GetChar(). -// -void FBaseParser::UngetChar() -{ - InputPos = PrevPos; - InputLine = PrevLine; -} - -// -// Look at a single character from the input stream and return it, or 0=end. -// Has no effect on the input stream. -// -TCHAR FBaseParser::PeekChar() -{ - return (InputPos < InputLen) ? Input[InputPos] : 0; -} - -// -// Skip past all spaces and tabs in the input stream. -// -TCHAR FBaseParser::GetLeadingChar() -{ - TCHAR TrailingCommentNewline = 0; - - for (;;) - { - bool MultipleNewlines = false; - - TCHAR c; - - // Skip blanks. - do - { - c = GetChar(); - - // Check if we've encountered another newline since the last one - if (c == TrailingCommentNewline) - { - MultipleNewlines = true; - } - } while (IsWhitespace(c)); - - if (c != TEXT('/') || PeekChar() != TEXT('/')) - { - return c; - } - - // Clear the comment if we've encountered newlines since the last comment - if (MultipleNewlines) - { - ClearComment(); - } - - // Record the first slash. The first iteration of the loop will get the second slash. - PrevComment += c; - - do - { - c = GetChar(true); - if (c == 0) - return c; - PrevComment += c; - } while (!IsEOL(c)); - - TrailingCommentNewline = c; - - for (;;) - { - c = GetChar(); - if (c == 0) - return c; - if (c == TrailingCommentNewline || !IsEOL(c)) - { - UngetChar(); - break; - } - - PrevComment += c; - } - } -} - -bool FBaseParser::IsEOL( TCHAR c ) -{ - return c==TEXT('\n') || c==TEXT('\r') || c==0; -} - -bool FBaseParser::IsWhitespace( TCHAR c ) -{ - return c==TEXT(' ') || c==TEXT('\t') || c==TEXT('\r') || c==TEXT('\n'); -} - -/*----------------------------------------------------------------------------- - Tokenizing. ------------------------------------------------------------------------------*/ - -// Gets the next token from the input stream, advancing the variables which keep track of the current input position and line. -bool FBaseParser::GetToken( FToken& Token, bool bNoConsts/*=false*/, ESymbolParseOption bParseTemplateClosingBracket/*=ESymbolParseOption::Normal*/ ) -{ - Token.ClearTokenName(); - TCHAR c = GetLeadingChar(); - if( c == 0 ) - { - UngetChar(); - return 0; - } - TCHAR p = PeekChar(); - Token.StartPos = PrevPos; - Token.StartLine = PrevLine; - if( (c>='A' && c<='Z') || (c>='a' && c<='z') || (c=='_') ) - { - // Alphanumeric token. - int32 Length=0; - do - { - Token.Identifier[Length++] = c; - if( Length >= NAME_SIZE ) - { - FError::Throwf(TEXT("Identifer length exceeds maximum of %i"), (int32)NAME_SIZE); - Length = ((int32)NAME_SIZE) - 1; - break; - } - c = GetChar(); - } while( ((c>='A')&&(c<='Z')) || ((c>='a')&&(c<='z')) || ((c>='0')&&(c<='9')) || (c=='_') ); - UngetChar(); - Token.Identifier[Length]=0; - - // Assume this is an identifier unless we find otherwise. - Token.TokenType = TOKEN_Identifier; - - // If const values are allowed, determine whether the identifier represents a constant - if ( !bNoConsts ) - { - // See if the identifier is part of a vector, rotation or other struct constant. - // boolean true/false - if( Token.Matches(TEXT("true"), ESearchCase::IgnoreCase) ) - { - Token.SetConstBool(true); - return true; - } - else if( Token.Matches(TEXT("false"), ESearchCase::IgnoreCase) ) - { - Token.SetConstBool(false); - return true; - } - } - return true; - } - - // if const values are allowed, determine whether the non-identifier token represents a const - else if ( !bNoConsts && ((c>='0' && c<='9') || ((c=='+' || c=='-') && (p>='0' && p<='9'))) ) - { - // Integer or floating point constant. - bool bIsFloat = 0; - int32 Length = 0; - bool bIsHex = 0; - do - { - if( c==TEXT('.') ) - { - bIsFloat = true; - } - if( c==TEXT('X') || c == TEXT('x') ) - { - bIsHex = true; - } - - Token.Identifier[Length++] = c; - if( Length >= NAME_SIZE ) - { - FError::Throwf(TEXT("Number length exceeds maximum of %i "), (int32)NAME_SIZE ); - Length = ((int32)NAME_SIZE) - 1; - break; - } - c = FChar::ToUpper(GetChar()); - } while ((c >= TEXT('0') && c <= TEXT('9')) || (!bIsFloat && c == TEXT('.')) || (!bIsHex && c == TEXT('X')) || (bIsHex && c >= TEXT('A') && c <= TEXT('F'))); - - Token.Identifier[Length]=0; - if (!bIsFloat || c != 'F') - { - UngetChar(); - } - - if (bIsFloat) - { - Token.SetConstFloat( FCString::Atof(Token.Identifier) ); - } - else if (bIsHex) - { - TCHAR* End = Token.Identifier + FCString::Strlen(Token.Identifier); - Token.SetConstInt64( FCString::Strtoi64(Token.Identifier,&End,0) ); - } - else - { - Token.SetConstInt64( FCString::Atoi64(Token.Identifier) ); - } - return true; - } - else if (c == '\'') - { - TCHAR ActualCharLiteral = GetChar(/*bLiteral=*/ true); - - if (ActualCharLiteral == '\\') - { - ActualCharLiteral = GetChar(/*bLiteral=*/ true); - switch (ActualCharLiteral) - { - case TCHAR('t'): - ActualCharLiteral = '\t'; - break; - case TCHAR('n'): - ActualCharLiteral = '\n'; - break; - case TCHAR('r'): - ActualCharLiteral = '\r'; - break; - } - } - - c = GetChar(/*bLiteral=*/ true); - if (c != '\'') - { - FError::Throwf(TEXT("Unterminated character constant")); - UngetChar(); - } - - Token.SetConstChar(ActualCharLiteral); - return true; - } - else if (c == '"') - { - // String constant. - TCHAR Temp[MAX_STRING_CONST_SIZE]; - int32 Length=0; - c = GetChar(/*bLiteral=*/ true); - while( (c!='"') && !IsEOL(c) ) - { - if( c=='\\' ) - { - c = GetChar(/*bLiteral=*/ true); - if( IsEOL(c) ) - { - break; - } - else if(c == 'n') - { - // Newline escape sequence. - c = '\n'; - } - } - Temp[Length++] = c; - if( Length >= MAX_STRING_CONST_SIZE ) - { - FError::Throwf(TEXT("String constant exceeds maximum of %i characters"), (int32)MAX_STRING_CONST_SIZE ); - c = TEXT('\"'); - Length = ((int32)MAX_STRING_CONST_SIZE) - 1; - break; - } - c = GetChar(/*bLiteral=*/ true); - } - Temp[Length]=0; - - if( c != '"' ) - { - FError::Throwf(TEXT("Unterminated string constant: %s"), Temp); - UngetChar(); - } - - Token.SetConstString(Temp); - return true; - } - else - { - // Symbol. - int32 Length=0; - Token.Identifier[Length++] = c; - - // Handle special 2-character symbols. - #define PAIR(cc,dd) ((c==cc)&&(d==dd)) /* Comparison macro for convenience */ - TCHAR d = GetChar(); - if - ( PAIR('<','<') - || (PAIR('>','>') && (bParseTemplateClosingBracket != ESymbolParseOption::CloseTemplateBracket)) - || PAIR('!','=') - || PAIR('<','=') - || PAIR('>','=') - || PAIR('+','+') - || PAIR('-','-') - || PAIR('+','=') - || PAIR('-','=') - || PAIR('*','=') - || PAIR('/','=') - || PAIR('&','&') - || PAIR('|','|') - || PAIR('^','^') - || PAIR('=','=') - || PAIR('*','*') - || PAIR('~','=') - || PAIR(':',':') - ) - { - Token.Identifier[Length++] = d; - if( c=='>' && d=='>' ) - { - if (GetChar()=='>') - { - Token.Identifier[Length++] = '>'; - } - else - { - UngetChar(); - } - } - } - else - { - UngetChar(); - } - #undef PAIR - - Token.Identifier[Length] = 0; - Token.TokenType = TOKEN_Symbol; - - return true; - } -} - -bool FBaseParser::GetRawTokenRespectingQuotes( FToken& Token, TCHAR StopChar /* = TCHAR('\n') */ ) -{ - // Get token after whitespace. - TCHAR Temp[MAX_STRING_CONST_SIZE]; - int32 Length=0; - TCHAR c = GetLeadingChar(); - - bool bInQuote = false; - - while( !IsEOL(c) && ((c != StopChar) || bInQuote) ) - { - if( (c=='/' && PeekChar()=='/') || (c=='/' && PeekChar()=='*') ) - { - break; - } - - if (c == '"') - { - bInQuote = !bInQuote; - } - - Temp[Length++] = c; - if( Length >= MAX_STRING_CONST_SIZE ) - { - FError::Throwf(TEXT("Identifier exceeds maximum of %i characters"), (int32)MAX_STRING_CONST_SIZE ); - c = GetChar(true); - Length = ((int32)MAX_STRING_CONST_SIZE) - 1; - break; - } - c = GetChar(true); - } - UngetChar(); - - if (bInQuote) - { - FError::Throwf(TEXT("Unterminated quoted string")); - } - - // Get rid of trailing whitespace. - while( Length>0 && (Temp[Length-1]==' ' || Temp[Length-1]==9 ) ) - { - Length--; - } - Temp[Length]=0; - - Token.SetConstString(Temp); - - return Length>0; -} - -/** - * Put all text from the current position up to either EOL or the StopToken - * into Token. Advances the compiler's current position. - * - * @param Token [out] will contain the text that was parsed - * @param StopChar stop processing when this character is reached - * - * @return the number of character parsed - */ -bool FBaseParser::GetRawToken( FToken& Token, TCHAR StopChar /* = TCHAR('\n') */ ) -{ - // Get token after whitespace. - TCHAR Temp[MAX_STRING_CONST_SIZE]; - int32 Length=0; - TCHAR c = GetLeadingChar(); - while( !IsEOL(c) && c != StopChar ) - { - if( (c=='/' && PeekChar()=='/') || (c=='/' && PeekChar()=='*') ) - { - break; - } - Temp[Length++] = c; - if( Length >= MAX_STRING_CONST_SIZE ) - { - FError::Throwf(TEXT("Identifier exceeds maximum of %i characters"), (int32)MAX_STRING_CONST_SIZE ); - } - c = GetChar(true); - } - UngetChar(); - - // Get rid of trailing whitespace. - while( Length>0 && (Temp[Length-1]==' ' || Temp[Length-1]==9 ) ) - { - Length--; - } - Temp[Length]=0; - - Token.SetConstString(Temp); - - return Length>0; -} - -// -// Get an identifier token, return 1 if gotten, 0 if not. -// -bool FBaseParser::GetIdentifier( FToken& Token, bool bNoConsts ) -{ - if (!GetToken(Token, bNoConsts)) - { - return false; - } - - if (Token.TokenType == TOKEN_Identifier) - { - return true; - } - - UngetToken(Token); - return false; -} - -// -// Get a symbol token, return 1 if gotten, 0 if not. -// -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -bool FBaseParser::GetSymbol( FToken& Token ) -{ - if (!GetToken(Token)) - { - return false; - } - - if( Token.TokenType == TOKEN_Symbol ) - { - return true; - } - - UngetToken(Token); - return false; -} -*/ - -bool FBaseParser::GetConstInt(int32& Result, const TCHAR* Tag) -{ - FToken Token; - if (GetToken(Token)) - { - if (Token.GetConstInt(Result)) - { - return true; - } - else - { - UngetToken(Token); - } - } - - if (Tag != NULL) - { - FError::Throwf(TEXT("%s: Missing constant integer"), Tag ); - } - - return false; -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -bool FBaseParser::GetConstInt64(int64& Result, const TCHAR* Tag) -{ - FToken Token; - if (GetToken(Token)) - { - if (Token.GetConstInt64(Result)) - { - return true; - } - else - { - UngetToken(Token); - } - } - - if (Tag != NULL) - { - FError::Throwf(TEXT("%s: Missing constant integer"), Tag ); - } - - return false; -} -*/ - -bool FBaseParser::MatchSymbol( const TCHAR Match, ESymbolParseOption bParseTemplateClosingBracket/*=ESymbolParseOption::Normal*/ ) -{ - FToken Token; - - if (GetToken(Token, /*bNoConsts=*/ true, bParseTemplateClosingBracket)) - { - if (Token.TokenType==TOKEN_Symbol && Token.Identifier[0] == Match && Token.Identifier[1] == 0) - { - return true; - } - else - { - UngetToken(Token); - } - } - - return false; -} - -bool FBaseParser::MatchSymbol(const TCHAR* Match, ESymbolParseOption bParseTemplateClosingBracket/*=ESymbolParseOption::Normal*/) -{ - FToken Token; - - if (GetToken(Token, /*bNoConsts=*/ true, bParseTemplateClosingBracket)) - { - if (Token.TokenType==TOKEN_Symbol && !FCString::Strcmp(Token.Identifier, Match)) - { - return true; - } - else - { - UngetToken(Token); - } - } - - return false; -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -// -// Get a specific identifier and return 1 if gotten, 0 if not. -// This is used primarily for checking for required symbols during compilation. -// -bool FBaseParser::MatchIdentifierByName( FName Match ) -{ - FToken Token; - if (!GetToken(Token)) - { - return false; - } - - if ((Token.TokenType == TOKEN_Identifier) && (Token.GetTokenName() == Match)) - { - return true; - } - - UngetToken(Token); - return false; -} -*/ - -bool FBaseParser::MatchIdentifier( const TCHAR* Match, ESearchCase::Type SearchCase) -{ - FToken Token; - if (GetToken(Token)) - { - if (Token.TokenType==TOKEN_Identifier && ((SearchCase == ESearchCase::CaseSensitive) ? !FCString::Strcmp(Token.Identifier, Match) : !FCString::Stricmp(Token.Identifier, Match))) - { - return true; - } - else - { - UngetToken(Token); - } - } - - return false; -} - -bool FBaseParser::MatchConstInt( const TCHAR* Match ) -{ - FToken Token; - if (GetToken(Token)) - { - if( Token.TokenType==TOKEN_Const && (Token.Type == CPT_Int || Token.Type == CPT_Int64) && FCString::Strcmp(Token.Identifier,Match)==0 ) - { - return true; - } - else - { - UngetToken(Token); - } - } - - return false; -} - -bool FBaseParser::MatchAnyConstInt() -{ - FToken Token; - if (GetToken(Token)) - { - if( Token.TokenType==TOKEN_Const && (Token.Type == CPT_Int || Token.Type == CPT_Int64) ) - { - return true; - } - else - { - UngetToken(Token); - } - } - - return false; -} - -void FBaseParser::MatchSemi() -{ - if( !MatchSymbol(TEXT(';')) ) - { - FToken Token; - if( GetToken(Token) ) - { - FError::Throwf(TEXT("Missing ';' before '%s'"), Token.Identifier ); - } - else - { - FError::Throwf(TEXT("Missing ';'") ); - } - } -} - - -// -// Peek ahead and see if a symbol follows in the stream. -// -bool FBaseParser::PeekSymbol( const TCHAR Match ) -{ - FToken Token; - if (!GetToken(Token, true)) - { - return false; - } - UngetToken(Token); - - return Token.TokenType==TOKEN_Symbol && Token.Identifier[0] == Match && Token.Identifier[1] == 0; -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -// -// Peek ahead and see if an identifier follows in the stream. -// -bool FBaseParser::PeekIdentifierByName( FName Match ) -{ - FToken Token; - if (!GetToken(Token, true)) - { - return false; - } - UngetToken(Token); - return Token.TokenType==TOKEN_Identifier && Token.GetTokenName()==Match; -} -*/ - -bool FBaseParser::PeekIdentifier( const TCHAR* Match, ESearchCase::Type SearchCase) -{ - FToken Token; - if (!GetToken(Token, true)) - { - return false; - } - UngetToken(Token); - return Token.TokenType==TOKEN_Identifier && ((SearchCase == ESearchCase::CaseSensitive) ? !FCString::Strcmp(Token.Identifier, Match) : !FCString::Stricmp(Token.Identifier, Match)); -} - -// -// Unget the most recently gotten token. -// -void FBaseParser::UngetToken( const FToken& Token ) -{ - InputPos = Token.StartPos; - InputLine = Token.StartLine; -} - -// -// Require a symbol. -// -void FBaseParser::RequireSymbol( const TCHAR Match, const TCHAR* Tag, ESymbolParseOption bParseTemplateClosingBracket/*=ESymbolParseOption::Normal*/ ) -{ - if (!MatchSymbol(Match, bParseTemplateClosingBracket)) - { - FError::Throwf(TEXT("Missing '%c' in %s"), Match, Tag ); - } -} - -void FBaseParser::RequireSymbol(const TCHAR Match, TFunctionRef ErrorGetter, ESymbolParseOption bParseTemplateClosingBracket/*=ESymbolParseOption::Normal*/) -{ - if (!MatchSymbol(Match, bParseTemplateClosingBracket)) - { - FError::Throwf(TEXT("Missing '%c' in %s"), Match, *ErrorGetter()); - } -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -// -// Require an integer. -// -void FBaseParser::RequireConstInt( const TCHAR* Match, const TCHAR* Tag ) -{ - if (!MatchConstInt(Match)) - { - FError::Throwf(TEXT("Missing integer '%s' in %s"), Match, Tag ); - } -} -*/ - -void FBaseParser::RequireAnyConstInt( const TCHAR* Tag ) -{ - if (!MatchAnyConstInt()) - { - FError::Throwf(TEXT("Missing integer in %s"), Tag ); - } -} - -// -// Require an identifier. -// -void FBaseParser::RequireIdentifier( const TCHAR* Match, const ESearchCase::Type SearchCase, const TCHAR* Tag ) -{ - if (!MatchIdentifier(Match, SearchCase)) - { - FError::Throwf(TEXT("Missing '%s' in %s"), Match, Tag ); - } -} - -// Clears out the stored comment. -void FBaseParser::ClearComment() -{ - PrevComment.Reset(); -} - -// Reads a new-style value -//@TODO: UCREMOVAL: Needs a better name -FString FBaseParser::ReadNewStyleValue(const TCHAR* TypeOfSpecifier) -{ - FToken ValueToken; - if (!GetToken(ValueToken, false)) - { - FError::Throwf(TEXT("Expected a value when handling a %s"), TypeOfSpecifier); - } - - FString Result; - - switch (ValueToken.TokenType) - { - case TOKEN_Identifier: - case TOKEN_Symbol: - - Result = ValueToken.Identifier; - - if (MatchSymbol(TEXT('='))) - { - Result += TEXT("="); - Result += ReadNewStyleValue(TypeOfSpecifier); - } - break; - - case TOKEN_Const: - - Result = ValueToken.GetConstantValue(); - break; - } - - return Result; -} - -// Reads [ Value | ['(' Value [',' Value]* ')'] ] and places each value into the Items array -bool FBaseParser::ReadOptionalCommaSeparatedListInParens(TArray& Items, const TCHAR* TypeOfSpecifier) -{ - if (MatchSymbol(TEXT('('))) - { - do - { - FString Value = ReadNewStyleValue(TypeOfSpecifier); - Items.Add(Value); - } while ( MatchSymbol(TEXT(',')) ); - - RequireSymbol(TEXT(')'), TypeOfSpecifier); - - return true; - } - - return false; -} - -void FBaseParser::ParseNameWithPotentialAPIMacroPrefix(FString& DeclaredName, FString& RequiredAPIMacroIfPresent, const TCHAR* FailureMessage) -{ - // Expecting Name | (MODULE_API Name) - FToken NameToken; - - // Read an identifier - if (!GetIdentifier(NameToken)) - { - FError::Throwf(TEXT("Missing %s name"), FailureMessage); - } - - // Is the identifier the name or an DLL import/export API macro? - FString NameTokenStr = NameToken.Identifier; - if (NameTokenStr.EndsWith(TEXT("_API"), ESearchCase::CaseSensitive)) - { - RequiredAPIMacroIfPresent = MoveTemp(NameTokenStr); - - // Read the real name - if (!GetIdentifier(NameToken)) - { - FError::Throwf(TEXT("Missing %s name"), FailureMessage); - } - DeclaredName = NameToken.Identifier; - } - else - { - DeclaredName = MoveTemp(NameTokenStr); - RequiredAPIMacroIfPresent.Reset(); - } -} - -// Reads a set of specifiers (with optional values) inside the () of a new-style metadata macro like UPROPERTY or UFUNCTION -void FBaseParser::ReadSpecifierSetInsideMacro(TArray& SpecifiersFound, const TCHAR* TypeOfSpecifier, TMap& MetaData) -{ - int32 FoundSpecifierCount = 0; - - auto ErrorMessageGetter = [TypeOfSpecifier]() { return FString::Printf(TEXT("%s declaration specifier"), TypeOfSpecifier); }; - - RequireSymbol(TEXT('('), ErrorMessageGetter); - - while (!MatchSymbol(TEXT(')'))) - { - if (FoundSpecifierCount > 0) - { - RequireSymbol(TEXT(','), ErrorMessageGetter); - } - ++FoundSpecifierCount; - - // Read the specifier key - FToken Specifier; - if (!GetToken(Specifier)) - { - FError::Throwf(TEXT("Expected %s"), *ErrorMessageGetter()); - } - - if (Specifier.Matches(TEXT("meta"), ESearchCase::IgnoreCase)) - { - RequireSymbol(TEXT('='), ErrorMessageGetter); - RequireSymbol(TEXT('('), ErrorMessageGetter); - - // Keep reading comma-separated metadata pairs - do - { - // Read a key - FToken MetaKeyToken; - if (!GetIdentifier(MetaKeyToken)) - { - FError::Throwf(TEXT("Expected a metadata key")); - } - - FString Key = MetaKeyToken.Identifier; - - // Potentially read a value - FString Value; - if (MatchSymbol(TEXT('='))) - { - Value = ReadNewStyleValue(TypeOfSpecifier); - } - - // Validate the value is a valid type for the key and insert it into the map - InsertMetaDataPair(MetaData, MoveTemp(Key), MoveTemp(Value)); - } while ( MatchSymbol(TEXT(',')) ); - - RequireSymbol(TEXT(')'), ErrorMessageGetter); - } - // Look up specifier in metadata dictionary - else if (FMetadataKeyword* MetadataKeyword = GetMetadataKeyword(Specifier.Identifier)) - { - if (MatchSymbol(TEXT('='))) - { - if (MetadataKeyword->ValueArgument == EMetadataValueArgument::None) - { - FError::Throwf(TEXT("Incorrect = after metadata specifier '%s'"), Specifier.Identifier); - } - - FString Value = ReadNewStyleValue(TypeOfSpecifier); - MetadataKeyword->ApplyToMetadata(MetaData, &Value); - } - else - { - if (MetadataKeyword->ValueArgument == EMetadataValueArgument::Required) - { - FError::Throwf(TEXT("Missing = after metadata specifier '%s'"), Specifier.Identifier); - } - - MetadataKeyword->ApplyToMetadata(MetaData); - } - } - else - { - // Creating a new specifier - SpecifiersFound.Emplace(Specifier.Identifier); - - // Look for a value for this specifier - if (MatchSymbol(TEXT('=')) || PeekSymbol(TEXT('('))) - { - TArray& NewPairValues = SpecifiersFound.Last().Values; - if (!ReadOptionalCommaSeparatedListInParens(NewPairValues, TypeOfSpecifier)) - { - NewPairValues.Add(ReadNewStyleValue(TypeOfSpecifier)); - } - } - } - } -} - -void FBaseParser::InsertMetaDataPair(TMap& MetaData, FString Key, FString Value) -{ - // make sure the key is valid - if (Key.Len() == 0) - { - FError::Throwf(TEXT("Invalid metadata")); - } - - // trim extra white space and quotes - Key.TrimStartAndEndInline(); - - InsertMetaDataPair(MetaData, FName(*Key), MoveTemp(Value)); -} - -void FBaseParser::InsertMetaDataPair(TMap& MetaData, FName KeyName, FString Value) -{ - Value.TrimStartAndEndInline(); - Value.TrimQuotesInline(); - - FString* ExistingValue = MetaData.Find(KeyName); - if (ExistingValue && Value != *ExistingValue) - { - FError::Throwf(TEXT("Metadata key '%s' first seen with value '%s' then '%s'"), *KeyName.ToString(), **ExistingValue, *Value); - } - - // finally we have enough to put it into our metadata - MetaData.Add(KeyName, MoveTemp(Value)); -} diff --git a/Engine/Source/Programs/UHTLite/Private/BaseParser.h b/Engine/Source/Programs/UHTLite/Private/BaseParser.h deleted file mode 100644 index aa6bd4031bad..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/BaseParser.h +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Containers/UnrealString.h" - -class FToken; - -///////////////////////////////////////////////////// -// FBaseParser - - -enum class ESymbolParseOption -{ - Normal, - CloseTemplateBracket -}; - -// A specifier with optional value -struct FPropertySpecifier -{ -public: - explicit FPropertySpecifier(FString&& InKey) - : Key(MoveTemp(InKey)) - { - } - - explicit FPropertySpecifier(const FString& InKey) - : Key(InKey) - { - } - - FString Key; - TArray Values; - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - FString ConvertToString() const; - */ -}; - -// -// Base class of header parsers. -// - -class FBaseParser -{ -protected: - FBaseParser(); - -public: - // Input text. - const TCHAR* Input; - - // Length of input text. - int32 InputLen; - - // Current position in text. - int32 InputPos; - - // Current line in text. - int32 InputLine; - - // Position previous to last GetChar() call. - int32 PrevPos; - - // Line previous to last GetChar() call. - int32 PrevLine; - - // Previous comment parsed by GetChar() call. - FString PrevComment; - - // Number of statements parsed. - int32 StatementsParsed; - - // Total number of lines parsed. - int32 LinesParsed; - - void ResetParser(const TCHAR* SourceBuffer, int32 StartingLineNumber = 1); - - // Low-level parsing functions. - TCHAR GetChar( bool Literal = false ); - TCHAR PeekChar(); - TCHAR GetLeadingChar(); - void UngetChar(); - - /** - * Tests if a character is an end-of-line character. - * - * @param c The character to test. - * - * @return true if c is an end-of-line character, false otherwise. - */ - static bool IsEOL( TCHAR c ); - - /** - * Tests if a character is a whitespace character. - * - * @param c The character to test. - * - * @return true if c is an whitespace character, false otherwise. - */ - static bool IsWhitespace( TCHAR c ); - - /** - * Gets the next token from the input stream, advancing the variables which keep track of the current input position and line. - * - * @param Token receives the value of the parsed text; if Token is pre-initialized, special logic is performed - * to attempt to evaluated Token in the context of that type. Useful for distinguishing between ambigous symbols - * like enum tags. - * @param NoConsts specify true to indicate that tokens representing literal const values are not allowed. - * @param ParseTemplateClosingBracket specify true to treat >> as two template closing brackets instead of shift operator. - * - * @return true if a token was successfully processed, false otherwise. - */ - bool GetToken( FToken& Token, bool bNoConsts = false, ESymbolParseOption bParseTemplateClosingBracket = ESymbolParseOption::Normal ); - - /** - * Put all text from the current position up to either EOL or the StopToken - * into Token. Advances the compiler's current position. - * - * @param Token [out] will contain the text that was parsed - * @param StopChar stop processing when this character is reached - * - * @return true if a token was parsed - */ - bool GetRawToken( FToken& Token, TCHAR StopChar = TCHAR('\n') ); - - // Doesn't quit if StopChar is found inside a double-quoted string, but does not support quote escapes - bool GetRawTokenRespectingQuotes( FToken& Token, TCHAR StopChar = TCHAR('\n') ); - - void UngetToken( const FToken& Token ); - bool GetIdentifier( FToken& Token, bool bNoConsts = false ); - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - bool GetSymbol( FToken& Token ); - */ - - /** - * Get an int constant - * @return true on success, otherwise false. - */ - bool GetConstInt(int32& Result, const TCHAR* Tag = NULL); - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - bool GetConstInt64(int64& Result, const TCHAR* Tag = NULL); - */ - - // Matching predefined text. - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - bool MatchIdentifierByName( FName Match ); - */ - bool MatchIdentifier( const TCHAR* Match, ESearchCase::Type SearchCase); - bool MatchConstInt( const TCHAR* Match ); - bool MatchAnyConstInt(); - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - bool PeekIdentifierByName( FName Match ); - */ - bool PeekIdentifier( const TCHAR* Match, ESearchCase::Type SearchCase); - bool MatchSymbol( const TCHAR Match, ESymbolParseOption bParseTemplateClosingBracket = ESymbolParseOption::Normal ); - bool MatchSymbol(const TCHAR* Match, ESymbolParseOption bParseTemplateClosingBracket = ESymbolParseOption::Normal); - void MatchSemi(); - bool PeekSymbol( const TCHAR Match ); - - // Requiring predefined text. - void RequireIdentifier( const TCHAR* Match, ESearchCase::Type SearchCase, const TCHAR* Tag ); - void RequireSymbol( const TCHAR Match, const TCHAR* Tag, ESymbolParseOption bParseTemplateClosingBracket = ESymbolParseOption::Normal ); - void RequireSymbol(const TCHAR Match, TFunctionRef TagGetter, ESymbolParseOption bParseTemplateClosingBracket = ESymbolParseOption::Normal); - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - void RequireConstInt( const TCHAR* Match, const TCHAR* Tag ); - */ - void RequireAnyConstInt( const TCHAR* Tag ); - - /** Clears out the stored comment. */ - void ClearComment(); - - // Reads a new-style value - //@TODO: UCREMOVAL: Needs a better name - FString ReadNewStyleValue(const TCHAR* TypeOfSpecifier); - - // Reads ['(' Value [',' Value]* ')'] and places each value into the Items array - bool ReadOptionalCommaSeparatedListInParens(TArray& Items, const TCHAR* TypeOfSpecifier); - - ////////////// - // Complicated* parsing code that needs to be shared between the preparser and the parser - // (* i.e., doesn't really belong in the base parser) - - // Expecting Name | (MODULE_API Name) - // Places Name into DeclaredName - // Places MODULE_API (where MODULE varies) into RequiredAPIMacroIfPresent - // FailureMessage is printed out if the expectation is broken. - void ParseNameWithPotentialAPIMacroPrefix(FString& DeclaredName, FString& RequiredAPIMacroIfPresent, const TCHAR* FailureMessage); - - // Reads a set of specifiers (with optional values) inside the () of a new-style metadata macro like UPROPERTY or UFUNCTION - void ReadSpecifierSetInsideMacro(TArray& SpecifiersFound, const TCHAR* TypeOfSpecifier, TMap& MetaData); - - // Validates and inserts one key-value pair into the meta data map - static void InsertMetaDataPair(TMap& MetaData, FString InKey, FString InValue); - - // Validates and inserts one key-value pair into the meta data map - static void InsertMetaDataPair(TMap& MetaData, FName InKey, FString InValue); - - ////////////// -}; diff --git a/Engine/Source/Programs/UHTLite/Private/ClassDeclarationMetaData.cpp b/Engine/Source/Programs/UHTLite/Private/ClassDeclarationMetaData.cpp deleted file mode 100644 index 3f7efda2d3f4..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ClassDeclarationMetaData.cpp +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "ClassDeclarationMetaData.h" -#include "UnrealHeaderTool.h" -#include "UObject/ErrorException.h" -#include "UObject/Object.h" -#include "UObject/Interface.h" -#include "BaseParser.h" -#include "HeaderParser.h" -#include "Classes.h" -#include "Specifiers/ClassMetadataSpecifiers.h" -#include "Algo/FindSortedStringCaseInsensitive.h" - -// Utility functions -namespace -{ - static const FName NAME_IgnoreCategoryKeywordsInSubclasses(TEXT("IgnoreCategoryKeywordsInSubclasses")); - bool IsActorClass(UClass *Class) - { - while (Class) - { - if (Class->GetFName() == NAME_Actor) - { - return true; - } - Class = Class->GetSuperClass(); - } - return false; - } -} - -FClassDeclarationMetaData::FClassDeclarationMetaData() - : ClassFlags(CLASS_None) - , WantsToBePlaceable(false) -{ -} - -void FClassDeclarationMetaData::ParseClassProperties(TArray&& InClassSpecifiers, const FString& InRequiredAPIMacroIfPresent) -{ - ClassFlags = CLASS_None; - // Record that this class is RequiredAPI if the CORE_API style macro was present - if (!InRequiredAPIMacroIfPresent.IsEmpty()) - { - ClassFlags |= CLASS_RequiredAPI; - } - ClassFlags |= CLASS_Native; - - // Process all of the class specifiers - - for (FPropertySpecifier& PropSpecifier : InClassSpecifiers) - { - switch ((EClassMetadataSpecifier)Algo::FindSortedStringCaseInsensitive(*PropSpecifier.Key, GClassMetadataSpecifierStrings)) - { - case EClassMetadataSpecifier::NoExport: - - // Don't export to C++ header. - ClassFlags |= CLASS_NoExport; - break; - - case EClassMetadataSpecifier::Intrinsic: - - ClassFlags |= CLASS_Intrinsic; - break; - - case EClassMetadataSpecifier::ComponentWrapperClass: - - MetaData.Add(NAME_IgnoreCategoryKeywordsInSubclasses, TEXT("true")); - break; - - case EClassMetadataSpecifier::Within: - - ClassWithin = FHeaderParser::RequireExactlyOneSpecifierValue(PropSpecifier); - break; - - case EClassMetadataSpecifier::EditInlineNew: - - // Class can be constructed from the New button in editinline - ClassFlags |= CLASS_EditInlineNew; - break; - - case EClassMetadataSpecifier::NotEditInlineNew: - - // Class cannot be constructed from the New button in editinline - ClassFlags &= ~CLASS_EditInlineNew; - break; - - case EClassMetadataSpecifier::Placeable: - - WantsToBePlaceable = true; - ClassFlags &= ~CLASS_NotPlaceable; - break; - - case EClassMetadataSpecifier::DefaultToInstanced: - - // these classed default to instanced. - ClassFlags |= CLASS_DefaultToInstanced; - break; - - case EClassMetadataSpecifier::NotPlaceable: - - // Don't allow the class to be placed in the editor. - ClassFlags |= CLASS_NotPlaceable; - break; - - case EClassMetadataSpecifier::HideDropdown: - - // Prevents class from appearing in class comboboxes in the property window - ClassFlags |= CLASS_HideDropDown; - break; - - case EClassMetadataSpecifier::DependsOn: - - FError::Throwf(TEXT("The dependsOn specifier is deprecated. Please use #include \"ClassHeaderFilename.h\" instead.")); - break; - - case EClassMetadataSpecifier::MinimalAPI: - - ClassFlags |= CLASS_MinimalAPI; - break; - - case EClassMetadataSpecifier::Const: - - ClassFlags |= CLASS_Const; - break; - - case EClassMetadataSpecifier::PerObjectConfig: - - ClassFlags |= CLASS_PerObjectConfig; - break; - - case EClassMetadataSpecifier::ConfigDoNotCheckDefaults: - - ClassFlags |= CLASS_ConfigDoNotCheckDefaults; - break; - - case EClassMetadataSpecifier::Abstract: - - // Hide all editable properties. - ClassFlags |= CLASS_Abstract; - break; - - case EClassMetadataSpecifier::Deprecated: - - ClassFlags |= CLASS_Deprecated; - - // Don't allow the class to be placed in the editor. - ClassFlags |= CLASS_NotPlaceable; - - break; - - case EClassMetadataSpecifier::Transient: - - // Transient class. - ClassFlags |= CLASS_Transient; - break; - - case EClassMetadataSpecifier::NonTransient: - - // this child of a transient class is not transient - remove the transient flag - ClassFlags &= ~CLASS_Transient; - break; - - case EClassMetadataSpecifier::CustomConstructor: - - // we will not export a constructor for this class, assuming it is in the CPP block - ClassFlags |= CLASS_CustomConstructor; - break; - - case EClassMetadataSpecifier::Config: - - // Class containing config properties - parse the name of the config file to use - ConfigName = FHeaderParser::RequireExactlyOneSpecifierValue(PropSpecifier); - break; - - case EClassMetadataSpecifier::DefaultConfig: - - // Save object config only to Default INIs, never to local INIs. - ClassFlags |= CLASS_DefaultConfig; - break; - - case EClassMetadataSpecifier::GlobalUserConfig: - - // Save object config only to global user overrides, never to local INIs - ClassFlags |= CLASS_GlobalUserConfig; - break; - - case EClassMetadataSpecifier::ShowCategories: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (FString& Value : PropSpecifier.Values) - { - ShowCategories.AddUnique(MoveTemp(Value)); - } - break; - - case EClassMetadataSpecifier::HideCategories: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (FString& Value : PropSpecifier.Values) - { - HideCategories.AddUnique(MoveTemp(Value)); - } - break; - - case EClassMetadataSpecifier::ShowFunctions: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (const FString& Value : PropSpecifier.Values) - { - HideFunctions.RemoveSwap(Value); - } - break; - - case EClassMetadataSpecifier::HideFunctions: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (FString& Value : PropSpecifier.Values) - { - HideFunctions.AddUnique(MoveTemp(Value)); - } - break; - - // Currently some code only handles a single sidecar data structure so we enforce that here - case EClassMetadataSpecifier::SparseClassDataTypes: - - SparseClassDataTypes.AddUnique(FHeaderParser::RequireExactlyOneSpecifierValue(PropSpecifier)); - break; - - case EClassMetadataSpecifier::ClassGroup: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (FString& Value : PropSpecifier.Values) - { - ClassGroupNames.Add(MoveTemp(Value)); - } - break; - - case EClassMetadataSpecifier::AutoExpandCategories: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (FString& Value : PropSpecifier.Values) - { - AutoCollapseCategories.RemoveSwap(Value); - AutoExpandCategories.AddUnique(MoveTemp(Value)); - } - break; - - case EClassMetadataSpecifier::AutoCollapseCategories: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (FString& Value : PropSpecifier.Values) - { - AutoExpandCategories.RemoveSwap(Value); - AutoCollapseCategories.AddUnique(MoveTemp(Value)); - } - break; - - case EClassMetadataSpecifier::DontAutoCollapseCategories: - - FHeaderParser::RequireSpecifierValue(PropSpecifier); - - for (const FString& Value : PropSpecifier.Values) - { - AutoCollapseCategories.RemoveSwap(Value); - } - break; - - case EClassMetadataSpecifier::CollapseCategories: - - // Class' properties should not be shown categorized in the editor. - ClassFlags |= CLASS_CollapseCategories; - break; - - case EClassMetadataSpecifier::DontCollapseCategories: - - // Class' properties should be shown categorized in the editor. - ClassFlags &= ~CLASS_CollapseCategories; - break; - - case EClassMetadataSpecifier::AdvancedClassDisplay: - - // By default the class properties are shown in advanced sections in UI - MetaData.Add(FHeaderParserNames::NAME_AdvancedClassDisplay, TEXT("true")); - break; - - case EClassMetadataSpecifier::ConversionRoot: - - MetaData.Add(FHeaderParserNames::NAME_IsConversionRoot, TEXT("true")); - break; - - default: - FError::Throwf(TEXT("Unknown class specifier '%s'"), *PropSpecifier.Key); - } - } -} - -void FClassDeclarationMetaData::MergeShowCategories() -{ - for (const FString& Value : ShowCategories) - { - // if we didn't find this specific category path in the HideCategories metadata - if (HideCategories.RemoveSwap(Value) == 0) - { - TArray SubCategoryList; - Value.ParseIntoArray(SubCategoryList, TEXT("|"), true); - - FString SubCategoryPath; - // look to see if any of the parent paths are excluded in the HideCategories list - for (int32 CategoryPathIndex = 0; CategoryPathIndex < SubCategoryList.Num() - 1; ++CategoryPathIndex) - { - SubCategoryPath += SubCategoryList[CategoryPathIndex]; - // if we're hiding a parent category, then we need to flag this sub category for show - if (HideCategories.Contains(SubCategoryPath)) - { - ShowSubCatgories.AddUnique(Value); - break; - } - SubCategoryPath += "|"; - } - } - } - // Once the categories have been merged, empty the array as we will no longer need it nor should we use it - ShowCategories.Empty(); -} - -void FClassDeclarationMetaData::MergeClassCategories(FClass* Class) -{ - TArray ParentHideCategories; - TArray ParentShowSubCatgories; - TArray ParentHideFunctions; - TArray ParentAutoExpandCategories; - TArray ParentAutoCollapseCategories; - Class->GetHideCategories(ParentHideCategories); - Class->GetShowCategories(ParentShowSubCatgories); - Class->GetHideFunctions(ParentHideFunctions); - Class->GetAutoExpandCategories(ParentAutoExpandCategories); - Class->GetAutoCollapseCategories(ParentAutoCollapseCategories); - - // Add parent categories. We store the opposite of HideCategories and HideFunctions in a separate array anyway. - HideCategories.Append(MoveTemp(ParentHideCategories)); - ShowSubCatgories.Append(MoveTemp(ParentShowSubCatgories)); - HideFunctions.Append(MoveTemp(ParentHideFunctions)); - - MergeShowCategories(); - - // Merge ShowFunctions and HideFunctions - for (const FString& Value : ShowFunctions) - { - HideFunctions.RemoveSwap(Value); - } - ShowFunctions.Empty(); - - // Merge DontAutoCollapseCategories and AutoCollapseCategories - for (const FString& Value : DontAutoCollapseCategories) - { - AutoCollapseCategories.RemoveSwap(Value); - } - DontAutoCollapseCategories.Empty(); - - // Merge ShowFunctions and HideFunctions - for (const FString& Value : ShowFunctions) - { - HideFunctions.RemoveSwap(Value); - } - ShowFunctions.Empty(); - - // Merge AutoExpandCategories and AutoCollapseCategories (we still want to keep AutoExpandCategories though!) - for (const FString& Value : AutoExpandCategories) - { - AutoCollapseCategories.RemoveSwap(Value); - ParentAutoCollapseCategories.RemoveSwap(Value); - } - - // Do the same as above but the other way around - for (const FString& Value : AutoCollapseCategories) - { - AutoExpandCategories.RemoveSwap(Value); - ParentAutoExpandCategories.RemoveSwap(Value); - } - - // Once AutoExpandCategories and AutoCollapseCategories for THIS class have been parsed, add the parent inherited categories - AutoCollapseCategories.Append(MoveTemp(ParentAutoCollapseCategories)); - AutoExpandCategories.Append(MoveTemp(ParentAutoExpandCategories)); -} - -void FClassDeclarationMetaData::MergeAndValidateClassFlags(const FString& DeclaredClassName, uint32 PreviousClassFlags, FClass* Class, const FClasses& AllClasses) const -{ - if (WantsToBePlaceable) - { - if (!(Class->ClassFlags & CLASS_NotPlaceable)) - { - FError::Throwf(TEXT("The 'placeable' specifier is only allowed on classes which have a base class that's marked as not placeable. Classes are assumed to be placeable by default.")); - } - Class->ClassFlags &= ~CLASS_NotPlaceable; - WantsToBePlaceable = false; // Reset this flag after it's been merged - } - - // Now merge all remaining flags/properties - Class->ClassFlags |= ClassFlags; - Class->ClassConfigName = FName(*ConfigName); - - SetAndValidateWithinClass(Class, AllClasses); - SetAndValidateConfigName(Class); - - if (!!(Class->ClassFlags & CLASS_EditInlineNew)) - { - // don't allow actor classes to be declared editinlinenew - if (IsActorClass(Class)) - { - FError::Throwf(TEXT("Invalid class attribute: Creating actor instances via the property window is not allowed")); - } - } - - // Make sure both RequiredAPI and MinimalAPI aren't specified - if (Class->HasAllClassFlags(CLASS_MinimalAPI | CLASS_RequiredAPI)) - { - FError::Throwf(TEXT("MinimalAPI cannot be specified when the class is fully exported using a MODULENAME_API macro")); - } - - // All classes must start with a valid Unreal prefix - const FString ExpectedClassName = Class->GetNameWithPrefix(); - if (DeclaredClassName != ExpectedClassName) - { - FError::Throwf(TEXT("Class name '%s' is invalid, should be identified as '%s'"), *DeclaredClassName, *ExpectedClassName); - } - - if ((Class->ClassFlags&CLASS_NoExport)) - { - // if the class's class flags didn't contain CLASS_NoExport before it was parsed, it means either: - // a) the DECLARE_CLASS macro for this native class doesn't contain the CLASS_NoExport flag (this is an error) - // b) this is a new native class, which isn't yet hooked up to static registration (this is OK) - if (!(Class->ClassFlags&CLASS_Intrinsic) && (PreviousClassFlags & CLASS_NoExport) == 0 && - (PreviousClassFlags&CLASS_Native) != 0) // a new native class (one that hasn't been compiled into C++ yet) won't have this set - { - FError::Throwf(TEXT("'noexport': Must include CLASS_NoExport in native class declaration")); - } - } - - if (!Class->HasAnyClassFlags(CLASS_Abstract) && ((PreviousClassFlags & CLASS_Abstract) != 0)) - { - if (Class->HasAnyClassFlags(CLASS_NoExport)) - { - FError::Throwf(TEXT("'abstract': NoExport class missing abstract keyword from class declaration (must change C++ version first)")); - Class->ClassFlags |= CLASS_Abstract; - } - else if (Class->IsNative()) - { - FError::Throwf(TEXT("'abstract': missing abstract keyword from class declaration - class will no longer be exported as abstract")); - } - } -} - -void FClassDeclarationMetaData::SetAndValidateConfigName(FClass* Class) const -{ - if (ConfigName.IsEmpty() == false) - { - // if the user specified "inherit", we're just going to use the parent class's config filename - // this is not actually necessary but it can be useful for explicitly communicating config-ness - if (ConfigName == TEXT("inherit")) - { - UClass* SuperClass = Class->GetSuperClass(); - if (!SuperClass) - { - FError::Throwf(TEXT("Cannot inherit config filename: %s has no super class"), *Class->GetName()); - } - - if (SuperClass->ClassConfigName == NAME_None) - { - FError::Throwf(TEXT("Cannot inherit config filename: parent class %s is not marked config."), *SuperClass->GetPathName()); - } - } - else - { - // otherwise, set the config name to the parsed identifier - Class->ClassConfigName = FName(*ConfigName); - } - } - else - { - // Invalidate config name if not specifically declared. - Class->ClassConfigName = NAME_None; - } -} - -void FClassDeclarationMetaData::SetAndValidateWithinClass(FClass* Class, const FClasses& AllClasses) const -{ - // Process all of the class specifiers - if (ClassWithin.IsEmpty() == false) - { - UClass* RequiredWithinClass = AllClasses.FindClass(*ClassWithin); - if (!RequiredWithinClass) - { - FError::Throwf(TEXT("Within class '%s' not found."), *ClassWithin); - } - if (RequiredWithinClass->IsChildOf(UInterface::StaticClass())) - { - FError::Throwf(TEXT("Classes cannot be 'within' interfaces")); - } - else if (Class->ClassWithin == NULL || Class->ClassWithin == UObject::StaticClass() || RequiredWithinClass->IsChildOf(Class->ClassWithin)) - { - Class->ClassWithin = RequiredWithinClass; - } - else if (Class->ClassWithin != RequiredWithinClass) - { - FError::Throwf(TEXT("%s must be within %s, not %s"), *Class->GetPathName(), *Class->ClassWithin->GetPathName(), *RequiredWithinClass->GetPathName()); - } - } - else - { - // Make sure there is a valid within - Class->ClassWithin = Class->GetSuperClass() - ? Class->GetSuperClass()->ClassWithin - : UObject::StaticClass(); - } - - UClass* ExpectedWithin = Class->GetSuperClass() - ? Class->GetSuperClass()->ClassWithin - : UObject::StaticClass(); - - if (!Class->ClassWithin->IsChildOf(ExpectedWithin)) - { - FError::Throwf(TEXT("Parent class declared within '%s'. Cannot override within class with '%s' since it isn't a child"), *ExpectedWithin->GetName(), *Class->ClassWithin->GetName()); - } -} diff --git a/Engine/Source/Programs/UHTLite/Private/ClassDeclarationMetaData.h b/Engine/Source/Programs/UHTLite/Private/ClassDeclarationMetaData.h deleted file mode 100644 index d5e3aaf96de2..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ClassDeclarationMetaData.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/NameTypes.h" -#include "Containers/Map.h" -#include "UObject/Class.h" - -class FClass; -class FClasses; -struct FPropertySpecifier; - -/** Structure that holds class meta data generated from its UCLASS declaration */ -class FClassDeclarationMetaData -{ -public: - FClassDeclarationMetaData(); - - EClassFlags ClassFlags; - TMap MetaData; - FString ClassWithin; - FString ConfigName; - - TArray HideCategories; - TArray ShowSubCatgories; - TArray HideFunctions; - TArray AutoExpandCategories; - TArray AutoCollapseCategories; - TArray DependsOn; - TArray ClassGroupNames; - TArray SparseClassDataTypes; - - /** - * Parse Class's properties to generate its declaration data. - * - * @param InClassSpecifiers Class properties collected from its UCLASS macro - * @param InRequiredAPIMacroIfPresent *_API macro if present (empty otherwise) - * @param OutClassData Parsed class meta data - */ - void ParseClassProperties(TArray&& InClassSpecifiers, const FString& InRequiredAPIMacroIfPresent); - - /** - * Merges all category properties with the class which at this point only has its parent propagated categories - * - * @param Class Class to merge categories for - */ - void MergeClassCategories(FClass* Class); - - /** - * Merges all class flags and validates them - * - * @param DeclaredClassName Name this class was declared with (for validation) - * @param PreviousClassFlags Class flags before resetting the class (for validation) - * @param Class Class to merge flags for - * @param AllClasses All known classes - */ - void MergeAndValidateClassFlags(const FString& DeclaredClassName, uint32 PreviousClassFlags, FClass* Class, const FClasses& AllClasses) const; -private: - - /** Merges all 'show' categories */ - void MergeShowCategories(); - /** Sets and validates 'within' property */ - void SetAndValidateWithinClass(FClass* Class, const FClasses& AllClasses) const; - /** Sets and validates 'ConfigName' property */ - void SetAndValidateConfigName(FClass* Class) const; - - TArray ShowCategories; - TArray ShowFunctions; - TArray DontAutoCollapseCategories; - mutable bool WantsToBePlaceable; -}; diff --git a/Engine/Source/Programs/UHTLite/Private/ClassMaps.cpp b/Engine/Source/Programs/UHTLite/Private/ClassMaps.cpp deleted file mode 100644 index e285df2fcdcc..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ClassMaps.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "ClassMaps.h" -#include "UnrealHeaderTool.h" -#include "UnrealTypeDefinitionInfo.h" - -void FClassDeclarations::AddIfMissing(FName Name, TUniqueFunction()>&& DeclConstructFunc) -{ - FRWScopeLock Lock(ClassDeclLock, SLT_Write); - if (!ClassDeclarations.Contains(Name)) - { - TSharedRef ClassDecl = DeclConstructFunc(); - ClassDeclarations.Add(Name, MoveTemp(ClassDecl)); - } -} - -FClassDeclarationMetaData* FClassDeclarations::Find(FName Name) const -{ - FRWScopeLock Lock(ClassDeclLock, SLT_ReadOnly); - if (const TSharedRef* ClassDecl = ClassDeclarations.Find(Name)) - { - return &ClassDecl->Get(); - } - return nullptr; -} - -FClassDeclarationMetaData& FClassDeclarations::FindChecked(FName Name) const -{ - FRWScopeLock Lock(ClassDeclLock, SLT_ReadOnly); - return ClassDeclarations.FindChecked(Name).Get(); -} diff --git a/Engine/Source/Programs/UHTLite/Private/ClassMaps.h b/Engine/Source/Programs/UHTLite/Private/ClassMaps.h deleted file mode 100644 index b5dae3519871..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ClassMaps.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/Stack.h" -#include "UObject/ErrorException.h" -#include "UnderlyingEnumType.h" - -#include "UnrealSourceFile.h" - -class UField; -class UClass; -class FProperty; -class UPackage; -class UEnum; -class FClassDeclarationMetaData; -class FArchive; -struct FManifestModule; -class FUnrealSourceFile; -class FUnrealTypeDefinitionInfo; - -enum class ESerializerArchiveType -{ - None, - Archive, - StructuredArchiveRecord -}; - -struct FArchiveTypeDefinePair -{ - ESerializerArchiveType ArchiveType; - FString EnclosingDefine; -}; - -// Wrapper class around TypeDefinition map so we can maintain a parallel by name map -struct FTypeDefinitionInfoMap -{ - void Add(UField* Field, TSharedRef&& Definition) - { - DefinitionsByField.Add(Field, Definition); - DefinitionsByName.Add(Field->GetFName(), MoveTemp(Definition)); - } - - bool Contains(const UField* Field) const - { - return DefinitionsByField.Contains(Field); - } - - const TSharedRef* Find(const UField* Field) const - { - return DefinitionsByField.Find(Field); - } - - const TSharedRef* FindByName(const FName Name) const - { - return DefinitionsByName.Find(Name); - } - - const TSharedRef& operator[](const UField* Field) const - { - return DefinitionsByField[Field]; - } - -private: - - TMap> DefinitionsByField; - TMap> DefinitionsByName; -}; - -// Wrapper class around ClassDeclarations map so we can control access in a threadsafe manner -struct FClassDeclarations -{ - void AddIfMissing(FName Name, TUniqueFunction()>&& DeclConstructFunc); - FClassDeclarationMetaData* Find(FName Name) const; - FClassDeclarationMetaData& FindChecked(FName Name) const; - -private: - TMap> ClassDeclarations; - - mutable FRWLock ClassDeclLock; -}; - -// Wrapper class around SourceFiles map so we can quickly get a list of source files for a given package -struct FUnrealSourceFiles -{ - void Add(FString&& Filename, TSharedRef SourceFile) - { - TSharedRef& Value = SourceFilesByString.FindOrAdd(MoveTemp(Filename), SourceFile); - if (Value != SourceFile) - { - FError::Throwf(TEXT("Duplicate filename found with different path '%s'."), *Value.Get().GetFilename()); - } - SourceFilesByPackage.FindOrAdd(SourceFile->GetPackage()).Add(&SourceFile.Get()); - } - const TSharedRef* Find(const FString& Id) const { return SourceFilesByString.Find(Id); } - const TArray* FindFilesForPackage(const UPackage* Package) const { return SourceFilesByPackage.Find(Package); } - -private: - // A map of all source files indexed by string. - TMap> SourceFilesByString; - - // The list of source files per package. Stored as raw pointer since SourceFilesByString holds shared ref. - TMap> SourceFilesByPackage; -}; - -// Wrapper class around PublicSourceFile set so we can quickly get a list of source files for a given package -struct FPublicSourceFileSet -{ - void Add(FUnrealSourceFile* SourceFile) - { - SourceFileSet.Add(SourceFile); - SourceFilesByPackage.FindOrAdd(SourceFile->GetPackage()).Add(SourceFile); - } - bool Contains(FUnrealSourceFile* SourceFile) const { return SourceFileSet.Contains(SourceFile); } - const TArray* FindFilesForPackage(const UPackage* Package) const { return SourceFilesByPackage.Find(Package); } - -private: - - // The set of all public source files. Stored as raw pointer since FUnrealSourceFiles::SourceFilesByString holds shared ref. - TSet SourceFileSet; - - // The list of public source files per package. Stored as raw pointer since FUnrealSourceFiles::SourceFilesByString holds shared ref. - TMap> SourceFilesByPackage; -}; - -/** Types access specifiers. */ -enum EAccessSpecifier -{ - ACCESS_NotAnAccessSpecifier = 0, - ACCESS_Public, - ACCESS_Private, - ACCESS_Protected, - ACCESS_Num, -}; - -inline FArchive& operator<<(FArchive& Ar, EAccessSpecifier& ObjectType) -{ - if (Ar.IsLoading()) - { - int32 Value; - Ar << Value; - ObjectType = EAccessSpecifier(Value); - } - else if (Ar.IsSaving()) - { - int32 Value = (int32)ObjectType; - Ar << Value; - } - - return Ar; -} diff --git a/Engine/Source/Programs/UHTLite/Private/Classes.cpp b/Engine/Source/Programs/UHTLite/Private/Classes.cpp deleted file mode 100644 index ce79da3f9d10..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Classes.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "Classes.h" -#include "UnrealHeaderTool.h" -#include "UObject/ErrorException.h" -#include "UObject/Package.h" -#include "Templates/Casts.h" -#include "UObject/ObjectRedirector.h" -#include "StringUtils.h" - -namespace -{ - /** - * Returns True if the given class name includes a valid Unreal prefix and matches based on the given class. - * - * @param InNameToCheck - Name w/ potential prefix to check - * @param OriginalClass - Class to check against - */ - bool ClassNameHasValidPrefix(const FString& InNameToCheck, const FClass* OriginalClass) - { - bool bIsLabledDeprecated; - 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; - } - - const FString OriginalClassName = OriginalClass->GetNameWithPrefix(); - - bool bNamesMatch = (InNameToCheck == OriginalClassName); - - if (!bNamesMatch) - { - //@TODO: UCREMOVAL: I/U interface hack - Ignoring prefixing for this call - if (OriginalClass->HasAnyClassFlags(CLASS_Interface)) - { - bNamesMatch = InNameToCheck.Mid(1) == OriginalClassName.Mid(1); - } - } - - return bNamesMatch; - } -} - -FClasses::FClasses(const TArray* Classes) - : UObjectClass((FClass*)UObject::StaticClass()) - , ClassTree(UObjectClass) -{ - if (Classes) - { - for (UClass* Class : *Classes) - { - ClassTree.AddClass(Class); - } - } -} - -FClass* FClasses::GetRootClass() const -{ - return UObjectClass; -} - -bool FClasses::IsDependentOn(const FClass* Suspect, const FClass* Source) const -{ - check(Suspect != Source); - TSet VisitedDpendencies; - return IsDependentOn(Suspect, Source, VisitedDpendencies); -} - -bool FClasses::IsDependentOn(const FClass* Suspect, const FClass* Source, TSet& VisitedDpendencies) const -{ - // Children are all implicitly dependent on their parent, that is, children require their parent - // to be compiled first therefore if the source is a parent of the suspect, the suspect is - // dependent on the source. - if (Suspect->IsChildOf(Source)) - { - return true; - } - - // Prevent circular #includes from causing infinite recursion - // Note that although it may mean there's a circular dependency somewhere, it does not - // necessarily mean it's the one we're looking for - if (VisitedDpendencies.Contains(Suspect)) - { - return false; - } - else - { - VisitedDpendencies.Add(Suspect); - } - - return false; -} - -FClass* FClasses::FindClass(const TCHAR* ClassName) const -{ - check(ClassName); - - UObject* ClassPackage = ANY_PACKAGE; - - if (UClass* Result = FindObject(ClassPackage, ClassName)) - { - return (FClass*)Result; - } - - if (UObjectRedirector* RenamedClassRedirector = FindObject(ClassPackage, ClassName)) - { - return (FClass*)CastChecked(RenamedClassRedirector->DestinationObject); - } - - return nullptr; -} - -TArray FClasses::GetDerivedClasses(FClass* Parent) const -{ - const FClassTree* ClassLeaf = ClassTree.FindNode(Parent); - TArray ChildLeaves; - ClassLeaf->GetChildClasses(ChildLeaves); - - TArray Result; - Result.Reserve(ChildLeaves.Num()); - for (const FClassTree* Node : ChildLeaves) - { - Result.Add((FClass*)Node->GetClass()); - } - - return Result; -} - -FClass* FClasses::FindAnyClass(const TCHAR* ClassName) const -{ - check(ClassName); - return (FClass*)FindObject(ANY_PACKAGE, ClassName); -} - -FClass* FClasses::FindScriptClassOrThrow(const FString& InClassName) const -{ - FString ErrorMsg; - if (FClass* Result = FindScriptClass(InClassName, &ErrorMsg)) - { - return Result; - } - - FError::Throwf(*ErrorMsg); - - // Unreachable, but compiler will warn otherwise because FError::Throwf isn't declared noreturn - return 0; -} - -FClass* FClasses::FindScriptClass(const FString& InClassName, FString* OutErrorMsg) const -{ - // Strip the class name of its prefix and then do a search for the class - FString ClassNameStripped = GetClassNameWithPrefixRemoved(InClassName); - if (FClass* FoundClass = FindClass(*ClassNameStripped)) - { - // If the class was found with the stripped class name, verify that the correct prefix was used and throw an error otherwise - if (!ClassNameHasValidPrefix(InClassName, FoundClass)) - { - if (OutErrorMsg) - { - *OutErrorMsg = FString::Printf(TEXT("Class '%s' has an incorrect prefix, expecting '%s'"), *InClassName, *FoundClass->GetNameWithPrefix()); - } - return nullptr; - } - - return (FClass*)FoundClass; - } - - // Couldn't find the class with a class name stripped of prefix (or a prefix was not found) - // See if the prefix was forgotten by trying to find the class with the given identifier - if (FClass* FoundClass = FindClass(*InClassName)) - { - // If the class was found with the given identifier, the user forgot to use the correct Unreal prefix - if (OutErrorMsg) - { - *OutErrorMsg = FString::Printf(TEXT("Class '%s' is missing a prefix, expecting '%s'"), *InClassName, *FoundClass->GetNameWithPrefix()); - } - } - else - { - // If the class was still not found, it wasn't a valid identifier - if (OutErrorMsg) - { - *OutErrorMsg = FString::Printf(TEXT("Class '%s' not found."), *InClassName); - } - } - - return nullptr; -} - -TArray FClasses::GetClassesInPackage(const UPackage* InPackage) const -{ - TArray Result; - Result.Add(UObjectClass); - - // This cast is evil, but it'll work until we get TArray covariance. ;-) - ClassTree.GetChildClasses((TArray&)Result, [=](const UClass* Class) { return InPackage == ANY_PACKAGE || Class->GetOuter() == InPackage; }, true); - - return Result; -} - -#if WIP_UHT_REFACTOR - -void FClasses::ChangeParentClass(FClass* Class) -{ - ClassTree.ChangeParentClass(Class); -} - -bool FClasses::ContainsClass(const FClass* Class) const -{ - return !!ClassTree.FindNode(const_cast(Class)); -} - -void FClasses::Validate() -{ - ClassTree.Validate(); -} - -#endif diff --git a/Engine/Source/Programs/UHTLite/Private/Classes.h b/Engine/Source/Programs/UHTLite/Private/Classes.h deleted file mode 100644 index 001535172905..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Classes.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "ParserClass.h" -#include "UObject/ClassTree.h" - -#define WIP_UHT_REFACTOR 1 - -class UPackage; -class FString; -class FClass; - -class FClasses -{ -public: - explicit FClasses(const TArray* Classes); - - /** - * Returns the root class (i.e. UObject) - * - * @return The root class. - */ - FClass* GetRootClass() const; - - /** - * Determines whether the class hierarchy rooted at Suspect is - * dependent on the hierarchy rooted at Source. - * - * @param Suspect Root of hierarchy for suspect class - * @param Source Root of hierarchy for source class - * @return true if the hierarchy rooted at Suspect is dependent on the one rooted at Source, false otherwise - */ - bool IsDependentOn(const FClass* Suspect, const FClass* Source) const; - - FClass* FindClass(const TCHAR* ClassName) const; - - TArray GetDerivedClasses(FClass* Parent) const; - - FClass* FindAnyClass(const TCHAR* ClassName) const; - - /** - * Attempts to find a script class based on the given name. Will attempt to strip - * the prefix of the given name while searching. Throws an exception with the script error - * if the class could not be found. - * - * @param InClassName Name w/ Unreal prefix to use when searching for a class - * @return The found class. - */ - FClass* FindScriptClassOrThrow(const FString& InClassName) const; - - /** - * Attempts to find a script class based on the given name. Will attempt to strip - * the prefix of the given name while searching. Optionally returns script errors when appropriate. - * - * @param InClassName Name w/ Unreal prefix to use when searching for a class - * @param OutErrorMsg Error message (if any) giving the caller flexibility in how they present an error - * @return The found class, or NULL if the class was not found. - */ - FClass* FindScriptClass(const FString& InClassName, FString* OutErrorMsg = nullptr) const; - - /** - * Returns an array of classes for the given package. - * - * @param InPackage The package to return the classes from. - * @return The classes in the specified package. - */ - TArray GetClassesInPackage(const UPackage* InPackage = ANY_PACKAGE) const; - -// Anything in here should eventually be removed when this class encapsulates its own data structure, rather than being 'poked' by the outside -#if WIP_UHT_REFACTOR - - /** - * Move a class node in the hierarchy tree after a class has changed its SuperClass - * - * @param SearchClass the class that has changed parents - * @param InNewParentClass if non-null force reparenting to this instead of the SuperClass - * - * @return true if SearchClass was successfully moved to the new location - */ - void ChangeParentClass(FClass* Class); - - bool ContainsClass(const FClass* Class) const; - - /** - * Validates the state of the tree (shouldn't be needed once this class has well-defined invariants). - */ - void Validate(); - - FORCEINLINE FClassTree& GetClassTree() - { - return ClassTree; - } - -#endif - -private: - FClass* UObjectClass; - FClassTree ClassTree; - - /** - * Determines whether the class hierarchy rooted at Suspect is dependent on the hierarchy rooted at Source. - * Used by the public overload of IsDependentOn to recursively track dependencies and handle circular references - */ - bool IsDependentOn(const FClass* Suspect, const FClass* Source, TSet& VisitedDpendencies) const; - - friend auto begin(const FClasses& Classes) -> decltype(begin(TObjectRange())) { return begin(TObjectRange()); } - friend auto end (const FClasses& Classes) -> decltype(end (TObjectRange())) { return end (TObjectRange()); } -}; diff --git a/Engine/Source/Programs/UHTLite/Private/CodeGenerator.cpp b/Engine/Source/Programs/UHTLite/Private/CodeGenerator.cpp deleted file mode 100644 index 4aa90b374d49..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/CodeGenerator.cpp +++ /dev/null @@ -1,6487 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "CoreMinimal.h" -#include "UnrealHeaderTool.h" -#include "Misc/AssertionMacros.h" -#include "HAL/PlatformProcess.h" -#include "Templates/UnrealTemplate.h" -#include "Math/UnrealMathUtility.h" -#include "Containers/UnrealString.h" -#include "UObject/NameTypes.h" -#include "Logging/LogMacros.h" -#include "CoreGlobals.h" -#include "HAL/FileManager.h" -#include "Misc/Parse.h" -#include "Misc/CoreMisc.h" -#include "Misc/CommandLine.h" -#include "Misc/FileHelper.h" -#include "Misc/Paths.h" -#include "Delegates/Delegate.h" -#include "Misc/Guid.h" -#include "Misc/ConfigCacheIni.h" -#include "Misc/FeedbackContext.h" -#include "Misc/OutputDeviceNull.h" -#include "UObject/ErrorException.h" -#include "UObject/Script.h" -#include "UObject/ObjectMacros.h" -#include "UObject/UObjectGlobals.h" -#include "UObject/Class.h" -#include "UObject/Package.h" -#include "UObject/MetaData.h" -#include "UObject/Interface.h" -#include "UObject/UnrealType.h" -#include "UObject/TextProperty.h" -#include "UObject/FieldPathProperty.h" -#include "Misc/PackageName.h" -#include "UnrealHeaderToolGlobals.h" - -#include "ParserClass.h" -#include "Scope.h" -#include "HeaderProvider.h" -#include "GeneratedCodeVersion.h" -#include "SimplifiedParsingClassInfo.h" -#include "UnrealSourceFile.h" -#include "ParserHelper.h" -#include "Classes.h" -#include "NativeClassExporter.h" -#include "ProfilingDebugging/ScopedTimers.h" -#include "HeaderParser.h" -#include "IScriptGeneratorPluginInterface.h" -#include "Manifest.h" -#include "StringUtils.h" -#include "Features/IModularFeatures.h" -#include "Algo/Copy.h" -#include "Algo/Sort.h" -#include "Algo/Reverse.h" -#include "Async/Async.h" -#if UHT_USE_PARALLEL_FOR -#include "Async/ParallelFor.h" -#endif -#include "Misc/ScopeExit.h" -#include "UnrealTypeDefinitionInfo.h" - -#include "FileLineException.h" -#include "UObject/FieldIterator.h" -#include "UObject/FieldPath.h" - -#include "UObject/WeakFieldPtr.h" -#include "Templates/SubclassOf.h" - -///////////////////////////////////////////////////// -// Globals - -FManifest GManifest; - -double GMacroizeTime = 0.0; - -TArray ChangeMessages; -bool bWriteContents = false; -bool bVerifyContents = false; - -// UHTLite NOTE: Globals! -TMap FNativeClassHeaderGenerator::GeneratedCodeHashes; -FRWLock FNativeClassHeaderGenerator::GeneratedCodeHashesLock; - -FCompilerMetadataManager GScriptHelper; - -// Array of all the temporary header async file tasks so we can ensure they have completed before issuing our timings -FGraphEventArray GAsyncFileTasks; - -bool HasIdentifierExactMatch(const TCHAR* StringBegin, const TCHAR* StringEnd, const FString& Find); - -namespace -{ - static const FName NAME_SerializeToFArchive("SerializeToFArchive"); - static const FName NAME_SerializeToFStructuredArchive("SerializeToFStructuredArchive"); - static const FName NAME_ObjectInitializerConstructorDeclared("ObjectInitializerConstructorDeclared"); - static const FName NAME_InitializeStaticSearchableValues("InitializeStaticSearchableValues"); - static const FName NAME_OverrideNativeName("OverrideNativeName"); - static const FName NAME_NoGetter("NoGetter"); - static const FName NAME_GetByRef("GetByRef"); - - static const FString STRING_StructPackage(TEXT("StructPackage")); - - static const int32 HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH = FString(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX).Len(); - - static FString AsTEXT(const FString& InStr) - { - return FString::Printf(TEXT("TEXT(\"%s\")"), *InStr); - } - - const TCHAR HeaderCopyright[] = - TEXT("// Copyright Epic Games, Inc. All Rights Reserved.\r\n") - TEXT("/*===========================================================================\r\n") - TEXT("\tGenerated code exported from UnrealHeaderTool.\r\n") - TEXT("\tDO NOT modify this manually! Edit the corresponding .h files instead!\r\n") - TEXT("===========================================================================*/\r\n") - LINE_TERMINATOR; - - const TCHAR RequiredCPPIncludes[] = TEXT("#include \"UObject/GeneratedCppIncludes.h\"") LINE_TERMINATOR; - - const TCHAR EnableDeprecationWarnings[] = TEXT("PRAGMA_ENABLE_DEPRECATION_WARNINGS") LINE_TERMINATOR; - const TCHAR DisableDeprecationWarnings[] = TEXT("PRAGMA_DISABLE_DEPRECATION_WARNINGS") LINE_TERMINATOR; - - // A struct which emits #if and #endif blocks as appropriate when invoked. - struct FMacroBlockEmitter - { - explicit FMacroBlockEmitter(FOutputDevice& InOutput, const TCHAR* InMacro) - : Output(InOutput) - , bEmittedIf(false) - , Macro(InMacro) - { - } - - ~FMacroBlockEmitter() - { - if (bEmittedIf) - { - Output.Logf(TEXT("#endif // %s\r\n"), Macro); - } - } - - void operator()(bool bInBlock) - { - if (!bEmittedIf && bInBlock) - { - Output.Logf(TEXT("#if %s\r\n"), Macro); - bEmittedIf = true; - } - else if (bEmittedIf && !bInBlock) - { - Output.Logf(TEXT("#endif // %s\r\n"), Macro); - bEmittedIf = false; - } - } - - FMacroBlockEmitter(const FMacroBlockEmitter&) = delete; - FMacroBlockEmitter& operator=(const FMacroBlockEmitter&) = delete; - - private: - FOutputDevice& Output; - bool bEmittedIf; - const TCHAR* Macro; - }; - - /** Guard that should be put at the start editor only generated code */ - const TCHAR BeginEditorOnlyGuard[] = TEXT("#if WITH_EDITOR") LINE_TERMINATOR; - - /** Guard that should be put at the end of editor only generated code */ - const TCHAR EndEditorOnlyGuard[] = TEXT("#endif //WITH_EDITOR") LINE_TERMINATOR; - - /** Whether or not the given class has any replicated properties. */ - static bool ClassHasReplicatedProperties(UClass* Class) - { - if (!Class->HasAnyClassFlags(CLASS_ReplicationDataIsSetUp)) - { - for (TFieldIterator It(Class, EFieldIteratorFlags::ExcludeSuper); It; ++It) - { - if ((It->PropertyFlags & CPF_Net) != 0) - { - return true; - } - } - } - - return Class->FirstOwnedClassRep < Class->ClassReps.Num(); - } - - static const FString STRING_GetLifetimeReplicatedPropsStr(TEXT("GetLifetimeReplicatedProps")); -} - -void FNativeClassHeaderGenerator::ExportNetData(FOutputDevice& Out, UClass* Class, const TCHAR* APIArg) const -{ - const TArray& ClassReps = Class->ClassReps; - - FUHTStringBuilder NetFieldBuilder; - NetFieldBuilder.Logf(TEXT("" - "\tenum class ENetFields_Private : uint16\r\n" - "\t{\r\n" - "\t\tNETFIELD_REP_START=(uint16)((int32)Super::ENetFields_Private::NETFIELD_REP_END + (int32)1),\r\n")); - - FUHTStringBuilder ArrayDimBuilder; - - bool bAnyStaticArrays = false; - bool bIsFirst = true; - for (int32 ClassRepIndex = Class->FirstOwnedClassRep; ClassRepIndex < ClassReps.Num(); ++ClassRepIndex) - { - const FRepRecord& ClassRep = ClassReps[ClassRepIndex]; - const FString PropertyName = ClassRep.Property->GetName(); - - if (ClassRep.Property->ArrayDim == 1) - { - if (UNLIKELY(bIsFirst)) - { - NetFieldBuilder.Logf(TEXT("\t\t%s=NETFIELD_REP_START,\r\n"), *PropertyName); - bIsFirst = false; - } - else - { - NetFieldBuilder.Logf(TEXT("\t\t%s,\r\n"), *PropertyName); - } - } - else - { - bAnyStaticArrays = true; - ArrayDimBuilder.Logf(TEXT("\t\t%s=%s,\r\n"), *PropertyName, *HeaderParser.ArrayDimensions.FindChecked(ClassReps[ClassRepIndex].Property)); - - if (UNLIKELY(bIsFirst)) - { - NetFieldBuilder.Logf(TEXT("\t\t%s_STATIC_ARRAY=NETFIELD_REP_START,\r\n"), *PropertyName); - bIsFirst = false; - } - else - { - NetFieldBuilder.Logf(TEXT("\t\t%s_STATIC_ARRAY,\r\n"), *PropertyName); - } - - NetFieldBuilder.Logf(TEXT("\t\t%s_STATIC_ARRAY_END=((uint16)%s_STATIC_ARRAY + (uint16)EArrayDims_Private::%s - (uint16)1),\r\n"), *PropertyName, *PropertyName, *PropertyName); - } - } - - const FProperty* LastProperty = ClassReps.Last().Property; - NetFieldBuilder.Logf(TEXT("\t\tNETFIELD_REP_END=%s%s"), *LastProperty->GetName(), LastProperty->ArrayDim > 1 ? TEXT("_STATIC_ARRAY_END") : TEXT("")); - - NetFieldBuilder.Log(TEXT("\t};")); - - if (bAnyStaticArrays) - { - Out.Logf(TEXT("" - "\tenum class EArrayDims_Private : uint16\r\n" - "\t{\r\n" - "%s" - "\t};\r\n"), *ArrayDimBuilder); - } - - Out.Logf(TEXT("" - "%s\r\n" // NetFields - "\t%s_API virtual void ValidateGeneratedRepEnums(const TArray& ClassReps) const override;\r\n"), - *NetFieldBuilder, - APIArg); -} - -void FNativeClassHeaderGenerator::WriteReplicatedMacroData( - const ClassDefinitionRange& ClassRange, - const TCHAR* ClassCPPName, - const TCHAR* APIArg, - FClass* Class, - FClass* SuperClass, - FOutputDevice& Writer, - const FUnrealSourceFile& SourceFile, - FNativeClassHeaderGenerator::EExportClassOutFlags& OutFlags) const -{ - const bool bHasGetLifetimeReplicatedProps = HasIdentifierExactMatch(ClassRange.Start, ClassRange.End, STRING_GetLifetimeReplicatedPropsStr); - - if (!bHasGetLifetimeReplicatedProps) - { - // Default version autogenerates declarations. - if (SourceFile.GetGeneratedCodeVersionForStruct(HeaderParser, Class) == EGeneratedCodeVersion::V1) - { - Writer.Logf(TEXT("\tvoid GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override;\r\n")); - } - else - { - FError::Throwf(TEXT("Class %s has Net flagged properties and should declare member function: void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override"), ClassCPPName); - } - } - - - ExportNetData(Writer, Class, APIArg); - - // If this class has replicated properties and it owns the first one, that means - // it's the base most replicated class. In that case, go ahead and add our interface macro. - if (Class->ClassReps.Num() > 0 && Class->FirstOwnedClassRep == 0) - { - OutFlags |= FNativeClassHeaderGenerator::EExportClassOutFlags::NeedsPushModelHeaders; - Writer.Logf(TEXT( - "private:\r\n" - "\tREPLICATED_BASE_CLASS(%s%s)\r\n" - "public:\r\n" - ), Class->GetPrefixCPP(), *Class->GetName()); - } -} - -#define BEGIN_WRAP_EDITOR_ONLY(DoWrap) DoWrap ? BeginEditorOnlyGuard : TEXT("") -#define END_WRAP_EDITOR_ONLY(DoWrap) DoWrap ? EndEditorOnlyGuard : TEXT("") - -/** - * Finds exact match of Identifier in string. Returns nullptr if none is found. - * - * @param StringBegin Start of string to search. - * @param StringEnd End of string to search. - * @param Identifier Identifier to find. - * @return Pointer to Identifier match within string. nullptr if none found. - */ -const TCHAR* FindIdentifierExactMatch(const TCHAR* StringBegin, const TCHAR* StringEnd, const FString& Identifier) -{ - int32 StringLen = UE_PTRDIFF_TO_INT32(StringEnd - StringBegin); - - // Check for exact match first. - if (FCString::Strncmp(StringBegin, *Identifier, StringLen) == 0) - { - return StringBegin; - } - - int32 FindLen = Identifier.Len(); - const TCHAR* StringToSearch = StringBegin; - - for (;;) - { - const TCHAR* IdentifierStart = FCString::Strstr(StringToSearch, *Identifier); - if (IdentifierStart == nullptr) - { - // Not found. - return nullptr; - } - - if (IdentifierStart > StringEnd || IdentifierStart + FindLen + 1 > StringEnd) - { - // Found match is out of string range. - return nullptr; - } - - if (IdentifierStart == StringBegin && !FChar::IsIdentifier(*(IdentifierStart + FindLen + 1))) - { - // Found match is at the beginning of string. - return IdentifierStart; - } - - if (IdentifierStart + FindLen == StringEnd && !FChar::IsIdentifier(*(IdentifierStart - 1))) - { - // Found match ends with end of string. - return IdentifierStart; - } - - if (!FChar::IsIdentifier(*(IdentifierStart + FindLen)) && !FChar::IsIdentifier(*(IdentifierStart - 1))) - { - // Found match is in the middle of string - return IdentifierStart; - } - - // Didn't find exact match, nor got to end of search string. Keep on searching. - StringToSearch = IdentifierStart + FindLen; - } - - // We should never get here. - checkNoEntry(); - return nullptr; -} - -/** - * Finds exact match of Identifier in string. Returns nullptr if none is found. - * - * @param String String to search. - * @param Identifier Identifier to find. - * @return Index to Identifier match within String. INDEX_NONE if none found. - */ -int32 FindIdentifierExactMatch(const FString& String, const FString& Identifier) -{ - const TCHAR* IdentifierPtr = FindIdentifierExactMatch(*String, *String + String.Len(), Identifier); - if (IdentifierPtr == nullptr) - { - return INDEX_NONE; - } - - return UE_PTRDIFF_TO_INT32(IdentifierPtr - *String); -} - -/** -* Checks if exact match of Identifier is in String. -* -* @param StringBegin Start of string to search. -* @param StringEnd End of string to search. -* @param Identifier Identifier to find. -* @return true if Identifier is within string, false otherwise. -*/ -bool HasIdentifierExactMatch(const TCHAR* StringBegin, const TCHAR* StringEnd, const FString& Find) -{ - return FindIdentifierExactMatch(StringBegin, StringEnd, Find) != nullptr; -} - -/** -* Checks if exact match of Identifier is in String. -* -* @param String String to search. -* @param Identifier Identifier to find. -* @return true if Identifier is within String, false otherwise. -*/ -bool HasIdentifierExactMatch(const FString &String, const FString& Identifier) -{ - return FindIdentifierExactMatch(String, Identifier) != INDEX_NONE; -} - -void ConvertToBuildIncludePath(const UPackage* Package, FString& LocalPath, const TMap& PackageToManifestModuleMap) -{ - FPaths::MakePathRelativeTo(LocalPath, *PackageToManifestModuleMap.FindChecked(Package)->IncludeBase); -} - -/** - * Helper function to retrieve the package manifest - * - * @param InPackage The name of the package of interest - * - * @return The manifest if found - */ -FManifestModule* GetPackageManifest(const FString& CheckPackage) -{ - // Mapping of processed packages to their locations - // An empty location string means it was processed but not found - static TMap CheckedPackageList; - - FManifestModule* ModuleInfoPtr = CheckedPackageList.FindRef(CheckPackage); - - if (!ModuleInfoPtr) - { - FManifestModule* ModuleInfoPtr2 = GManifest.Modules.FindByPredicate([&](FManifestModule& Module) { return Module.Name == CheckPackage; }); - if (ModuleInfoPtr2 && IFileManager::Get().DirectoryExists(*ModuleInfoPtr2->BaseDirectory)) - { - ModuleInfoPtr = ModuleInfoPtr2; - CheckedPackageList.Add(CheckPackage, ModuleInfoPtr); - } - } - - return ModuleInfoPtr; -} - -FString Macroize(const TCHAR* MacroName, FString&& StringToMacroize) -{ - FScopedDurationTimer Tracker(GMacroizeTime); - - FString Result(MoveTemp(StringToMacroize)); - if (Result.Len()) - { - Result.ReplaceInline(TEXT("\r\n"), TEXT("\n"), ESearchCase::CaseSensitive); - Result.ReplaceInline(TEXT("\n"), TEXT(" \\\n"), ESearchCase::CaseSensitive); - checkSlow(Result.EndsWith(TEXT(" \\\n"), ESearchCase::CaseSensitive)); - - if (Result.Len() >= 3) - { - for (int32 Index = Result.Len() - 3; Index < Result.Len(); ++Index) - { - Result[Index] = TEXT('\n'); - } - } - else - { - Result = TEXT("\n\n\n"); - } - Result.ReplaceInline(TEXT("\n"), TEXT("\r\n"), ESearchCase::CaseSensitive); - } - return FString::Printf(TEXT("#define %s%s\r\n%s"), MacroName, Result.Len() ? TEXT(" \\") : TEXT(""), *Result); -} - -void FNativeClassHeaderGenerator::AddGeneratedCodeHash(void* Field, uint32 Hash) const -{ - FRWScopeLock Lock(GeneratedCodeHashesLock, SLT_Write); - GeneratedCodeHashes.Add(Field, Hash); -} - -/** Generates a Hash tag string for the specified field */ -FString FNativeClassHeaderGenerator::GetGeneratedCodeHashTag(void* Field) const -{ - FString Tag; - bool bFoundHash = false; - uint32 Hash = 0; - - { - FRWScopeLock Lock(GeneratedCodeHashesLock, SLT_ReadOnly); - if (const uint32* FieldHash = GeneratedCodeHashes.Find(Field)) - { - bFoundHash = true; - Hash = *FieldHash; - } - } - - if (bFoundHash) - { - Tag = FString::Printf(TEXT(" // %u"), Hash); - } - return Tag; -} - -struct FParmsAndReturnProperties -{ - FParmsAndReturnProperties() - : Return(nullptr) - { - } - - bool HasParms() const - { - return Parms.Num() || Return; - } - - TArray Parms; - FProperty* Return; -}; - -/** - * Get parameters and return type for a given function. - * - * @param Function The function to get the parameters for. - * @return An aggregate containing the parameters and return type of that function. - */ -FParmsAndReturnProperties GetFunctionParmsAndReturn(UFunction* Function) -{ - FParmsAndReturnProperties Result; - for ( TFieldIterator It(Function); It; ++It) - { - FProperty* Field = *It; - - if ((It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm) - { - Result.Parms.Add(Field); - } - else if (It->PropertyFlags & CPF_ReturnParm) - { - Result.Return = Field; - } - } - return Result; -} - -/** - * Determines whether the glue version of the specified native function - * should be exported - * - * @param Function the function to check - * @return true if the glue version of the function should be exported. - */ -bool ShouldExportUFunction(UFunction* Function) -{ - // export any script stubs for native functions declared in interface classes - bool bIsBlueprintNativeEvent = (Function->FunctionFlags & FUNC_BlueprintEvent) && (Function->FunctionFlags & FUNC_Native); - if (Function->GetOwnerClass()->HasAnyClassFlags(CLASS_Interface) && !bIsBlueprintNativeEvent) - { - return true; - } - - // always export if the function is static - if (Function->FunctionFlags & FUNC_Static) - { - return true; - } - - // don't export the function if this is not the original declaration and there is - // at least one parent version of the function that is declared native - for (UFunction* ParentFunction = Function->GetSuperFunction(); ParentFunction; ParentFunction = ParentFunction->GetSuperFunction()) - { - if (ParentFunction->FunctionFlags & FUNC_Native) - { - return false; - } - } - - return true; -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -FString CreateLiteralString(const FString& Str) -{ - FString Result; - - // Have a reasonable guess at reserving the right size - Result.Reserve(Str.Len() + 8); - Result += TEXT("TEXT(\""); - - bool bPreviousCharacterWasHex = false; - - const TCHAR* Ptr = *Str; - while (TCHAR Ch = *Ptr++) - { - switch (Ch) - { - case TEXT('\r'): continue; - case TEXT('\n'): Result += TEXT("\\n"); bPreviousCharacterWasHex = false; break; - case TEXT('\\'): Result += TEXT("\\\\"); bPreviousCharacterWasHex = false; break; - case TEXT('\"'): Result += TEXT("\\\""); bPreviousCharacterWasHex = false; break; - default: - if (Ch < 31 || Ch >= 128) - { - Result += FString::Printf(TEXT("\\x%04x"), Ch); - bPreviousCharacterWasHex = true; - } - else - { - // We close and open the literal (with TEXT) here in order to ensure that successive hex characters aren't appended to the hex sequence, causing a different number - if (bPreviousCharacterWasHex && FCharWide::IsHexDigit(Ch)) - { - Result += "\")TEXT(\""; - } - bPreviousCharacterWasHex = false; - Result += Ch; - } - break; - } - } - - Result += TEXT("\")"); - return Result; -} -*/ - -FString CreateUTF8LiteralString(const FString& Str) -{ - FString Result; - - // Have a reasonable guess at reserving the right size - Result.Reserve(Str.Len() + 2); - Result += TEXT("\""); - - bool bPreviousCharacterWasHex = false; - - FTCHARToUTF8 StrUTF8(*Str); - - const char* Ptr = StrUTF8.Get(); - while (char Ch = *Ptr++) - { - switch (Ch) - { - case '\r': continue; - case '\n': Result += TEXT("\\n"); bPreviousCharacterWasHex = false; break; - case '\\': Result += TEXT("\\\\"); bPreviousCharacterWasHex = false; break; - case '\"': Result += TEXT("\\\""); bPreviousCharacterWasHex = false; break; - default: - if (Ch < 31) - { - Result += FString::Printf(TEXT("\\x%02x"), (uint8)Ch); - bPreviousCharacterWasHex = true; - } - else - { - // We close and open the literal here in order to ensure that successive hex characters aren't appended to the hex sequence, causing a different number - if (bPreviousCharacterWasHex && FCharWide::IsHexDigit(Ch)) - { - Result += "\"\""; - } - bPreviousCharacterWasHex = false; - Result += Ch; - } - break; - } - } - - Result += TEXT("\""); - return Result; -} - -TMap GenerateMetadataMapForObject(const UObject* Obj) -{ - check(Obj); - UPackage* Package = Obj->GetOutermost(); - check(Package); - UMetaData* Metadata = Package->GetMetaData(); - check(Metadata); - - TMap* PackageMap = Metadata->ObjectMetaDataMap.Find(Obj); - TMap Map; - if (PackageMap) - { - for (const TPair& MetaKeyValue : *PackageMap) - { - FString Key = MetaKeyValue.Key.ToString(); - if (!Key.StartsWith(TEXT("/Script"))) - { - Map.Add(MetaKeyValue.Key, MetaKeyValue.Value); - } - } - } - return Map; -} - -TMap GenerateMetadataMapForField(const FField* Field) -{ - TMap MetaDataMap; - const TMap* FieldMetaDataMap = Field->GetMetaDataMap(); - if (FieldMetaDataMap) - { - MetaDataMap = *FieldMetaDataMap; - } - return MetaDataMap; -} - -// Returns the METADATA_PARAMS for this output -static FString OutputMetaDataCodeForObject(FOutputDevice& OutDeclaration, FOutputDevice& Out, FFieldVariant Object, const TCHAR* MetaDataBlockName, const TCHAR* DeclSpaces, const TCHAR* Spaces) -{ - TMap MetaData; - - if (Object.IsUObject()) - { - MetaData = GenerateMetadataMapForObject(Object.ToUObject()); - } - else - { - MetaData = GenerateMetadataMapForField(Object.ToField()); - } - - FString Result; - if (MetaData.Num()) - { - typedef TKeyValuePair KVPType; - TArray KVPs; - KVPs.Reserve(MetaData.Num()); - for (TPair& KVP : MetaData) - { - KVPs.Add(KVPType(KVP.Key, &KVP.Value)); - } - - // We sort the metadata here so that we can get consistent output across multiple runs - // even when metadata is added in a different order - Algo::SortBy(KVPs, &KVPType::Key, FNameLexicalLess()); - - FString MetaDataBlockNameWithoutScope = MetaDataBlockName; - int32 ScopeIndex = MetaDataBlockNameWithoutScope.Find(TEXT("::"), ESearchCase::CaseSensitive); - if (ScopeIndex != INDEX_NONE) - { - MetaDataBlockNameWithoutScope.RightChopInline(ScopeIndex + 2, false); - } - - OutDeclaration.Log (TEXT("#if WITH_METADATA\r\n")); - OutDeclaration.Logf(TEXT("%sstatic const UE4CodeGen_Private::FMetaDataPairParam %s[];\r\n"), DeclSpaces, *MetaDataBlockNameWithoutScope); - OutDeclaration.Log (TEXT("#endif\r\n")); - - Out.Log (TEXT("#if WITH_METADATA\r\n")); - Out.Logf(TEXT("%sconst UE4CodeGen_Private::FMetaDataPairParam %s[] = {\r\n"), Spaces, MetaDataBlockName); - - for (const KVPType& KVP : KVPs) - { - Out.Logf(TEXT("%s\t{ %s, %s },\r\n"), Spaces, *CreateUTF8LiteralString(KVP.Key.ToString()), *CreateUTF8LiteralString(*KVP.Value)); - } - - Out.Logf(TEXT("%s};\r\n"), Spaces); - Out.Log (TEXT("#endif\r\n")); - - Result = FString::Printf(TEXT("METADATA_PARAMS(%s, UE_ARRAY_COUNT(%s))"), MetaDataBlockName, MetaDataBlockName); - } - else - { - Result = TEXT("METADATA_PARAMS(nullptr, 0)"); - } - - return Result; -} - -void FNativeClassHeaderGenerator::ExportProperties(FOutputDevice& Out, UStruct* Struct, int32 TextIndent) const -{ - FProperty* Previous = NULL; - FProperty* PreviousNonEditorOnly = NULL; - FProperty* LastInSuper = NULL; - UStruct* InheritanceSuper = Struct->GetInheritanceSuper(); - - // Find last property in the lowest base class that has any properties - UStruct* CurrentSuper = InheritanceSuper; - while (LastInSuper == NULL && CurrentSuper) - { - for( TFieldIterator It(CurrentSuper,EFieldIteratorFlags::ExcludeSuper); It; ++It ) - { - FProperty* Current = *It; - - // Disregard properties with 0 size like functions. - if( It.GetStruct() == CurrentSuper && Current->ElementSize ) - { - LastInSuper = Current; - } - } - // go up a layer in the hierarchy - CurrentSuper = CurrentSuper->GetSuperStruct(); - } - - FMacroBlockEmitter WithEditorOnlyData(Out, TEXT("WITH_EDITORONLY_DATA")); - - // Iterate over all properties in this struct. - for( TFieldIterator It(Struct, EFieldIteratorFlags::ExcludeSuper); It; ++It ) - { - FProperty* Current = *It; - - // Disregard properties with 0 size like functions. - if (It.GetStruct() == Struct) - { - WithEditorOnlyData(Current->IsEditorOnlyProperty()); - - // Export property specifiers - // Indent code and export CPP text. - { - FUHTStringBuilder JustPropertyDecl; - - const FString* Dim = HeaderParser.ArrayDimensions.Find(Current); - Current->ExportCppDeclaration( JustPropertyDecl, EExportedDeclaration::Member, Dim ? **Dim : NULL); - ApplyAlternatePropertyExportText(*It, JustPropertyDecl, EExportingState::TypeEraseDelegates); - - // Finish up line. - Out.Logf(TEXT("%s%s;\r\n"), FCString::Tab(TextIndent + 1), *JustPropertyDecl); - } - - LastInSuper = NULL; - Previous = Current; - if (!Current->IsEditorOnlyProperty()) - { - PreviousNonEditorOnly = Current; - } - } - } -} - -/** - * Class that is representing a type singleton. - */ -struct FTypeSingleton -{ -public: - /** Constructor */ - FTypeSingleton(FString InName, UField* InType) - : Name(MoveTemp(InName)), Type(InType) {} - - /** - * Gets this singleton's name. - */ - const FString& GetName() const - { - return Name; - } - - /** - * Gets this singleton's extern declaration. - */ - const FString& GetExternDecl() const - { - FRWScopeLock Lock(ExternDeclLock, SLT_ReadOnly); - if (ExternDecl.IsEmpty()) - { - Lock.ReleaseReadOnlyLockAndAcquireWriteLock_USE_WITH_CAUTION(); - - // Verify the decl is still empty in case another thread had also been waiting on writing this data and got the write lock first - if (ExternDecl.IsEmpty()) - { - ExternDecl = GenerateExternDecl(Type, GetName()); - } - } - - return ExternDecl; - } - -private: - /** - * Extern declaration generator. - */ - static FString GenerateExternDecl(UField* InType, const FString& InName) - { - const TCHAR* TypeStr = nullptr; - - if (InType->GetClass() == UClass::StaticClass()) - { - TypeStr = TEXT("UClass"); - } - else if (InType->GetClass() == UFunction::StaticClass() || InType->GetClass() == UDelegateFunction::StaticClass() || InType->GetClass() == USparseDelegateFunction::StaticClass()) - { - TypeStr = TEXT("UFunction"); - } - else if (InType->GetClass() == UScriptStruct::StaticClass()) - { - TypeStr = TEXT("UScriptStruct"); - } - else if (InType->GetClass() == UEnum::StaticClass()) - { - TypeStr = TEXT("UEnum"); - } - else - { - FError::Throwf(TEXT("Unsupported item type to get extern for.")); - } - - return FString::Printf( - TEXT("\t%s_API %s* %s;\r\n"), - *FPackageName::GetShortName(InType->GetOutermost()).ToUpper(), - TypeStr, - *InName - ); - } - - /** Field that stores this singleton name. */ - FString Name; - - /** Cached field that stores this singleton extern declaration. */ - mutable FString ExternDecl; - - /** Mutex to ensure 2 threads don't try to generate the extern decl at the same time. */ - mutable FRWLock ExternDeclLock; - - /** Type of the singleton */ - UField* Type; -}; - -/** - * Class that represents type singleton cache. - */ -class FTypeSingletonCache -{ -public: - /** - * Gets type singleton from cache. - * - * @param Type Singleton type. - * @param bRequiresValidObject Does it require a valid object? - */ - static const FTypeSingleton& Get(UField* Type, bool bRequiresValidObject = true) - { - FTypeSingletonCacheKey Key(Type, bRequiresValidObject); - - FRWScopeLock Lock(Mutex, SLT_ReadOnly); - TUniquePtr* SingletonPtr = CacheData.Find(Key); - if (SingletonPtr == nullptr) - { - TUniquePtr TypeSingleton(MakeUnique(GenerateSingletonName(Type, bRequiresValidObject), Type)); - - Lock.ReleaseReadOnlyLockAndAcquireWriteLock_USE_WITH_CAUTION(); - - // Check the map again in case another thread had also been waiting on writing this data and got the write lock first - SingletonPtr = CacheData.Find(Key); - if (SingletonPtr == nullptr) - { - SingletonPtr = &CacheData.Add(Key, MoveTemp(TypeSingleton)); - } - } - - return **SingletonPtr; - } - -private: - /** - * Private type that represents cache map key. - */ - struct FTypeSingletonCacheKey - { - /** FTypeSingleton type */ - UField* Type; - - /** If this type singleton requires valid object. */ - bool bRequiresValidObject; - - /* Constructor */ - FTypeSingletonCacheKey(UField* InType, bool bInRequiresValidObject) - : Type(InType), bRequiresValidObject(bInRequiresValidObject) - {} - - /** - * Equality operator. - * - * @param Other Other key. - * - * @returns True if this is equal to Other. False otherwise. - */ - bool operator==(const FTypeSingletonCacheKey& Other) const - { - return Type == Other.Type && bRequiresValidObject == Other.bRequiresValidObject; - } - - /** - * Gets hash value for this object. - */ - friend uint32 GetTypeHash(const FTypeSingletonCacheKey& Object) - { - return HashCombine( - GetTypeHash(Object.Type), - GetTypeHash(Object.bRequiresValidObject) - ); - } - }; - - /** - * Generates singleton name. - */ - static FString GenerateSingletonName(UField* Item, bool bRequiresValidObject) - { - check(Item); - - bool bNoRegister = false; - if (UClass* ItemClass = Cast(Item)) - { - if (!bRequiresValidObject && !ItemClass->HasAllClassFlags(CLASS_Intrinsic)) - { - bNoRegister = true; - } - } - - const TCHAR* Suffix = (bNoRegister ? TEXT("_NoRegister") : TEXT("")); - - FString Result; - for (UObject* Outer = Item; Outer; Outer = Outer->GetOuter()) - { - if (!Result.IsEmpty()) - { - Result = TEXT("_") + Result; - } - - if (Cast(Outer) || Cast(Outer)) - { - Result = FNameLookupCPP::GetNameCPP(Cast(Outer)) + Result; - - // Structs can also have UPackage outer. - if (Cast(Outer) || Cast(Outer->GetOuter())) - { - break; - } - } - else - { - Result = Outer->GetName() + Result; - } - } - - // Can't use long package names in function names. - if (Result.StartsWith(TEXT("/Script/"), ESearchCase::CaseSensitive)) - { - Result = FPackageName::GetShortName(Result); - } - - const FString ClassString = FNameLookupCPP::GetNameCPP(Item->GetClass()); - return FString::Printf(TEXT("Z_Construct_%s_%s%s()"), *ClassString, *Result, Suffix); - } - - static TMap> CacheData; - static FRWLock Mutex; -}; - -TMap> FTypeSingletonCache::CacheData; -FRWLock FTypeSingletonCache::Mutex; - -const FString& FNativeClassHeaderGenerator::GetSingletonName(UField* Item, TSet* UniqueCrossModuleReferences, bool bRequiresValidObject) -{ - const FTypeSingleton& Cache = FTypeSingletonCache::Get(Item, bRequiresValidObject); - - // We don't need to export UFunction externs, though we may need the externs for UDelegateFunctions - if (UniqueCrossModuleReferences && (!Item->IsA() || Item->IsA())) - { - UniqueCrossModuleReferences->Add(Cache.GetExternDecl()); - } - - return Cache.GetName(); -} - -FString FNativeClassHeaderGenerator::GetSingletonNameFuncAddr(UField* Item, TSet* UniqueCrossModuleReferences, bool bRequiresValidObject) -{ - FString Result; - if (!Item) - { - Result = TEXT("nullptr"); - } - else - { - Result = GetSingletonName(Item, UniqueCrossModuleReferences, bRequiresValidObject).LeftChop(2); - } - return Result; -} - -void FNativeClassHeaderGenerator::PropertyNew(FOutputDevice& DeclOut, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, FProperty* Prop, const TCHAR* OffsetStr, const TCHAR* Name, const TCHAR* DeclSpaces, const TCHAR* Spaces, const TCHAR* SourceStruct) const -{ - FString PropName = CreateUTF8LiteralString(FNativeClassHeaderGenerator::GetOverriddenName(Prop)); - FString PropNameDep = Prop->HasAllPropertyFlags(CPF_Deprecated) ? Prop->GetName() + TEXT("_DEPRECATED") : Prop->GetName(); - const TCHAR* FPropertyObjectFlags = FClass::IsOwnedByDynamicType(Prop) ? TEXT("RF_Public|RF_Transient") : TEXT("RF_Public|RF_Transient|RF_MarkAsNative"); - EPropertyFlags PropFlags = Prop->PropertyFlags & ~CPF_ComputedFlags; - - FString PropTag = GetGeneratedCodeHashTag(Prop); - FString PropNotifyFunc = (Prop->RepNotifyFunc != NAME_None) ? CreateUTF8LiteralString(*Prop->RepNotifyFunc.ToString()) : TEXT("nullptr"); - - FString ArrayDim = (Prop->ArrayDim != 1) ? FString::Printf(TEXT("CPP_ARRAY_DIM(%s, %s)"), *PropNameDep, SourceStruct) : TEXT("1"); - - FString MetaDataParams = OutputMetaDataCodeForObject(DeclOut, Out, Prop, *FString::Printf(TEXT("%s_MetaData"), Name), DeclSpaces, Spaces); - - FString NameWithoutScope = Name; - FString Scope; - int32 ScopeIndex = NameWithoutScope.Find(TEXT("::"), ESearchCase::CaseSensitive); - if (ScopeIndex != INDEX_NONE) - { - Scope = NameWithoutScope.Left(ScopeIndex) + TEXT("_"); - NameWithoutScope.RightChopInline(ScopeIndex + 2, false); - } - - if (FByteProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FBytePropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FBytePropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Byte, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->Enum, OutReferenceGatherers.UniqueCrossModuleReferences), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FInt8Property* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FInt8PropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FInt8PropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Int8, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FInt16Property* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FInt16PropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FInt16PropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Int16, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FIntProperty* TypedProp = CastField(Prop)) - { - const TCHAR* PropTypeName = HeaderParser.UnsizedProperties.Contains(TypedProp) ? TEXT("FUnsizedIntPropertyParams") : TEXT("FIntPropertyParams"); - - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::%s %s;\r\n"), DeclSpaces, PropTypeName, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::%s %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Int, %s, %s, %s, %s };%s\r\n"), - Spaces, - PropTypeName, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FInt64Property* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FInt64PropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FInt64PropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Int64, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FUInt16Property* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FFInt16PropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FFInt16PropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::UInt16, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FUInt32Property* TypedProp = CastField(Prop)) - { - const TCHAR* PropTypeName = HeaderParser.UnsizedProperties.Contains(TypedProp) ? TEXT("FUnsizedFIntPropertyParams") : TEXT("FUInt32PropertyParams"); - - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::%s %s;\r\n"), DeclSpaces, PropTypeName, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::%s %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::UInt32, %s, %s, %s, %s };%s\r\n"), - Spaces, - PropTypeName, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FUInt64Property* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FFInt64PropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FFInt64PropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::UInt64, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FFloatProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FFloatPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FFloatPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Float, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FDoubleProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FDoublePropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FDoublePropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Double, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FBoolProperty* TypedProp = CastField(Prop)) - { - FString OuterSize; - FString Setter; - if (!Prop->GetOwner()) - { - OuterSize = TEXT("0"); - Setter = TEXT("nullptr"); - } - else - { - OuterSize = FString::Printf(TEXT("sizeof(%s)"), SourceStruct); - - DeclOut.Logf(TEXT("%sstatic void %s_SetBit(void* Obj);\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf(TEXT("%svoid %s_SetBit(void* Obj)\r\n"), Spaces, Name); - Out.Logf(TEXT("%s{\r\n"), Spaces); - Out.Logf(TEXT("%s\t((%s*)Obj)->%s%s = 1;\r\n"), Spaces, SourceStruct, *Prop->GetName(), Prop->HasAllPropertyFlags(CPF_Deprecated) ? TEXT("_DEPRECATED") : TEXT("")); - Out.Logf(TEXT("%s}\r\n"), Spaces); - - Setter = FString::Printf(TEXT("&%s_SetBit"), Name); - } - - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FBoolPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FBoolPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Bool %s, %s, %s, sizeof(%s), %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - TypedProp->IsNativeBool() ? TEXT("| UE4CodeGen_Private::EPropertyGenFlags::NativeBool") : TEXT(""), - FPropertyObjectFlags, - *ArrayDim, - *TypedProp->GetCPPType(nullptr, 0), - *OuterSize, - *Setter, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FSoftClassProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FSoftClassPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FSoftClassPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::SoftClass, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->MetaClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FWeakObjectProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FWeakObjectPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FWeakObjectPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::WeakObject, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->PropertyClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FLazyObjectProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FLazyObjectPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FLazyObjectPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::LazyObject, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->PropertyClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FSoftObjectProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FSoftObjectPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FSoftObjectPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::SoftObject, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->PropertyClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FClassProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FClassPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FClassPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Class, %s, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->MetaClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *GetSingletonNameFuncAddr(TypedProp->PropertyClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FObjectProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FObjectPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FObjectPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Object, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->PropertyClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FInterfaceProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FInterfacePropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FInterfacePropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Interface, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->InterfaceClass, OutReferenceGatherers.UniqueCrossModuleReferences, false), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FNameProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FNamePropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FNamePropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Name, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FStrProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FStrPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FStrPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Str, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FArrayProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FArrayPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FArrayPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Array, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - HeaderParser.PropertyUsesMemoryImageAllocator.Contains(TypedProp) ? TEXT("EArrayPropertyFlags::UsesMemoryImageAllocator") : TEXT("EArrayPropertyFlags::None"), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FMapProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FMapPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FMapPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Map, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - HeaderParser.PropertyUsesMemoryImageAllocator.Contains(TypedProp) ? TEXT("EMapPropertyFlags::UsesMemoryImageAllocator") : TEXT("EMapPropertyFlags::None"), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FSetProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FSetPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FSetPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Set, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FStructProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FStructPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FStructPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Struct, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->Struct, OutReferenceGatherers.UniqueCrossModuleReferences), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FDelegateProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FDelegatePropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FDelegatePropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Delegate, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->SignatureFunction, OutReferenceGatherers.UniqueCrossModuleReferences), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FMulticastDelegateProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FMulticastDelegatePropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FMulticastDelegatePropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::%sMulticastDelegate, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - (TypedProp->IsA() ? TEXT("Inline") : TEXT("Sparse")), - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->SignatureFunction, OutReferenceGatherers.UniqueCrossModuleReferences), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FTextProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FTextPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FTextPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Text, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FEnumProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FEnumPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FEnumPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::Enum, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *GetSingletonNameFuncAddr(TypedProp->Enum, OutReferenceGatherers.UniqueCrossModuleReferences), - *MetaDataParams, - *PropTag - ); - - return; - } - - if (FFieldPathProperty* TypedProp = CastField(Prop)) - { - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FFieldPathPropertyParams %s;\r\n"), DeclSpaces, *NameWithoutScope); - - Out.Logf( - TEXT("%sconst UE4CodeGen_Private::FFieldPathPropertyParams %s = { %s, %s, (EPropertyFlags)0x%016llx, UE4CodeGen_Private::EPropertyGenFlags::FieldPath, %s, %s, %s, %s, %s };%s\r\n"), - Spaces, - Name, - *PropName, - *PropNotifyFunc, - PropFlags, - FPropertyObjectFlags, - *ArrayDim, - OffsetStr, - *FString::Printf(TEXT("&F%s::StaticClass"), *TypedProp->PropertyClass->GetName()), - *MetaDataParams, - *PropTag - ); - - return; - } - - // Unhandled type - check(false); -} - -bool IsEditorOnlyDataProperty(FProperty* Prop) -{ - while (Prop) - { - if (Prop->IsEditorOnlyProperty()) - { - return true; - } - - Prop = Prop->GetOwner(); - } - - return false; -} - -TTuple FNativeClassHeaderGenerator::OutputProperties(FOutputDevice& DeclOut, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const TCHAR* Scope, const TArray& Properties, const TCHAR* DeclSpaces, const TCHAR* Spaces) const -{ - if (Properties.Num() == 0) - { - return TTuple(TEXT("nullptr"), TEXT("0")); - } - - TArray PropertyNamesAndPointers; - bool bHasAllEditorOnlyDataProperties = true; - - { - FMacroBlockEmitter WithEditorOnlyMacroEmitter(Out, TEXT("WITH_EDITORONLY_DATA")); - FMacroBlockEmitter WithEditorOnlyMacroEmitterDecl(DeclOut, TEXT("WITH_EDITORONLY_DATA")); - - for (FProperty* Prop : Properties) - { - bool bRequiresHasEditorOnlyMacro = IsEditorOnlyDataProperty(Prop); - if (!bRequiresHasEditorOnlyMacro) - { - bHasAllEditorOnlyDataProperties = false; - } - - WithEditorOnlyMacroEmitter(bRequiresHasEditorOnlyMacro); - WithEditorOnlyMacroEmitterDecl(bRequiresHasEditorOnlyMacro); - OutputProperty(DeclOut, Out, OutReferenceGatherers, Scope, PropertyNamesAndPointers, Prop, DeclSpaces, Spaces); - } - - WithEditorOnlyMacroEmitter(bHasAllEditorOnlyDataProperties); - WithEditorOnlyMacroEmitterDecl(bHasAllEditorOnlyDataProperties); - DeclOut.Logf(TEXT("%sstatic const UE4CodeGen_Private::FPropertyParamsBase* const PropPointers[];\r\n"), DeclSpaces); - Out.Logf(TEXT("%sconst UE4CodeGen_Private::FPropertyParamsBase* const %sPropPointers[] = {\r\n"), Spaces, Scope); - - for (const FPropertyNamePointerPair& PropNameAndPtr : PropertyNamesAndPointers) - { - bool bRequiresHasEditorOnlyMacro = IsEditorOnlyDataProperty(PropNameAndPtr.Prop); - - WithEditorOnlyMacroEmitter(bRequiresHasEditorOnlyMacro); - WithEditorOnlyMacroEmitterDecl(bRequiresHasEditorOnlyMacro); - Out.Logf(TEXT("%s\t(const UE4CodeGen_Private::FPropertyParamsBase*)&%s,\r\n"), Spaces, *PropNameAndPtr.Name); - } - - WithEditorOnlyMacroEmitter(bHasAllEditorOnlyDataProperties); - WithEditorOnlyMacroEmitterDecl(bHasAllEditorOnlyDataProperties); - Out.Logf(TEXT("%s};\r\n"), Spaces); - } - - if (bHasAllEditorOnlyDataProperties) - { - return TTuple( - FString::Printf(TEXT("IF_WITH_EDITORONLY_DATA(%sPropPointers, nullptr)"), Scope), - FString::Printf(TEXT("IF_WITH_EDITORONLY_DATA(UE_ARRAY_COUNT(%sPropPointers), 0)"), Scope) - ); - } - else - { - return TTuple( - FString::Printf(TEXT("%sPropPointers"), Scope), - FString::Printf(TEXT("UE_ARRAY_COUNT(%sPropPointers)"), Scope) - ); - } -} - -inline FString GetEventStructParamsName(UObject* Outer, const TCHAR* FunctionName) -{ - FString OuterName; - if (Outer->IsA()) - { - OuterName = ((UClass*)Outer)->GetName(); - } - else if (Outer->IsA()) - { - OuterName = ((UPackage*)Outer)->GetName(); - OuterName.ReplaceInline(TEXT("/"), TEXT("_"), ESearchCase::CaseSensitive); - } - else - { - FError::Throwf(TEXT("Unrecognized outer type")); - } - - FString Result = FString::Printf(TEXT("%s_event%s_Parms"), *OuterName, FunctionName); - if (Result.Len() && FChar::IsDigit(Result[0])) - { - Result.InsertAt(0, TCHAR('_')); - } - return Result; -} - -void FNativeClassHeaderGenerator::OutputProperty(FOutputDevice& DeclOut, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const TCHAR* Scope, TArray& PropertyNamesAndPointers, FProperty* Prop, const TCHAR* DeclSpaces, const TCHAR* Spaces) const -{ - // Helper to handle the creation of the underlying properties if they're enum properties - auto HandleUnderlyingEnumProperty = [this, &PropertyNamesAndPointers, &DeclOut, &Out, &OutReferenceGatherers, DeclSpaces, Spaces](FProperty* LocalProp, FString&& InOuterName) - { - if (FEnumProperty* EnumProp = CastField(LocalProp)) - { - FString PropVarName = InOuterName + TEXT("_Underlying"); - - PropertyNew(DeclOut, Out, OutReferenceGatherers, EnumProp->UnderlyingProp, TEXT("0"), *PropVarName, DeclSpaces, Spaces); - PropertyNamesAndPointers.Emplace(MoveTemp(PropVarName), EnumProp->UnderlyingProp); - } - - PropertyNamesAndPointers.Emplace(MoveTemp(InOuterName), LocalProp); - }; - - if (FArrayProperty* ArrayProperty = CastField(Prop)) - { - FString InnerVariableName = FString::Printf(TEXT("%sNewProp_%s_Inner"), Scope, *ArrayProperty->Inner->GetName()); - - HandleUnderlyingEnumProperty(ArrayProperty->Inner, CopyTemp(InnerVariableName)); - PropertyNew(DeclOut, Out, OutReferenceGatherers, ArrayProperty->Inner, TEXT("0"), *InnerVariableName, DeclSpaces, Spaces); - } - - else if (FMapProperty* MapProperty = CastField(Prop)) - { - FProperty* Key = MapProperty->KeyProp; - FProperty* Value = MapProperty->ValueProp; - - FString KeyVariableName = FString::Printf(TEXT("%sNewProp_%s_KeyProp"), Scope, *Key->GetName()); - FString ValueVariableName = FString::Printf(TEXT("%sNewProp_%s_ValueProp"), Scope, *Value->GetName()); - - HandleUnderlyingEnumProperty(Value, CopyTemp(ValueVariableName)); - PropertyNew(DeclOut, Out, OutReferenceGatherers, Value, TEXT("1"), *ValueVariableName, DeclSpaces, Spaces); - - HandleUnderlyingEnumProperty(Key, CopyTemp(KeyVariableName)); - PropertyNew(DeclOut, Out, OutReferenceGatherers, Key, TEXT("0"), *KeyVariableName, DeclSpaces, Spaces); - } - - else if (FSetProperty* SetProperty = CastField(Prop)) - { - FProperty* Inner = SetProperty->ElementProp; - - FString ElementVariableName = FString::Printf(TEXT("%sNewProp_%s_ElementProp"), Scope, *Inner->GetName()); - - HandleUnderlyingEnumProperty(Inner, CopyTemp(ElementVariableName)); - PropertyNew(DeclOut, Out, OutReferenceGatherers, Inner, TEXT("0"), *ElementVariableName, DeclSpaces, Spaces); - } - - { - FString SourceStruct; - if (UFunction* Function = Prop->GetOwner()) - { - while (Function->GetSuperFunction()) - { - Function = Function->GetSuperFunction(); - } - FString FunctionName = Function->GetName(); - if (Function->HasAnyFunctionFlags(FUNC_Delegate)) - { - FunctionName.LeftChopInline(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH, false); - } - - SourceStruct = GetEventStructParamsName(Function->GetOuter(), *FunctionName); - } - else - { - SourceStruct = FNameLookupCPP::GetNameCPP(CastChecked(Prop->GetOwner())); - } - - FString PropName = Prop->GetName(); - FString PropVariableName = FString::Printf(TEXT("%sNewProp_%s"), Scope, *PropName); - - if (Prop->HasAllPropertyFlags(CPF_Deprecated)) - { - PropName += TEXT("_DEPRECATED"); - } - - FString PropMacroOuterClass = FString::Printf(TEXT("STRUCT_OFFSET(%s, %s)"), *SourceStruct, *PropName); - - HandleUnderlyingEnumProperty(Prop, CopyTemp(PropVariableName)); - PropertyNew(DeclOut, Out, OutReferenceGatherers, Prop, *PropMacroOuterClass, *PropVariableName, DeclSpaces, Spaces, *SourceStruct); - } -} - -static bool IsAlwaysAccessible(UScriptStruct* Script) -{ - FName ToTest = Script->GetFName(); - if (ToTest == NAME_Matrix) - { - return false; // special case, the C++ FMatrix does not have the same members. - } - bool Result = Script->HasDefaults(); // if we have cpp struct ops in it for UHT, then we can assume it is always accessible - if( ToTest == NAME_Plane - || ToTest == NAME_Vector - || ToTest == NAME_Vector4 - || ToTest == NAME_Quat - || ToTest == NAME_Color - ) - { - check(Result); - } - return Result; -} - -static void FindNoExportStructsRecursive(TArray& Structs, UStruct* Start) -{ - while (Start) - { - if (UScriptStruct* StartScript = Cast(Start)) - { - if (StartScript->StructFlags & STRUCT_Native) - { - break; - } - - if (!IsAlwaysAccessible(StartScript)) // these are a special cases that already exists and if wrong if exported naively - { - // this will topologically sort them in reverse order - Structs.Remove(StartScript); - Structs.Add(StartScript); - } - } - - for (FProperty* Prop : TFieldRange(Start, EFieldIteratorFlags::ExcludeSuper)) - { - if (FStructProperty* StructProp = CastField(Prop)) - { - FindNoExportStructsRecursive(Structs, StructProp->Struct); - } - else if (FArrayProperty* ArrayProp = CastField(Prop)) - { - if (FStructProperty* InnerStructProp = CastField(ArrayProp->Inner)) - { - FindNoExportStructsRecursive(Structs, InnerStructProp->Struct); - } - } - else if (FMapProperty* MapProp = CastField(Prop)) - { - if (FStructProperty* KeyStructProp = CastField(MapProp->KeyProp)) - { - FindNoExportStructsRecursive(Structs, KeyStructProp->Struct); - } - if (FStructProperty* ValueStructProp = CastField(MapProp->ValueProp)) - { - FindNoExportStructsRecursive(Structs, ValueStructProp->Struct); - } - } - else if (FSetProperty* SetProp = CastField(Prop)) - { - if (FStructProperty* ElementStructProp = CastField(SetProp->ElementProp)) - { - FindNoExportStructsRecursive(Structs, ElementStructProp->Struct); - } - } - } - Start = Start->GetSuperStruct(); - } -} - -static TArray FindNoExportStructs(UStruct* Start) -{ - TArray Result; - FindNoExportStructsRecursive(Result, Start); - - // These come out in reverse order of topology so reverse them - Algo::Reverse(Result); - - return Result; -} - -struct FPackageSingletonStrings -{ - FPackageSingletonStrings(FString&& InPackageSingletonName) - : PackageSingletonName(MoveTemp(InPackageSingletonName)) - , PackageUniqueCrossModuleReference(FString::Printf(TEXT("\tUPackage* %s;\r\n"), *PackageSingletonName)) - { - } - - FString PackageSingletonName; - FString PackageUniqueCrossModuleReference; -}; - -static TMap> PackageSingletonNames; -static FRWLock PackageSingletonNamesLock; - -const FString& FNativeClassHeaderGenerator::GetPackageSingletonName(const UPackage* InPackage, TSet* UniqueCrossModuleReferences) -{ - FRWScopeLock Lock(PackageSingletonNamesLock, SLT_ReadOnly); - - TUniquePtr* PackageSingletonStrings = PackageSingletonNames.Find(InPackage); - if (PackageSingletonStrings == nullptr) - { - FString PackageName = InPackage->GetName(); - PackageName.ReplaceInline(TEXT("/"), TEXT("_"), ESearchCase::CaseSensitive); - - TUniquePtr NewPackageSingletonStrings(MakeUnique(FString::Printf(TEXT("Z_Construct_UPackage_%s()"), *PackageName))); - - Lock.ReleaseReadOnlyLockAndAcquireWriteLock_USE_WITH_CAUTION(); - - // Check the map again in case another thread had also been waiting on writing this data and got the write lock first - PackageSingletonStrings = PackageSingletonNames.Find(InPackage); - - if (PackageSingletonStrings == nullptr) - { - PackageSingletonStrings = &PackageSingletonNames.Add(InPackage, MoveTemp(NewPackageSingletonStrings)); - } - } - - if (UniqueCrossModuleReferences) - { - UniqueCrossModuleReferences->Add((*PackageSingletonStrings)->PackageUniqueCrossModuleReference); - } - - return (*PackageSingletonStrings)->PackageSingletonName; -} - -void FNativeClassHeaderGenerator::ExportGeneratedPackageInitCode(FOutputDevice& Out, const TCHAR* InDeclarations, const UPackage* InPackage, uint32 Hash, TMap>& PackageSingletons) -{ - const FString& SingletonName = GetPackageSingletonName(InPackage, nullptr); - - TArray* SingletonsToOutput = PackageSingletons.Find(InPackage); - if (SingletonsToOutput) - { - Algo::Sort(*SingletonsToOutput, [](UField* A, UField* B) - { - // Structs before delegates then UniqueId order - return (uint64(A->IsA()) << 32) + A->GetUniqueID() < - (uint64(B->IsA()) << 32) + B->GetUniqueID(); - }); - - for (UField* ScriptType : *SingletonsToOutput) - { - Out.Log(FTypeSingletonCache::Get(ScriptType, true).GetExternDecl()); - } - } - - FOutputDeviceNull OutputDeviceNull; - FString MetaDataParams = OutputMetaDataCodeForObject(OutputDeviceNull, Out, const_cast(InPackage), TEXT("Package_MetaDataParams"), TEXT(""), TEXT("\t\t\t")); - - Out.Logf(TEXT("\tUPackage* %s\r\n"), *SingletonName); - Out.Logf(TEXT("\t{\r\n")); - Out.Logf(TEXT("\t\tstatic UPackage* ReturnPackage = nullptr;\r\n")); - Out.Logf(TEXT("\t\tif (!ReturnPackage)\r\n")); - Out.Logf(TEXT("\t\t{\r\n")); - - const TCHAR* SingletonArray; - const TCHAR* SingletonCount; - if (SingletonsToOutput) - { - Out.Logf(TEXT("\t\t\tstatic UObject* (*const SingletonFuncArray[])() = {\r\n")); - for (UField* ScriptType : *SingletonsToOutput) - { - const FString Name = FTypeSingletonCache::Get(ScriptType, true).GetName().LeftChop(2); - - Out.Logf(TEXT("\t\t\t\t(UObject* (*)())%s,\r\n"), *Name); - } - Out.Logf(TEXT("\t\t\t};\r\n")); - - SingletonArray = TEXT("SingletonFuncArray"); - SingletonCount = TEXT("UE_ARRAY_COUNT(SingletonFuncArray)"); - } - else - { - SingletonArray = TEXT("nullptr"); - SingletonCount = TEXT("0"); - } - - Out.Logf(TEXT("\t\t\tstatic const UE4CodeGen_Private::FPackageParams PackageParams = {\r\n")); - Out.Logf(TEXT("\t\t\t\t%s,\r\n"), *CreateUTF8LiteralString(InPackage->GetName())); - Out.Logf(TEXT("\t\t\t\t%s,\r\n"), SingletonArray); - Out.Logf(TEXT("\t\t\t\t%s,\r\n"), SingletonCount); - Out.Logf(TEXT("\t\t\t\tPKG_CompiledIn | 0x%08X,\r\n"), InPackage->GetPackageFlags() & (PKG_ClientOptional | PKG_ServerSideOnly | PKG_EditorOnly | PKG_Developer | PKG_UncookedOnly)); - Out.Logf(TEXT("\t\t\t\t0x%08X,\r\n"), Hash); - Out.Logf(TEXT("\t\t\t\t0x%08X,\r\n"), GenerateTextHash(InDeclarations)); - Out.Logf(TEXT("\t\t\t\t%s\r\n"), *MetaDataParams); - Out.Logf(TEXT("\t\t\t};\r\n")); - Out.Logf(TEXT("\t\t\tUE4CodeGen_Private::ConstructUPackage(ReturnPackage, PackageParams);\r\n")); - Out.Logf(TEXT("\t\t}\r\n")); - Out.Logf(TEXT("\t\treturn ReturnPackage;\r\n")); - Out.Logf(TEXT("\t}\r\n")); -} - -void FNativeClassHeaderGenerator::ExportNativeGeneratedInitCode(FOutputDevice& Out, FOutputDevice& OutDeclarations, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, FClass* Class, FUHTStringBuilder& OutFriendText) const -{ - check(!OutFriendText.Len()); - - UE_CLOG(Class->ClassGeneratedBy, LogCompile, Fatal, TEXT("For intrinsic and compiled-in classes, ClassGeneratedBy should always be null")); - - const bool bIsNoExport = Class->HasAnyClassFlags(CLASS_NoExport); - const bool bIsDynamic = FClass::IsDynamic(Class); - const FString ClassNameCPP = FNameLookupCPP::GetNameCPP(Class); - - const FString& ApiString = GetAPIString(); - - TSet AlreadyIncludedNames; - TArray FunctionsToExport; - bool bAllEditorOnlyFunctions = true; - for (UFunction* LocalFunc : TFieldRange(Class,EFieldIteratorFlags::ExcludeSuper)) - { - FName TrueName = FNativeClassHeaderGenerator::GetOverriddenFName(LocalFunc); - bool bAlreadyIncluded = false; - AlreadyIncludedNames.Add(TrueName, &bAlreadyIncluded); - if (bAlreadyIncluded) - { - // In a dynamic class the same function signature may be used for a Multi- and a Single-cast delegate. - if (!LocalFunc->IsA() || !bIsDynamic) - { - FError::Throwf(TEXT("The same function linked twice. Function: %s Class: %s"), *LocalFunc->GetName(), *Class->GetName()); - } - continue; - } - if (!LocalFunc->IsA()) - { - bAllEditorOnlyFunctions &= LocalFunc->HasAnyFunctionFlags(FUNC_EditorOnly); - } - FunctionsToExport.Add(LocalFunc); - } - - // Sort the list of functions - FunctionsToExport.Sort(); - - FUHTStringBuilder GeneratedClassRegisterFunctionText; - - // The class itself. - { - // simple ::StaticClass wrapper to avoid header, link and DLL hell - { - const FString& SingletonNameNoRegister = GetSingletonName(Class, OutReferenceGatherers.UniqueCrossModuleReferences, false); - - OutDeclarations.Log(FTypeSingletonCache::Get(Class, false).GetExternDecl()); - - GeneratedClassRegisterFunctionText.Logf(TEXT("\tUClass* %s\r\n"), *SingletonNameNoRegister); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t{\r\n")); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\treturn %s::StaticClass();\r\n"), *ClassNameCPP); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t}\r\n")); - } - const FString& SingletonName = GetSingletonName(Class, OutReferenceGatherers.UniqueCrossModuleReferences); - - FString StaticsStructName = SingletonName.LeftChop(2) + TEXT("_Statics"); - - OutFriendText.Logf(TEXT("\tfriend struct %s;\r\n"), *StaticsStructName); - OutDeclarations.Log(FTypeSingletonCache::Get(Class).GetExternDecl()); - - GeneratedClassRegisterFunctionText.Logf(TEXT("\tstruct %s\r\n"), *StaticsStructName); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t{\r\n")); - - FUHTStringBuilder StaticDefinitions; - - FUHTStringBuilder Singletons; - UClass* SuperClass = Class->GetSuperClass(); - if (SuperClass && SuperClass != Class) - { - OutDeclarations.Log(FTypeSingletonCache::Get(SuperClass).GetExternDecl()); - Singletons.Logf(TEXT("\t\t(UObject* (*)())%s,\r\n"), *GetSingletonName(SuperClass, OutReferenceGatherers.UniqueCrossModuleReferences).LeftChop(2)); - } - if (!bIsDynamic) - { - const FString& PackageSingletonName = GetPackageSingletonName(Class->GetOutermost(), OutReferenceGatherers.UniqueCrossModuleReferences); - - OutDeclarations.Logf(TEXT("\t%s_API UPackage* %s;\r\n"), *ApiString, *PackageSingletonName); - Singletons.Logf(TEXT("\t\t(UObject* (*)())%s,\r\n"), *PackageSingletonName.LeftChop(2)); - } - - const TCHAR* SingletonsArray; - const TCHAR* SingletonsCount; - if (Singletons.Len() != 0) - { - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tstatic UObject* (*const DependentSingletons[])();\r\n")); - - StaticDefinitions.Logf(TEXT("\tUObject* (*const %s::DependentSingletons[])() = {\r\n"), *StaticsStructName); - StaticDefinitions.Log (*Singletons); - StaticDefinitions.Logf(TEXT("\t};\r\n")); - - SingletonsArray = TEXT("DependentSingletons"); - SingletonsCount = TEXT("UE_ARRAY_COUNT(DependentSingletons)"); - } - else - { - SingletonsArray = TEXT("nullptr"); - SingletonsCount = TEXT("0"); - } - - const TCHAR* FunctionsArray; - const TCHAR* FunctionsCount; - if (FunctionsToExport.Num() != 0) - { - GeneratedClassRegisterFunctionText.Log(BEGIN_WRAP_EDITOR_ONLY(bAllEditorOnlyFunctions)); - GeneratedClassRegisterFunctionText.Log(TEXT("\t\tstatic const FClassFunctionLinkInfo FuncInfo[];\r\n")); - GeneratedClassRegisterFunctionText.Log(END_WRAP_EDITOR_ONLY(bAllEditorOnlyFunctions)); - - StaticDefinitions.Log(BEGIN_WRAP_EDITOR_ONLY(bAllEditorOnlyFunctions)); - StaticDefinitions.Logf(TEXT("\tconst FClassFunctionLinkInfo %s::FuncInfo[] = {\r\n"), *StaticsStructName); - - for (UFunction* Function : FunctionsToExport) - { - const bool bIsEditorOnlyFunction = Function->HasAnyFunctionFlags(FUNC_EditorOnly); - - if (!Function->IsA()) - { - ExportFunction(Out, OutReferenceGatherers, SourceFile, Function, bIsNoExport); - } - - StaticDefinitions.Logf( - TEXT("%s\t\t{ &%s, %s },%s\r\n%s"), - BEGIN_WRAP_EDITOR_ONLY(bIsEditorOnlyFunction), - *GetSingletonNameFuncAddr(Function, OutReferenceGatherers.UniqueCrossModuleReferences), - *FNativeClassHeaderGenerator::GetUTF8OverriddenNameForLiteral(Function), - *GetGeneratedCodeHashTag(Function), - END_WRAP_EDITOR_ONLY(bIsEditorOnlyFunction) - ); - } - - StaticDefinitions.Log(TEXT("\t};\r\n")); - StaticDefinitions.Log(END_WRAP_EDITOR_ONLY(bAllEditorOnlyFunctions)); - - if (bAllEditorOnlyFunctions) - { - FunctionsArray = TEXT("IF_WITH_EDITOR(FuncInfo, nullptr)"); - FunctionsCount = TEXT("IF_WITH_EDITOR(UE_ARRAY_COUNT(FuncInfo), 0)"); - } - else - { - FunctionsArray = TEXT("FuncInfo"); - FunctionsCount = TEXT("UE_ARRAY_COUNT(FuncInfo)"); - } - } - else - { - FunctionsArray = TEXT("nullptr"); - FunctionsCount = TEXT("0"); - } - - TMap* MetaDataMap = UMetaData::GetMapForObject(Class); - if (MetaDataMap) - { - FClassMetaData* ClassMetaData = GScriptHelper.FindClassData(Class); - if (ClassMetaData && ClassMetaData->bObjectInitializerConstructorDeclared) - { - MetaDataMap->Add(NAME_ObjectInitializerConstructorDeclared, FString()); - } - } - - FString MetaDataParams = OutputMetaDataCodeForObject(GeneratedClassRegisterFunctionText, StaticDefinitions, Class, *FString::Printf(TEXT("%s::Class_MetaDataParams"), *StaticsStructName), TEXT("\t\t"), TEXT("\t")); - - TArray Props; - for (FProperty* Prop : TFieldRange(Class, EFieldIteratorFlags::ExcludeSuper)) - { - Props.Add(Prop); - } - - TTuple PropertyRange = OutputProperties(GeneratedClassRegisterFunctionText, StaticDefinitions, OutReferenceGatherers, *FString::Printf(TEXT("%s::"), *StaticsStructName), Props, TEXT("\t\t"), TEXT("\t")); - - const TCHAR* InterfaceArray; - const TCHAR* InterfaceCount; - if (Class->Interfaces.Num() > 0) - { - GeneratedClassRegisterFunctionText.Log(TEXT("\t\tstatic const UE4CodeGen_Private::FImplementedInterfaceParams InterfaceParams[];\r\n")); - - StaticDefinitions.Logf(TEXT("\t\tconst UE4CodeGen_Private::FImplementedInterfaceParams %s::InterfaceParams[] = {\r\n"), *StaticsStructName); - for (const FImplementedInterface& Inter : Class->Interfaces) - { - check(Inter.Class); - FString OffsetString; - if (Inter.PointerOffset) - { - OffsetString = FString::Printf(TEXT("(int32)VTABLE_OFFSET(%s, %s)"), *ClassNameCPP, *FNameLookupCPP::GetNameCPP(Inter.Class, true)); - } - else - { - OffsetString = TEXT("0"); - } - StaticDefinitions.Logf( - TEXT("\t\t\t{ %s, %s, %s },\r\n"), - *GetSingletonName(Inter.Class, OutReferenceGatherers.UniqueCrossModuleReferences, false).LeftChop(2), - *OffsetString, - Inter.bImplementedByK2 ? TEXT("true") : TEXT("false") - ); - } - StaticDefinitions.Log(TEXT("\t\t};\r\n")); - - InterfaceArray = TEXT("InterfaceParams"); - InterfaceCount = TEXT("UE_ARRAY_COUNT(InterfaceParams)"); - } - else - { - InterfaceArray = TEXT("nullptr"); - InterfaceCount = TEXT("0"); - } - - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tstatic const FCppClassTypeInfoStatic StaticCppClassTypeInfo;\r\n")); - - StaticDefinitions.Logf(TEXT("\tconst FCppClassTypeInfoStatic %s::StaticCppClassTypeInfo = {\r\n"), *StaticsStructName); - StaticDefinitions.Logf(TEXT("\t\tTCppClassTypeTraits<%s>::IsAbstract,\r\n"), *FNameLookupCPP::GetNameCPP(Class, Class->HasAllClassFlags(CLASS_Interface))); - StaticDefinitions.Logf(TEXT("\t};\r\n")); - - GeneratedClassRegisterFunctionText.Log (TEXT("\t\tstatic const UE4CodeGen_Private::FClassParams ClassParams;\r\n")); - - uint32 ClassFlags = (uint32)Class->ClassFlags; - if (!bIsNoExport) - { - ClassFlags = ClassFlags | CLASS_MatchedSerializers; - } - ClassFlags = ClassFlags & CLASS_SaveInCompiledInClasses; - - StaticDefinitions.Logf(TEXT("\tconst UE4CodeGen_Private::FClassParams %s::ClassParams = {\r\n"), *StaticsStructName); - StaticDefinitions.Logf(TEXT("\t\t&%s::StaticClass,\r\n"), *ClassNameCPP); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), (Class->ClassConfigName != NAME_None) ? *CreateUTF8LiteralString(Class->ClassConfigName.ToString()) : TEXT("nullptr")); - StaticDefinitions.Log (TEXT("\t\t&StaticCppClassTypeInfo,\r\n")); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), SingletonsArray); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), FunctionsArray); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), *PropertyRange.Get<0>()); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), InterfaceArray); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), SingletonsCount); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), FunctionsCount); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), *PropertyRange.Get<1>()); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), InterfaceCount); - StaticDefinitions.Logf(TEXT("\t\t0x%08Xu,\r\n"), ClassFlags); - StaticDefinitions.Logf(TEXT("\t\t%s\r\n"), *MetaDataParams); - StaticDefinitions.Log (TEXT("\t};\r\n")); - - GeneratedClassRegisterFunctionText.Logf(TEXT("\t};\r\n")); - GeneratedClassRegisterFunctionText.Log(*StaticDefinitions); - - GeneratedClassRegisterFunctionText.Logf(TEXT("\tUClass* %s\r\n"), *SingletonName); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t{\r\n")); - if (!bIsDynamic) - { - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tstatic UClass* OuterClass = nullptr;\r\n")); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tif (!OuterClass)\r\n")); - } - else - { - const FString& DynamicClassPackageName = FClass::GetTypePackageName(Class); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tUPackage* OuterPackage = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *DynamicClassPackageName); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tUClass* OuterClass = Cast(StaticFindObjectFast(UClass::StaticClass(), OuterPackage, TEXT(\"%s\")));\r\n"), *FNativeClassHeaderGenerator::GetOverriddenName(Class)); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tif (!OuterClass || !(OuterClass->ClassFlags & CLASS_Constructed))\r\n")); - } - - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\t{\r\n")); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\t\tUE4CodeGen_Private::ConstructUClass(OuterClass, %s::ClassParams);\r\n"), *StaticsStructName); - - TArray SparseClassDataTypes; - ((FClass*)Class)->GetSparseClassDataTypes(SparseClassDataTypes); - - for (const FString& SparseClassDataString : SparseClassDataTypes) - { - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\t\tOuterClass->SetSparseClassDataStruct(F%s::StaticStruct());\r\n"), *SparseClassDataString); - } - - if (bIsDynamic) - { - FString* CustomDynamicClassInitializationMD = MetaDataMap ? MetaDataMap->Find(TEXT("CustomDynamicClassInitialization")) : nullptr; - if (CustomDynamicClassInitializationMD) - { - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\t\t\t%s(CastChecked(OuterClass));\n"), *(*CustomDynamicClassInitializationMD)); - } - } - - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\t}\r\n")); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\treturn OuterClass;\r\n")); - GeneratedClassRegisterFunctionText.Logf(TEXT("\t}\r\n")); - - Out.Logf(TEXT("%s"), *GeneratedClassRegisterFunctionText); - } - - if (OutFriendText.Len() && bIsNoExport) - { - Out.Logf(TEXT("\t/* friend declarations for pasting into noexport class %s\r\n"), *ClassNameCPP); - Out.Log(OutFriendText); - Out.Logf(TEXT("\t*/\r\n")); - OutFriendText.Reset(); - } - - FString SingletonName = GetSingletonName(Class, OutReferenceGatherers.UniqueCrossModuleReferences); - SingletonName.ReplaceInline(TEXT("()"), TEXT(""), ESearchCase::CaseSensitive); // function address - - FString OverriddenClassName = *FNativeClassHeaderGenerator::GetOverriddenName(Class); - - const FString EmptyString; - const FString& InitSearchableValuesFunctionName = bIsDynamic ? Class->GetMetaData(NAME_InitializeStaticSearchableValues) : EmptyString; - const FString InitSearchableValuesFunctionParam = InitSearchableValuesFunctionName.IsEmpty() ? FString(TEXT("nullptr")) : - FString::Printf(TEXT("&%s::%s"), *ClassNameCPP, *InitSearchableValuesFunctionName); - - // Append base class' hash at the end of the generated code, this will force update derived classes - // when base class changes during hot-reload. - uint32 BaseClassHash = 0; - FClass* SuperClass = Class->GetSuperClass(); - if (SuperClass && !SuperClass->HasAnyClassFlags(CLASS_Intrinsic)) - { - // Since we are dependent on our SuperClass having generated its hash, if it is not available - // we will need to wait on it becoming available. Since the SourceFile array provided to the - // ParallelFor is in dependency order and does not allow cyclic dependencies, we can be certain - // that another thread has started processing the file containing our SuperClass before this - // file would have been assigned out, so we just have to wait - while (1) - { - { - FRWScopeLock Lock(GeneratedCodeHashesLock, SLT_ReadOnly); - if (const uint32* Hash = GeneratedCodeHashes.Find(SuperClass)) - { - BaseClassHash = *Hash; - break; - } - } - FPlatformProcess::Sleep(0.01); - } - } - GeneratedClassRegisterFunctionText.Logf(TEXT("\r\n// %u\r\n"), BaseClassHash); - - // Append info for the sparse class data struct onto the text to be hashed - TArray SparseClassDataTypes; - ((FClass*)Class)->GetSparseClassDataTypes(SparseClassDataTypes); - - for (const FString& SparseClassDataString : SparseClassDataTypes) - { - UScriptStruct* SparseClassDataStruct = FindObjectSafe(ANY_PACKAGE, *SparseClassDataString); - if (!SparseClassDataStruct) - { - continue; - } - GeneratedClassRegisterFunctionText.Logf(TEXT("%s\r\n"), *SparseClassDataStruct->GetName()); - for (FProperty* Child : TFieldRange(SparseClassDataStruct)) - { - GeneratedClassRegisterFunctionText.Logf(TEXT("%s %s\r\n"), *Child->GetCPPType(), *Child->GetNameCPP()); - } - } - - // Calculate generated class initialization code hash so that we know when it changes after hot-reload - uint32 ClassHash = GenerateTextHash(*GeneratedClassRegisterFunctionText); - AddGeneratedCodeHash(Class, ClassHash); - // Emit the IMPLEMENT_CLASS macro to go in the generated cpp file. - if (!bIsDynamic) - { - Out.Logf(TEXT("\tIMPLEMENT_CLASS(%s, %u);\r\n"), *ClassNameCPP, ClassHash); - } - else - { - Out.Logf(TEXT("\tIMPLEMENT_DYNAMIC_CLASS(%s, TEXT(\"%s\"), %u);\r\n"), *ClassNameCPP, *OverriddenClassName, ClassHash); - } - - Out.Logf(TEXT("\ttemplate<> %sUClass* StaticClass<%s>()\r\n"), *GetAPIString(), *ClassNameCPP); - Out.Logf(TEXT("\t{\r\n")); - Out.Logf(TEXT("\t\treturn %s::StaticClass();\r\n"), *ClassNameCPP); - Out.Logf(TEXT("\t}\r\n")); - - if (bIsDynamic) - { - const FString& ClassPackageName = FClass::GetTypePackageName(Class); - Out.Logf(TEXT("\tstatic FCompiledInDefer Z_CompiledInDefer_UClass_%s(%s, &%s::StaticClass, TEXT(\"%s\"), TEXT(\"%s\"), true, %s, %s, %s);\r\n"), - *ClassNameCPP, - *SingletonName, - *ClassNameCPP, - *ClassPackageName, - *OverriddenClassName, - *AsTEXT(ClassPackageName), - *AsTEXT(FNativeClassHeaderGenerator::GetOverriddenPathName(Class)), - *InitSearchableValuesFunctionParam); - } - else - { - Out.Logf(TEXT("\tstatic FCompiledInDefer Z_CompiledInDefer_UClass_%s(%s, &%s::StaticClass, TEXT(\"%s\"), TEXT(\"%s\"), false, nullptr, nullptr, %s);\r\n"), - *ClassNameCPP, - *SingletonName, - *ClassNameCPP, - *Class->GetOutermost()->GetName(), - *ClassNameCPP, - *InitSearchableValuesFunctionParam); - } - - - if (ClassHasReplicatedProperties(Class)) - { - Out.Logf(TEXT( - "\r\n" - "\tvoid %s::ValidateGeneratedRepEnums(const TArray& ClassReps) const\r\n" - "\t{\r\n" - ), *ClassNameCPP); - - FUHTStringBuilder NameBuilder; - - FUHTStringBuilder ValidationBuilder; - ValidationBuilder.Log(TEXT("\t\tconst bool bIsValid = true")); - - for (int32 i = Class->FirstOwnedClassRep; i < Class->ClassReps.Num(); ++i) - { - const FProperty* const Property = Class->ClassReps[i].Property; - const FString PropertyName = Property->GetName(); - - NameBuilder.Logf(TEXT("\t\tstatic const FName Name_%s(TEXT(\"%s\"));\r\n"), *PropertyName, *FNativeClassHeaderGenerator::GetOverriddenName(Property)); - - if (Property->ArrayDim == 1) - { - ValidationBuilder.Logf(TEXT("\r\n\t\t\t&& Name_%s == ClassReps[(int32)ENetFields_Private::%s].Property->GetFName()"), *PropertyName, *PropertyName); - } - else - { - ValidationBuilder.Logf(TEXT("\r\n\t\t\t&& Name_%s == ClassReps[(int32)ENetFields_Private::%s_STATIC_ARRAY].Property->GetFName()"), *PropertyName, *PropertyName); - } - } - - ValidationBuilder.Log(TEXT(";\r\n")); - - Out.Logf(TEXT( - "%s\r\n" // NameBuilder - "%s\r\n" // ValidationBuilder - "\t\tcheckf(bIsValid, TEXT(\"UHT Generated Rep Indices do not match runtime populated Rep Indices for properties in %s\"));\r\n" - "\t}\r\n" - ), *NameBuilder, *ValidationBuilder, *ClassNameCPP); - } -} - -void FNativeClassHeaderGenerator::ExportFunction(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UFunction* Function, bool bIsNoExport) const -{ - UFunction* SuperFunction = Function->GetSuperFunction(); - - const bool bIsEditorOnlyFunction = Function->HasAnyFunctionFlags(FUNC_EditorOnly); - - bool bIsDelegate = Function->HasAnyFunctionFlags(FUNC_Delegate); - - const FString& SingletonName = GetSingletonName(Function, OutReferenceGatherers.UniqueCrossModuleReferences); - FString StaticsStructName = SingletonName.LeftChop(2) + TEXT("_Statics"); - - FUHTStringBuilder CurrentFunctionText; - FUHTStringBuilder StaticDefinitions; - - // Begin wrapping editor only functions. Note: This should always be the first step! - if (bIsEditorOnlyFunction) - { - CurrentFunctionText.Logf(BeginEditorOnlyGuard); - } - - CurrentFunctionText.Logf(TEXT("\tstruct %s\r\n"), *StaticsStructName); - CurrentFunctionText.Log (TEXT("\t{\r\n")); - - if (bIsNoExport || !(Function->FunctionFlags&FUNC_Event)) // non-events do not export a params struct, so lets do that locally for offset determination - { - TArray Structs = FindNoExportStructs(Function); - for (UScriptStruct* Struct : Structs) - { - ExportMirrorsForNoexportStruct(CurrentFunctionText, Struct, /*Indent=*/ 2); - } - - ExportEventParm(CurrentFunctionText, OutReferenceGatherers.ForwardDeclarations, Function, /*Indent=*/ 2, /*bOutputConstructor=*/ false, EExportingState::TypeEraseDelegates); - } - - UField* FieldOuter = Cast(Function->GetOuter()); - const bool bIsDynamic = (FieldOuter && FClass::IsDynamic(FieldOuter)); - - FString OuterFunc; - if (UObject* Outer = Function->GetOuter()) - { - OuterFunc = Outer->IsA() ? GetPackageSingletonName((UPackage*)Outer, OutReferenceGatherers.UniqueCrossModuleReferences).LeftChop(2) : GetSingletonNameFuncAddr(Function->GetOwnerClass(), OutReferenceGatherers.UniqueCrossModuleReferences); - } - else - { - OuterFunc = TEXT("nullptr"); - } - - TArray Props; - Algo::Copy(TFieldRange(Function, EFieldIteratorFlags::ExcludeSuper), Props, Algo::NoRef); - - FString StructureSize; - if (Props.Num()) - { - UFunction* TempFunction = Function; - while (TempFunction->GetSuperFunction()) - { - TempFunction = TempFunction->GetSuperFunction(); - } - FString FunctionName = TempFunction->GetName(); - if (TempFunction->HasAnyFunctionFlags(FUNC_Delegate)) - { - FunctionName.LeftChopInline(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH, false); - } - - StructureSize = FString::Printf(TEXT("sizeof(%s)"), *GetEventStructParamsName(TempFunction->GetOuter(), *FunctionName)); - } - else - { - StructureSize = TEXT("0"); - } - - USparseDelegateFunction* SparseDelegateFunction = Cast(Function); - const TCHAR* UFunctionObjectFlags = FClass::IsOwnedByDynamicType(Function) ? TEXT("RF_Public|RF_Transient") : TEXT("RF_Public|RF_Transient|RF_MarkAsNative"); - - TTuple PropertyRange = OutputProperties(CurrentFunctionText, StaticDefinitions, OutReferenceGatherers, *FString::Printf(TEXT("%s::"), *StaticsStructName), Props, TEXT("\t\t"), TEXT("\t")); - - const FFunctionData* CompilerInfo = FFunctionData::FindForFunction(Function); - const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData(); - const bool bIsNet = !!(FunctionData.FunctionFlags & (FUNC_NetRequest | FUNC_NetResponse)); - - FString MetaDataParams = OutputMetaDataCodeForObject(CurrentFunctionText, StaticDefinitions, Function, *FString::Printf(TEXT("%s::Function_MetaDataParams"), *StaticsStructName), TEXT("\t\t"), TEXT("\t")); - - CurrentFunctionText.Log(TEXT("\t\tstatic const UE4CodeGen_Private::FFunctionParams FuncParams;\r\n")); - - StaticDefinitions.Logf( - TEXT("\tconst UE4CodeGen_Private::FFunctionParams %s::FuncParams = { (UObject*(*)())%s, %s, %s, %s, %s, %s, %s, %s, %s, (EFunctionFlags)0x%08X, %d, %d, %s };\r\n"), - *StaticsStructName, - *OuterFunc, - *GetSingletonNameFuncAddr(SuperFunction, OutReferenceGatherers.UniqueCrossModuleReferences), - *CreateUTF8LiteralString(FNativeClassHeaderGenerator::GetOverriddenName(Function)), - (SparseDelegateFunction ? *CreateUTF8LiteralString(SparseDelegateFunction->OwningClassName.ToString()) : TEXT("nullptr")), - (SparseDelegateFunction ? *CreateUTF8LiteralString(SparseDelegateFunction->DelegateName.ToString()) : TEXT("nullptr")), - *StructureSize, - *PropertyRange.Get<0>(), - *PropertyRange.Get<1>(), - UFunctionObjectFlags, - (uint32)Function->FunctionFlags, - bIsNet ? FunctionData.RPCId : 0, - bIsNet ? FunctionData.RPCResponseId : 0, - *MetaDataParams - ); - - CurrentFunctionText.Log(TEXT("\t};\r\n")); - CurrentFunctionText.Log(*StaticDefinitions); - - CurrentFunctionText.Logf(TEXT("\tUFunction* %s\r\n"), *SingletonName); - CurrentFunctionText.Log (TEXT("\t{\r\n")); - - if (!bIsDynamic) - { - CurrentFunctionText.Logf(TEXT("\t\tstatic UFunction* ReturnFunction = nullptr;\r\n")); - } - else - { - FString FunctionName = FNativeClassHeaderGenerator::GetOverriddenNameForLiteral(Function); - CurrentFunctionText.Logf(TEXT("\t\tUObject* Outer = %s();\r\n"), *OuterFunc); - CurrentFunctionText.Logf(TEXT("\t\tUFunction* ReturnFunction = static_cast(StaticFindObjectFast( UFunction::StaticClass(), Outer, %s ));\r\n"), *FunctionName); - } - - CurrentFunctionText.Logf(TEXT("\t\tif (!ReturnFunction)\r\n")); - CurrentFunctionText.Logf(TEXT("\t\t{\r\n")); - CurrentFunctionText.Logf(TEXT("\t\t\tUE4CodeGen_Private::ConstructUFunction(ReturnFunction, %s::FuncParams);\r\n"), *StaticsStructName); - CurrentFunctionText.Log (TEXT("\t\t}\r\n")); - CurrentFunctionText.Log (TEXT("\t\treturn ReturnFunction;\r\n")); - CurrentFunctionText.Log (TEXT("\t}\r\n")); - - // End wrapping editor only functions. Note: This should always be the last step! - if (bIsEditorOnlyFunction) - { - CurrentFunctionText.Logf(EndEditorOnlyGuard); - } - - uint32 FunctionHash = GenerateTextHash(*CurrentFunctionText); - AddGeneratedCodeHash(Function, FunctionHash); - Out.Log(CurrentFunctionText); -} - -void FNativeClassHeaderGenerator::ExportNatives(FOutputDevice& Out, FClass* Class) -{ - const FString ClassCPPName = FNameLookupCPP::GetNameCPP(Class); - FString TypeName = Class->HasAnyClassFlags(CLASS_Interface) ? FString::Printf(TEXT("I%s"), *Class->GetName()) : ClassCPPName; - - Out.Logf(TEXT("\tvoid %s::StaticRegisterNatives%s()\r\n"), *ClassCPPName, *ClassCPPName); - Out.Log(TEXT("\t{\r\n")); - - { - bool bAllEditorOnly = true; - - TArray> NamedFunctionsToExport; - for (UFunction* Function : TFieldRange(Class, EFieldIteratorFlags::ExcludeSuper)) - { - if ((Function->FunctionFlags & (FUNC_Native | FUNC_NetRequest)) == FUNC_Native) - { - FString OverriddenName = FNativeClassHeaderGenerator::GetUTF8OverriddenNameForLiteral(Function); - NamedFunctionsToExport.Emplace(Function, MoveTemp(OverriddenName)); - - if (!Function->HasAnyFunctionFlags(FUNC_EditorOnly)) - { - bAllEditorOnly = false; - } - } - } - - Algo::SortBy(NamedFunctionsToExport, [](const TTuple& Pair){ return Pair.Get<0>()->GetFName(); }, FNameLexicalLess()); - - if (NamedFunctionsToExport.Num() > 0) - { - FMacroBlockEmitter EditorOnly(Out, TEXT("WITH_EDITOR")); - EditorOnly(bAllEditorOnly); - - Out.Logf(TEXT("\t\tUClass* Class = %s::StaticClass();\r\n"), *ClassCPPName); - Out.Log(TEXT("\t\tstatic const FNameNativePtrPair Funcs[] = {\r\n")); - - for (const TTuple& Func : NamedFunctionsToExport) - { - UFunction* Function = Func.Get<0>(); - - EditorOnly(Function->HasAnyFunctionFlags(FUNC_EditorOnly)); - - Out.Logf( - TEXT("\t\t\t{ %s, &%s::exec%s },\r\n"), - *Func.Get<1>(), - *TypeName, - *Function->GetName() - ); - } - - EditorOnly(bAllEditorOnly); - - Out.Log(TEXT("\t\t};\r\n")); - Out.Logf(TEXT("\t\tFNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));\r\n")); - } - } - - for (UScriptStruct* Struct : TFieldRange(Class, EFieldIteratorFlags::ExcludeSuper)) - { - if (Struct->StructFlags & STRUCT_Native) - { - Out.Logf( TEXT("\t\tUScriptStruct::DeferCppStructOps(FName(TEXT(\"%s\")),new UScriptStruct::TCppStructOps<%s%s>);\r\n"), *Struct->GetName(), Struct->GetPrefixCPP(), *Struct->GetName() ); - } - } - - Out.Logf(TEXT("\t}\r\n")); -} - -void FNativeClassHeaderGenerator::ExportInterfaceCallFunctions(FOutputDevice& OutCpp, FUHTStringBuilder& Out, FReferenceGatherers& OutReferenceGatherers, const TArray& CallbackFunctions, const TCHAR* ClassName) const -{ - const FString& APIString = GetAPIString(); - - for (UFunction* Function : CallbackFunctions) - { - FString FunctionName = Function->GetName(); - - FFunctionData* CompilerInfo = FFunctionData::FindForFunction(Function); - - const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData(); - const TCHAR* ConstQualifier = FunctionData.FunctionReference->HasAllFunctionFlags(FUNC_Const) ? TEXT("const ") : TEXT(""); - FString ExtraParam = FString::Printf(TEXT("%sUObject* O"), ConstQualifier); - - ExportNativeFunctionHeader(Out, OutReferenceGatherers.ForwardDeclarations, FunctionData, EExportFunctionType::Interface, EExportFunctionHeaderStyle::Declaration, *ExtraParam, *APIString); - Out.Logf( TEXT(";") LINE_TERMINATOR ); - - FString FunctionNameName = FString::Printf(TEXT("NAME_%s_%s"), *FNameLookupCPP::GetNameCPP(CastChecked(Function->GetOuter())), *FunctionName); - OutCpp.Logf(TEXT("\tstatic FName %s = FName(TEXT(\"%s\"));") LINE_TERMINATOR, *FunctionNameName, *GetOverriddenFName(Function).ToString()); - - ExportNativeFunctionHeader(OutCpp, OutReferenceGatherers.ForwardDeclarations, FunctionData, EExportFunctionType::Interface, EExportFunctionHeaderStyle::Definition, *ExtraParam, *APIString); - OutCpp.Logf( LINE_TERMINATOR TEXT("\t{") LINE_TERMINATOR ); - - OutCpp.Logf(TEXT("\t\tcheck(O != NULL);") LINE_TERMINATOR); - OutCpp.Logf(TEXT("\t\tcheck(O->GetClass()->ImplementsInterface(U%s::StaticClass()));") LINE_TERMINATOR, ClassName); - - FParmsAndReturnProperties Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference); - - // See if we need to create Parms struct - const bool bHasParms = Parameters.HasParms(); - if (bHasParms) - { - FString EventParmStructName = GetEventStructParamsName(Function->GetOuter(), *FunctionName); - OutCpp.Logf(TEXT("\t\t%s Parms;") LINE_TERMINATOR, *EventParmStructName); - } - - OutCpp.Logf(TEXT("\t\tUFunction* const Func = O->FindFunction(%s);") LINE_TERMINATOR, *FunctionNameName); - OutCpp.Log(TEXT("\t\tif (Func)") LINE_TERMINATOR); - OutCpp.Log(TEXT("\t\t{") LINE_TERMINATOR); - - // code to populate Parms struct - for (FProperty* Param : Parameters.Parms) - { - const FString ParamName = Param->GetName(); - OutCpp.Logf(TEXT("\t\t\tParms.%s=%s;") LINE_TERMINATOR, *ParamName, *ParamName); - } - - const FString ObjectRef = FunctionData.FunctionReference->HasAllFunctionFlags(FUNC_Const) ? FString::Printf(TEXT("const_cast(O)")) : TEXT("O"); - OutCpp.Logf(TEXT("\t\t\t%s->ProcessEvent(Func, %s);") LINE_TERMINATOR, *ObjectRef, bHasParms ? TEXT("&Parms") : TEXT("NULL")); - - for (FProperty* Param : Parameters.Parms) - { - if( Param->HasAllPropertyFlags(CPF_OutParm) && !Param->HasAnyPropertyFlags(CPF_ConstParm|CPF_ReturnParm)) - { - const FString ParamName = Param->GetName(); - OutCpp.Logf(TEXT("\t\t\t%s=Parms.%s;") LINE_TERMINATOR, *ParamName, *ParamName); - } - } - - OutCpp.Log(TEXT("\t\t}") LINE_TERMINATOR); - - - // else clause to call back into native if it's a BlueprintNativeEvent - if (Function->FunctionFlags & FUNC_Native) - { - OutCpp.Logf(TEXT("\t\telse if (auto I = (%sI%s*)(O->GetNativeInterfaceAddress(U%s::StaticClass())))") LINE_TERMINATOR, ConstQualifier, ClassName, ClassName); - OutCpp.Log(TEXT("\t\t{") LINE_TERMINATOR); - - OutCpp.Log(TEXT("\t\t\t")); - if (Parameters.Return) - { - OutCpp.Log(TEXT("Parms.ReturnValue = ")); - } - - OutCpp.Logf(TEXT("I->%s_Implementation("), *FunctionName); - - bool bFirst = true; - for (FProperty* Param : Parameters.Parms) - { - if (!bFirst) - { - OutCpp.Logf(TEXT(",")); - } - bFirst = false; - - OutCpp.Logf(TEXT("%s"), *Param->GetName()); - } - - OutCpp.Logf(TEXT(");") LINE_TERMINATOR); - - OutCpp.Logf(TEXT("\t\t}") LINE_TERMINATOR); - } - - if (Parameters.Return) - { - OutCpp.Logf(TEXT("\t\treturn Parms.ReturnValue;") LINE_TERMINATOR); - } - - OutCpp.Logf(TEXT("\t}") LINE_TERMINATOR); - } -} - -/** - * Gets preprocessor string to emit GENERATED_U*_BODY() macro is deprecated. - * - * @param MacroName Name of the macro to be deprecated. - * - * @returns Preprocessor string to emit the message. - */ -FString GetGeneratedMacroDeprecationWarning(const TCHAR* MacroName) -{ - // Deprecation warning is disabled right now. After people get familiar with the new macro it should be re-enabled. - //return FString() + TEXT("EMIT_DEPRECATED_WARNING_MESSAGE(\"") + MacroName + TEXT("() macro is deprecated. Please use GENERATED_BODY() macro instead.\")") LINE_TERMINATOR; - return TEXT(""); -} - -/** - * Returns a string with access specifier that was met before parsing GENERATED_BODY() macro to preserve it. - * - * @param Class Class for which to return the access specifier. - * - * @returns Access specifier string. - */ -FString GetPreservedAccessSpecifierString(FClass* Class) -{ - FString PreservedAccessSpecifier; - if (FClassMetaData* Data = GScriptHelper.FindClassData(Class)) - { - switch (Data->GeneratedBodyMacroAccessSpecifier) - { - case EAccessSpecifier::ACCESS_Private: - PreservedAccessSpecifier = "private:"; - break; - case EAccessSpecifier::ACCESS_Protected: - PreservedAccessSpecifier = "protected:"; - break; - case EAccessSpecifier::ACCESS_Public: - PreservedAccessSpecifier = "public:"; - break; - case EAccessSpecifier::ACCESS_NotAnAccessSpecifier : - PreservedAccessSpecifier = FString::Printf(TEXT("static_assert(false, \"Unknown access specifier for GENERATED_BODY() macro in class %s.\");"), *GetNameSafe(Class)); - break; - } - } - - return PreservedAccessSpecifier + LINE_TERMINATOR; -} - -void WriteMacro(FOutputDevice& Output, const FString& MacroName, FString MacroContent) -{ - Output.Log(Macroize(*MacroName, MoveTemp(MacroContent))); -} - -void FNativeClassHeaderGenerator::ExportClassFromSourceFileInner( - FOutputDevice& OutGeneratedHeaderText, - FOutputDevice& OutCpp, - FOutputDevice& OutDeclarations, - FReferenceGatherers& OutReferenceGatherers, - FClass* Class, - const FUnrealSourceFile& SourceFile, - EExportClassOutFlags& OutFlags -) const -{ - FUHTStringBuilder StandardUObjectConstructorsMacroCall; - FUHTStringBuilder EnhancedUObjectConstructorsMacroCall; - - FClassMetaData* ClassData = GScriptHelper.FindClassData(Class); - checkf(ClassData, TEXT("No class data generated for file %s"), *SourceFile.GetFilename()); - - // C++ -> VM stubs (native function execs) - FUHTStringBuilder ClassMacroCalls; - FUHTStringBuilder ClassNoPureDeclsMacroCalls; - ExportNativeFunctions(OutGeneratedHeaderText, OutCpp, ClassMacroCalls, ClassNoPureDeclsMacroCalls, OutReferenceGatherers, SourceFile, Class, ClassData); - - // Get Callback functions - TArray CallbackFunctions; - for (UFunction* Function : TFieldRange(Class, EFieldIteratorFlags::ExcludeSuper)) - { - if ((Function->FunctionFlags & FUNC_Event) && Function->GetSuperFunction() == nullptr) - { - CallbackFunctions.Add(Function); - } - } - - FUHTStringBuilder PrologMacroCalls; - if (CallbackFunctions.Num() != 0) - { - Algo::SortBy(CallbackFunctions, [](UObject* Obj) { return Obj->GetName(); }); - - FUHTStringBuilder UClassMacroContent; - - // export parameters structs for all events and delegates - for (UFunction* Function : CallbackFunctions) - { - ExportEventParm(UClassMacroContent, OutReferenceGatherers.ForwardDeclarations, Function, /*Indent=*/ 1, /*bOutputConstructor=*/ true, EExportingState::Normal); - } - - FString MacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_EVENT_PARMS")); - WriteMacro(OutGeneratedHeaderText, MacroName, UClassMacroContent); - PrologMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); - - // VM -> C++ proxies (events and delegates). - FOutputDeviceNull NullOutput; - FOutputDevice& CallbackOut = Class->HasAnyClassFlags(CLASS_NoExport) ? NullOutput : OutCpp; - FString CallbackWrappersMacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_CALLBACK_WRAPPERS")); - ExportCallbackFunctions( - OutGeneratedHeaderText, - CallbackOut, - OutReferenceGatherers.ForwardDeclarations, - CallbackFunctions, - *CallbackWrappersMacroName, - (Class->ClassFlags & CLASS_Interface) ? EExportCallbackType::Interface : EExportCallbackType::Class, - *GetAPIString() - ); - - ClassMacroCalls.Logf(TEXT("\t%s\r\n"), *CallbackWrappersMacroName); - ClassNoPureDeclsMacroCalls.Logf(TEXT("\t%s\r\n"), *CallbackWrappersMacroName); - } - - // Class definition. - if (!Class->HasAnyClassFlags(CLASS_NoExport)) - { - ExportNatives(OutCpp, Class); - } - - FUHTStringBuilder FriendText; - ExportNativeGeneratedInitCode(OutCpp, OutDeclarations, OutReferenceGatherers, SourceFile, Class, FriendText); - - FClass* SuperClass = Class->GetSuperClass(); - - // the name for the C++ version of the UClass - const FString ClassCPPName = FNameLookupCPP::GetNameCPP(Class); - const FString SuperClassCPPName = (SuperClass ? FNameLookupCPP::GetNameCPP(SuperClass) : TEXT("None")); - - FString APIArg = API; - if (!Class->HasAnyClassFlags(CLASS_MinimalAPI)) - { - APIArg = TEXT("NO"); - } - - ClassDefinitionRange ClassRange; - if (const ClassDefinitionRange* FoundRange = HeaderParser.ClassDefinitionRanges.Find(Class)) - { - ClassRange = *FoundRange; - ClassRange.Validate(); - } - - FString GeneratedSerializeFunctionCPP; - FString GeneratedSerializeFunctionHeaderMacroName; - - { - // UHTLite - FRWScopeLock Lock(HeaderParser.ClassSerializerMapLock, SLT_ReadOnly); - - if (const FArchiveTypeDefinePair* ArchiveTypeDefinePair = HeaderParser.ClassSerializerMap.Find(Class)) - { - FString EnclosingDefines; - FUHTStringBuilder Boilerplate, BoilerPlateCPP; - const TCHAR* MacroNameHeader; - const TCHAR* MacroNameCPP; - GeneratedSerializeFunctionHeaderMacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_ARCHIVESERIALIZER")); - - EnclosingDefines = ArchiveTypeDefinePair->EnclosingDefine; - if (ArchiveTypeDefinePair->ArchiveType == ESerializerArchiveType::StructuredArchiveRecord) - { - MacroNameHeader = TEXT("DECLARE_FARCHIVE_SERIALIZER"); - MacroNameCPP = TEXT("IMPLEMENT_FARCHIVE_SERIALIZER"); - } - else - { - MacroNameHeader = TEXT("DECLARE_FSTRUCTUREDARCHIVE_SERIALIZER"); - MacroNameCPP = TEXT("IMPLEMENT_FSTRUCTUREDARCHIVE_SERIALIZER"); - } - - // if the existing Serialize function was wrapped in a compiler define directive, we need to replicate that on the generated function - if (EnclosingDefines.Len()) - { - OutGeneratedHeaderText.Logf(TEXT("#if %s\r\n"), *EnclosingDefines); - BoilerPlateCPP.Logf(TEXT("#if %s\r\n"), *EnclosingDefines); - } - - Boilerplate.Logf(TEXT("\t%s(%s, %s_API)\r\n"), MacroNameHeader, *ClassCPPName, *APIArg); - OutGeneratedHeaderText.Log(Macroize(*GeneratedSerializeFunctionHeaderMacroName, *Boilerplate)); - BoilerPlateCPP.Logf(TEXT("\t%s(%s)\r\n"), MacroNameCPP, *ClassCPPName); - - if (EnclosingDefines.Len()) - { - OutGeneratedHeaderText.Logf(TEXT("#else\r\n")); - OutGeneratedHeaderText.Log(Macroize(*GeneratedSerializeFunctionHeaderMacroName, TEXT(""))); - OutGeneratedHeaderText.Logf(TEXT("#endif\r\n")); - BoilerPlateCPP.Logf(TEXT("#endif\r\n")); - } - - GeneratedSerializeFunctionCPP = BoilerPlateCPP; - } - } - - { - FUHTStringBuilder Boilerplate; - - // Export the class's native function registration. - Boilerplate.Logf(TEXT("private:\r\n")); - Boilerplate.Logf(TEXT("\tstatic void StaticRegisterNatives%s();\r\n"), *ClassCPPName); - Boilerplate.Log(*FriendText); - Boilerplate.Logf(TEXT("public:\r\n")); - - const bool bCastedClass = Class->HasAnyCastFlag(CASTCLASS_AllFlags) && SuperClass && Class->ClassCastFlags != SuperClass->ClassCastFlags; - - Boilerplate.Logf(TEXT("\tDECLARE_CLASS(%s, %s, COMPILED_IN_FLAGS(%s%s), %s, TEXT(\"%s\"), %s_API)\r\n"), - *ClassCPPName, - *SuperClassCPPName, - Class->HasAnyClassFlags(CLASS_Abstract) ? TEXT("CLASS_Abstract") : TEXT("0"), - *GetClassFlagExportText(Class), - bCastedClass ? *FString::Printf(TEXT("CASTCLASS_%s"), *ClassCPPName) : TEXT("CASTCLASS_None"), - *FClass::GetTypePackageName(Class), - *APIArg); - - Boilerplate.Logf(TEXT("\tDECLARE_SERIALIZER(%s)\r\n"), *ClassCPPName); - - // Add the serialization function declaration if we generated one - if (GeneratedSerializeFunctionHeaderMacroName.Len() > 0) - { - Boilerplate.Logf(TEXT("\t%s\r\n"), *GeneratedSerializeFunctionHeaderMacroName); - } - - if (SuperClass && Class->ClassWithin != SuperClass->ClassWithin) - { - Boilerplate.Logf(TEXT("\tDECLARE_WITHIN(%s)\r\n"), *FNameLookupCPP::GetNameCPP(Class->GetClassWithin())); - } - - if (Class->HasAnyClassFlags(CLASS_Interface)) - { - ExportConstructorsMacros(OutGeneratedHeaderText, OutCpp, StandardUObjectConstructorsMacroCall, EnhancedUObjectConstructorsMacroCall, SourceFile.GetGeneratedMacroName(ClassData), Class, *APIArg); - - FString InterfaceMacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_GENERATED_UINTERFACE_BODY")); - OutGeneratedHeaderText.Log(Macroize(*(InterfaceMacroName + TEXT("()")), *Boilerplate)); - - int32 ClassGeneratedBodyLine = ClassData->GetGeneratedBodyLine(); - - FString DeprecationWarning = GetGeneratedMacroDeprecationWarning(TEXT("GENERATED_UINTERFACE_BODY")); - - const TCHAR* Offset = TEXT("\t"); - - OutGeneratedHeaderText.Log( - Macroize( - *SourceFile.GetGeneratedBodyMacroName(ClassGeneratedBodyLine, true), - FString::Printf(TEXT("\t%s\t%s\t%s()") LINE_TERMINATOR TEXT("%s\t%s") - , *DeprecationWarning - , DisableDeprecationWarnings - , *InterfaceMacroName - , *StandardUObjectConstructorsMacroCall - , EnableDeprecationWarnings - ) - ) - ); - - OutGeneratedHeaderText.Log( - Macroize( - *SourceFile.GetGeneratedBodyMacroName(ClassGeneratedBodyLine), - FString::Printf(TEXT("\t%s\t%s()") LINE_TERMINATOR TEXT("%s%s\t%s") - , DisableDeprecationWarnings - , *InterfaceMacroName - , *EnhancedUObjectConstructorsMacroCall - , *GetPreservedAccessSpecifierString(Class) - , EnableDeprecationWarnings - ) - ) - ); - - // ============================================= - // Export the pure interface version of the class - - // the name of the pure interface class - FString InterfaceCPPName = FString::Printf(TEXT("I%s"), *Class->GetName()); - FString SuperInterfaceCPPName; - if (SuperClass != NULL) - { - SuperInterfaceCPPName = FString::Printf(TEXT("I%s"), *SuperClass->GetName()); - } - - // Thunk functions - FUHTStringBuilder InterfaceBoilerplate; - - InterfaceBoilerplate.Logf(TEXT("protected:\r\n\tvirtual ~%s() {}\r\n"), *InterfaceCPPName); - InterfaceBoilerplate.Logf(TEXT("public:\r\n\ttypedef %s UClassType;\r\n"), *ClassCPPName); - InterfaceBoilerplate.Logf(TEXT("\ttypedef %s ThisClass;\r\n"), *InterfaceCPPName); - - ExportInterfaceCallFunctions(OutCpp, InterfaceBoilerplate, OutReferenceGatherers, CallbackFunctions, *Class->GetName()); - - // we'll need a way to get to the UObject portion of a native interface, so that we can safely pass native interfaces - // to script VM functions - if (SuperClass->IsChildOf(UInterface::StaticClass())) - { - // Note: This used to be declared as a pure virtual function, but it was changed here in order to allow the Blueprint nativization process - // to detect C++ interface classes that explicitly declare pure virtual functions via type traits. This code will no longer trigger that check. - InterfaceBoilerplate.Logf(TEXT("\tvirtual UObject* _getUObject() const { check(0 && \"Missing required implementation.\"); return nullptr; }\r\n")); - } - - if (ClassHasReplicatedProperties(Class)) - { - WriteReplicatedMacroData(ClassRange, *ClassCPPName, *APIArg, Class, SuperClass, InterfaceBoilerplate, SourceFile, OutFlags); - } - - FString NoPureDeclsMacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_INCLASS_IINTERFACE_NO_PURE_DECLS")); - WriteMacro(OutGeneratedHeaderText, NoPureDeclsMacroName, InterfaceBoilerplate); - ClassNoPureDeclsMacroCalls.Logf(TEXT("\t%s\r\n"), *NoPureDeclsMacroName); - - FString MacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_INCLASS_IINTERFACE")); - WriteMacro(OutGeneratedHeaderText, MacroName, InterfaceBoilerplate); - ClassMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); - } - else - { - // export the class's config name - if (SuperClass && Class->ClassConfigName != NAME_None && Class->ClassConfigName != SuperClass->ClassConfigName) - { - Boilerplate.Logf(TEXT("\tstatic const TCHAR* StaticConfigName() {return TEXT(\"%s\");}\r\n\r\n"), *Class->ClassConfigName.ToString()); - } - - // export implementation of _getUObject for classes that implement interfaces - if (Class->Interfaces.Num() > 0) - { - Boilerplate.Logf(TEXT("\tvirtual UObject* _getUObject() const override { return const_cast<%s*>(this); }\r\n"), *ClassCPPName); - } - - if (ClassHasReplicatedProperties(Class)) - { - WriteReplicatedMacroData(ClassRange, *ClassCPPName, *APIArg, Class, SuperClass, Boilerplate, SourceFile, OutFlags); - } - - { - FString NoPureDeclsMacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_INCLASS_NO_PURE_DECLS")); - WriteMacro(OutGeneratedHeaderText, NoPureDeclsMacroName, Boilerplate); - ClassNoPureDeclsMacroCalls.Logf(TEXT("\t%s\r\n"), *NoPureDeclsMacroName); - - FString MacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_INCLASS")); - WriteMacro(OutGeneratedHeaderText, MacroName, Boilerplate); - ClassMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); - - ExportConstructorsMacros(OutGeneratedHeaderText, OutCpp, StandardUObjectConstructorsMacroCall, EnhancedUObjectConstructorsMacroCall, SourceFile.GetGeneratedMacroName(ClassData), Class, *APIArg); - } - } - } - - { - FString MacroName = SourceFile.GetGeneratedMacroName(ClassData->GetPrologLine(), TEXT("_PROLOG")); - WriteMacro(OutGeneratedHeaderText, MacroName, PrologMacroCalls); - } - - { - const TCHAR* Public = TEXT("public:") LINE_TERMINATOR; - - const bool bIsIInterface = Class->HasAnyClassFlags(CLASS_Interface); - - const TCHAR* MacroName; - FString DeprecationWarning; - FString LegacyGeneratedBody; - FString GeneratedBody; - int32 GeneratedBodyLine; - - - if (bIsIInterface) - { - MacroName = TEXT("GENERATED_IINTERFACE_BODY()"); - GeneratedBodyLine = ClassData->GetInterfaceGeneratedBodyLine(); - LegacyGeneratedBody = ClassMacroCalls; - GeneratedBody = ClassNoPureDeclsMacroCalls; - } - else - { - MacroName = TEXT("GENERATED_UCLASS_BODY()"); - DeprecationWarning = GetGeneratedMacroDeprecationWarning(MacroName); - GeneratedBodyLine = ClassData->GetGeneratedBodyLine(); - LegacyGeneratedBody = FString::Printf(TEXT("%s%s"), *ClassMacroCalls, *StandardUObjectConstructorsMacroCall); - GeneratedBody = FString::Printf(TEXT("%s%s"), *ClassNoPureDeclsMacroCalls, *EnhancedUObjectConstructorsMacroCall); - } - - FString WrappedLegacyGeneratedBody = FString::Printf(TEXT("%s%s%s%s%s%s"), *DeprecationWarning, DisableDeprecationWarnings, Public, *LegacyGeneratedBody, Public, EnableDeprecationWarnings); - FString WrappedGeneratedBody = FString::Printf(TEXT("%s%s%s%s%s"), DisableDeprecationWarnings, Public, *GeneratedBody, *GetPreservedAccessSpecifierString(Class), EnableDeprecationWarnings); - - OutGeneratedHeaderText.Log(Macroize(*SourceFile.GetGeneratedBodyMacroName(GeneratedBodyLine, true), MoveTemp(WrappedLegacyGeneratedBody))); - OutGeneratedHeaderText.Log(Macroize(*SourceFile.GetGeneratedBodyMacroName(GeneratedBodyLine, false), MoveTemp(WrappedGeneratedBody))); - } - - // Forward declare the StaticClass specialisation in the header - OutGeneratedHeaderText.Logf(TEXT("template<> %sUClass* StaticClass();\r\n\r\n"), *GetAPIString(), *ClassCPPName); - - // If there is a serialization function implementation for the CPP file, add it now - if (GeneratedSerializeFunctionCPP.Len()) - { - OutCpp.Log(GeneratedSerializeFunctionCPP); - } -} - -/** -* Generates private copy-constructor declaration. -* -* @param Out Output device to generate to. -* @param Class Class to generate constructor for. -* @param API API string for this constructor. -*/ -void ExportCopyConstructorDefinition(FOutputDevice& Out, const TCHAR* API, const TCHAR* ClassCPPName) -{ - Out.Logf(TEXT("private:\r\n")); - Out.Logf(TEXT("\t/** Private move- and copy-constructors, should never be used */\r\n")); - Out.Logf(TEXT("\t%s_API %s(%s&&);\r\n"), API, ClassCPPName, ClassCPPName); - Out.Logf(TEXT("\t%s_API %s(const %s&);\r\n"), API, ClassCPPName, ClassCPPName); - Out.Logf(TEXT("public:\r\n")); -} - -/** - * Generates vtable helper caller and eventual constructor body. - * - * @param Out Output device to generate to. - * @param Class Class to generate for. - * @param API API string. - */ -void ExportVTableHelperCtorAndCaller(FOutputDevice& Out, FClassMetaData* ClassData, const TCHAR* API, const TCHAR* ClassCPPName) -{ - if (!ClassData->bCustomVTableHelperConstructorDeclared) - { - Out.Logf(TEXT("\tDECLARE_VTABLE_PTR_HELPER_CTOR(%s_API, %s);" LINE_TERMINATOR), API, ClassCPPName); - } - Out.Logf(TEXT("DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(%s);" LINE_TERMINATOR), ClassCPPName); -} - -/** - * Generates standard constructor declaration. - * - * @param Out Output device to generate to. - * @param Class Class to generate constructor for. - * @param API API string for this constructor. - */ -void ExportStandardConstructorsMacro(FOutputDevice& Out, FClass* Class, FClassMetaData* ClassData, const TCHAR* API, const TCHAR* ClassCPPName) -{ - if (!Class->HasAnyClassFlags(CLASS_CustomConstructor)) - { - Out.Logf(TEXT("\t/** Standard constructor, called after all reflected properties have been initialized */\r\n")); - Out.Logf(TEXT("\t%s_API %s(const FObjectInitializer& ObjectInitializer%s);\r\n"), API, ClassCPPName, - ClassData->bDefaultConstructorDeclared ? TEXT("") : TEXT(" = FObjectInitializer::Get()")); - } - Out.Logf(TEXT("\tDEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(%s)\r\n"), ClassCPPName); - - ExportVTableHelperCtorAndCaller(Out, ClassData, API, ClassCPPName); - ExportCopyConstructorDefinition(Out, API, ClassCPPName); -} - -/** - * Generates constructor definition. - * - * @param Out Output device to generate to. - * @param Class Class to generate constructor for. - * @param API API string for this constructor. - */ -void ExportConstructorDefinition(FOutputDevice& Out, FClass* Class, FClassMetaData* ClassData, const TCHAR* API, const TCHAR* ClassCPPName) -{ - if (!ClassData->bConstructorDeclared) - { - Out.Logf(TEXT("\t/** Standard constructor, called after all reflected properties have been initialized */\r\n")); - - // Assume super class has OI constructor, this may not always be true but we should always be able to check this. - // In any case, it will default to old behaviour before we even checked this. - bool bSuperClassObjectInitializerConstructorDeclared = true; - FClass* SuperClass = Class->GetSuperClass(); - if (SuperClass != nullptr) - { - FClassMetaData* SuperClassData = GScriptHelper.FindClassData(SuperClass); - if (SuperClassData) - { - // Since we are dependent on our SuperClass having determined which constructors are defined, - // if it is not yet determined we will need to wait on it becoming available. - // Since the SourceFile array provided to the ParallelFor is in dependency order and does not allow cyclic dependencies, - // we can be certain that another thread has started processing the file containing our SuperClass before this - // file would have been assigned out, so we just have to wait - while (!SuperClassData->bConstructorDeclared) - { - FPlatformProcess::Sleep(0.01); - } - - bSuperClassObjectInitializerConstructorDeclared = SuperClassData->bObjectInitializerConstructorDeclared; - } - } - if (bSuperClassObjectInitializerConstructorDeclared) - { - Out.Logf(TEXT("\t%s_API %s(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { };\r\n"), API, ClassCPPName); - ClassData->bObjectInitializerConstructorDeclared = true; - } - else - { - Out.Logf(TEXT("\t%s_API %s() { };\r\n"), API, ClassCPPName); - ClassData->bDefaultConstructorDeclared = true; - } - - ClassData->bConstructorDeclared = true; - } - ExportCopyConstructorDefinition(Out, API, ClassCPPName); -} - -/** - * Generates constructor call definition. - * - * @param Out Output device to generate to. - * @param Class Class to generate constructor call definition for. - */ -void ExportDefaultConstructorCallDefinition(FOutputDevice& Out, FClassMetaData* ClassData, const TCHAR* ClassCPPName) -{ - if (ClassData->bObjectInitializerConstructorDeclared) - { - Out.Logf(TEXT("\tDEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(%s)\r\n"), ClassCPPName); - } - else if (ClassData->bDefaultConstructorDeclared) - { - Out.Logf(TEXT("\tDEFINE_DEFAULT_CONSTRUCTOR_CALL(%s)\r\n"), ClassCPPName); - } - else - { - Out.Logf(TEXT("\tDEFINE_FORBIDDEN_DEFAULT_CONSTRUCTOR_CALL(%s)\r\n"), ClassCPPName); - } -} - -/** - * Generates enhanced constructor declaration. - * - * @param Out Output device to generate to. - * @param Class Class to generate constructor for. - * @param API API string for this constructor. - */ -void ExportEnhancedConstructorsMacro(FOutputDevice& Out, FClass* Class, FClassMetaData* ClassData, const TCHAR* API, const TCHAR* ClassCPPName) -{ - ExportConstructorDefinition(Out, Class, ClassData, API, ClassCPPName); - ExportVTableHelperCtorAndCaller(Out, ClassData, API, ClassCPPName); - ExportDefaultConstructorCallDefinition(Out, ClassData, ClassCPPName); -} - -/** - * Gets a package relative inclusion path of the given source file for build. - * - * @param SourceFile Given source file. - * - * @returns Inclusion path. - */ -FString GetBuildPath(FUnrealSourceFile& SourceFile, const TMap& PackageToManifestModuleMap) -{ - FString Out = SourceFile.GetFilename(); - - ConvertToBuildIncludePath(SourceFile.GetPackage(), Out, PackageToManifestModuleMap); - - return Out; -} - -void FNativeClassHeaderGenerator::ExportConstructorsMacros(FOutputDevice& OutGeneratedHeaderText, FOutputDevice& Out, FOutputDevice& StandardUObjectConstructorsMacroCall, FOutputDevice& EnhancedUObjectConstructorsMacroCall, const FString& ConstructorsMacroPrefix, FClass* Class, const TCHAR* APIArg) -{ - const FString ClassCPPName = FNameLookupCPP::GetNameCPP(Class); - - FClassMetaData* ClassData = GScriptHelper.FindClassData(Class); - check(ClassData); - - FUHTStringBuilder StdMacro; - FUHTStringBuilder EnhMacro; - FString StdMacroName = ConstructorsMacroPrefix + TEXT("_STANDARD_CONSTRUCTORS"); - FString EnhMacroName = ConstructorsMacroPrefix + TEXT("_ENHANCED_CONSTRUCTORS"); - - ExportStandardConstructorsMacro(StdMacro, Class, ClassData, APIArg, *ClassCPPName); - ExportEnhancedConstructorsMacro(EnhMacro, Class, ClassData, APIArg, *ClassCPPName); - - if (!ClassData->bCustomVTableHelperConstructorDeclared) - { - Out.Logf(TEXT("\tDEFINE_VTABLE_PTR_HELPER_CTOR(%s);" LINE_TERMINATOR), *ClassCPPName); - } - - OutGeneratedHeaderText.Log(Macroize(*StdMacroName, *StdMacro)); - OutGeneratedHeaderText.Log(Macroize(*EnhMacroName, *EnhMacro)); - - StandardUObjectConstructorsMacroCall.Logf(TEXT("\t%s\r\n"), *StdMacroName); - EnhancedUObjectConstructorsMacroCall.Logf(TEXT("\t%s\r\n"), *EnhMacroName); -} - -bool FNativeClassHeaderGenerator::WriteHeader(const FPreloadHeaderFileInfo& FileInfo, const FString& InBodyText, const TSet& InAdditionalHeaders, FReferenceGatherers& InOutReferenceGatherers, FGraphEventRef& OutSaveTempTask) const -{ - FUHTStringBuilder GeneratedHeaderTextWithCopyright; - GeneratedHeaderTextWithCopyright.Logf(TEXT("%s"), HeaderCopyright); - GeneratedHeaderTextWithCopyright.Log(TEXT("#include \"UObject/ObjectMacros.h\"\r\n")); - GeneratedHeaderTextWithCopyright.Log(TEXT("#include \"UObject/ScriptMacros.h\"\r\n")); - - for (const FString& AdditionalHeader : InAdditionalHeaders) - { - GeneratedHeaderTextWithCopyright.Logf(TEXT("#include \"%s\"\r\n"), *AdditionalHeader); - } - - GeneratedHeaderTextWithCopyright.Log(LINE_TERMINATOR); - GeneratedHeaderTextWithCopyright.Log(DisableDeprecationWarnings); - - for (const FString& FWDecl : InOutReferenceGatherers.ForwardDeclarations) - { - if (FWDecl.Len() > 0) - { - GeneratedHeaderTextWithCopyright.Logf(TEXT("%s\r\n"), *FWDecl); - } - } - - GeneratedHeaderTextWithCopyright.Log(InBodyText); - GeneratedHeaderTextWithCopyright.Log(EnableDeprecationWarnings); - - - const bool bHasChanged = SaveHeaderIfChanged(InOutReferenceGatherers, FileInfo, MoveTemp(GeneratedHeaderTextWithCopyright), OutSaveTempTask); - return bHasChanged; -} - -/** - * Returns a string in the format CLASS_Something|CLASS_Something which represents all class flags that are set for the specified - * class which need to be exported as part of the DECLARE_CLASS macro - */ -FString FNativeClassHeaderGenerator::GetClassFlagExportText( UClass* Class ) -{ - FString StaticClassFlagText; - - check(Class); - if ( Class->HasAnyClassFlags(CLASS_Transient) ) - { - StaticClassFlagText += TEXT(" | CLASS_Transient"); - } - if( Class->HasAnyClassFlags(CLASS_DefaultConfig) ) - { - StaticClassFlagText += TEXT(" | CLASS_DefaultConfig"); - } - if( Class->HasAnyClassFlags(CLASS_GlobalUserConfig) ) - { - StaticClassFlagText += TEXT(" | CLASS_GlobalUserConfig"); - } - if( Class->HasAnyClassFlags(CLASS_Config) ) - { - StaticClassFlagText += TEXT(" | CLASS_Config"); - } - if ( Class->HasAnyClassFlags(CLASS_Interface) ) - { - StaticClassFlagText += TEXT(" | CLASS_Interface"); - } - if ( Class->HasAnyClassFlags(CLASS_Deprecated) ) - { - StaticClassFlagText += TEXT(" | CLASS_Deprecated"); - } - - return StaticClassFlagText; -} - -/** -* Exports the header text for the list of enums specified -* -* @param Enums the enums to export -*/ -void FNativeClassHeaderGenerator::ExportEnum(FOutputDevice& Out, UEnum* Enum) const -{ - // Export FOREACH macro - Out.Logf( TEXT("#define FOREACH_ENUM_%s(op) "), *Enum->GetName().ToUpper() ); - bool bHasExistingMax = Enum->ContainsExistingMax(); - int64 MaxEnumVal = bHasExistingMax ? Enum->GetMaxEnumValue() : 0; - for (int32 i = 0; i < Enum->NumEnums(); i++) - { - if (bHasExistingMax && Enum->GetValueByIndex(i) == MaxEnumVal) - { - continue; - } - - const FString QualifiedEnumValue = Enum->GetNameByIndex(i).ToString(); - Out.Logf( TEXT("\\\r\n\top(%s) "), *QualifiedEnumValue ); - } - Out.Logf( TEXT("\r\n") ); - - { - // UHTLite - FRWScopeLock Lock(HeaderParser.EnumUnderlyingTypesLock, SLT_ReadOnly); - - // Forward declare the StaticEnum<> specialisation for enum classes - if (const EUnderlyingEnumType* EnumPropType = HeaderParser.EnumUnderlyingTypes.Find(Enum)) - { - check(Enum->GetCppForm() == UEnum::ECppForm::EnumClass); - - FString UnderlyingTypeString; - - if (*EnumPropType != EUnderlyingEnumType::Unspecified) - { - UnderlyingTypeString = TEXT(" : "); - - switch (*EnumPropType) - { - case EUnderlyingEnumType::int8: UnderlyingTypeString += TNameOf::GetName(); break; - case EUnderlyingEnumType::int16: UnderlyingTypeString += TNameOf::GetName(); break; - case EUnderlyingEnumType::int32: UnderlyingTypeString += TNameOf::GetName(); break; - case EUnderlyingEnumType::int64: UnderlyingTypeString += TNameOf::GetName(); break; - case EUnderlyingEnumType::uint8: UnderlyingTypeString += TNameOf::GetName(); break; - case EUnderlyingEnumType::uint16: UnderlyingTypeString += TNameOf::GetName(); break; - case EUnderlyingEnumType::uint32: UnderlyingTypeString += TNameOf::GetName(); break; - case EUnderlyingEnumType::uint64: UnderlyingTypeString += TNameOf::GetName(); break; - default: - check(false); - } - } - - Out.Logf(TEXT("\r\n")); - Out.Logf(TEXT("enum class %s%s;\r\n"), *Enum->CppType, *UnderlyingTypeString); - Out.Logf(TEXT("template<> %sUEnum* StaticEnum<%s>();\r\n"), *GetAPIString(), *Enum->CppType); - Out.Logf(TEXT("\r\n")); - } - } -} - -// Exports the header text for the list of structs specified (GENERATED_BODY impls) -void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FOutputDevice& OutGeneratedHeaderText, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UScriptStruct* Struct) const -{ - const bool bIsDynamic = FClass::IsDynamic(Struct); - const FString ActualStructName = FNativeClassHeaderGenerator::GetOverriddenName(Struct); - const FString& FriendApiString = GetAPIString(); - - UStruct* BaseStruct = Struct->GetSuperStruct(); - - const FString StructNameCPP = FNameLookupCPP::GetNameCPP(Struct); - - const FString& SingletonName = GetSingletonName(Struct, OutReferenceGatherers.UniqueCrossModuleReferences); - const FString ChoppedSingletonName = SingletonName.LeftChop(2); - - const FString RigVMParameterPrefix = TEXT("const FName& RigVMOperatorName, int32 RigVMOperatorIndex"); - TArray RigVMVirtualFuncProlog, RigVMStubProlog; - - // for RigVM methods we need to generated a macro used for implementing the static method - // and prepare two prologs: one for the virtual function implementation, and one for the stub - // invoking the static method. - const FRigVMStructInfo* StructRigVMInfo = HeaderParser.StructRigVMMap.Find(Struct); - if(StructRigVMInfo) - { - //RigVMStubProlog.Add(FString::Printf(TEXT("ensure(RigVMOperandMemory.Num() == %d);"), StructRigVMInfo->Members.Num())); - - int32 OperandIndex = 0; - for (int32 ParameterIndex = 0; ParameterIndex < StructRigVMInfo->Members.Num(); ParameterIndex++) - { - const FRigVMParameter& Parameter = StructRigVMInfo->Members[ParameterIndex]; - if(Parameter.RequiresCast()) - { - if (Parameter.IsArray() && !Parameter.IsConst() && !Parameter.MaxArraySize.IsEmpty()) - { - RigVMVirtualFuncProlog.Add(FString::Printf(TEXT("%s.SetNum( %s );"), *Parameter.Name, *Parameter.MaxArraySize)); - } - RigVMVirtualFuncProlog.Add(FString::Printf(TEXT("%s %s(%s);"), *Parameter.CastType, *Parameter.CastName, *Parameter.Name)); - } - - FString VariableType; - FString ExtractedType; - const FString& ParamTypeOriginal = Parameter.TypeOriginal(true); - const FString& ParamNameOriginal = Parameter.NameOriginal(false); - - if (ParamTypeOriginal.StartsWith(TEXT("TArrayView"), ESearchCase::CaseSensitive)) - { - ExtractedType = Parameter.ExtendedType().LeftChop(1).RightChop(1); - VariableType = ParamTypeOriginal; - - RigVMStubProlog.Add(FString::Printf(TEXT("%s %s = TArrayView<%s>((%s*)RigVMOperandMemory[%d], reinterpret_cast(RigVMOperandMemory[%d]));"), - *VariableType, - *ParamNameOriginal, - *ExtractedType, - *ExtractedType, - OperandIndex, - OperandIndex + 1)); - - OperandIndex += 2; - } - else - { - VariableType = Parameter.TypeVariableRef(true); - ExtractedType = Parameter.TypeOriginal(); - - FString ParameterCast = FString::Printf(TEXT("*(%s*)"), *ExtractedType); - - RigVMStubProlog.Add(FString::Printf(TEXT("%s %s = %sRigVMOperandMemory[%d];"), - *VariableType, - *ParamNameOriginal, - *ParameterCast, - OperandIndex)); - - OperandIndex++; - } - } - - FString StructMembers = StructRigVMInfo->Members.Declarations(false, TEXT(", \\\r\n\t\t"), true, false); - - OutGeneratedHeaderText.Log(TEXT("\n")); - for (const FRigVMMethodInfo& MethodInfo : StructRigVMInfo->Methods) - { - FString ParameterSuffix = MethodInfo.Parameters.Declarations(true, TEXT(", \\\r\n\t\t")); - FString RigVMParameterPrefix2 = RigVMParameterPrefix + FString((StructMembers.IsEmpty() && ParameterSuffix.IsEmpty()) ? TEXT("") : TEXT(", \\\r\n\t\t")); - OutGeneratedHeaderText.Logf(TEXT("#define %s_%s() \\\r\n"), *StructNameCPP, *MethodInfo.Name); - OutGeneratedHeaderText.Logf(TEXT("\t%s %s::Static%s( \\\r\n\t\t%s%s%s \\\r\n\t)\n"), *MethodInfo.ReturnType, *StructNameCPP, *MethodInfo.Name, *RigVMParameterPrefix2, *StructMembers, *ParameterSuffix); - } - OutGeneratedHeaderText.Log(TEXT("\n")); - } - - // Export struct. - if (Struct->StructFlags & STRUCT_Native) - { - check(Struct->StructMacroDeclaredLineNumber != INDEX_NONE); - - const bool bRequiredAPI = !(Struct->StructFlags & STRUCT_RequiredAPI); - - const FString FriendLine = FString::Printf(TEXT("\tfriend struct %s_Statics;\r\n"), *ChoppedSingletonName); - const FString StaticClassLine = FString::Printf(TEXT("\t%sstatic class UScriptStruct* StaticStruct();\r\n"), (bRequiredAPI ? *FriendApiString : TEXT(""))); - - // if we have RigVM methods on this struct we need to - // declare the static method as well as the stub method - FString RigVMMethodsDeclarations; - if (StructRigVMInfo) - { - FString StructMembers = StructRigVMInfo->Members.Declarations(false, TEXT(",\r\n\t\t"), true, false); - for (const FRigVMMethodInfo& MethodInfo : StructRigVMInfo->Methods) - { - FString StructMembersForStub = StructRigVMInfo->Members.Names(false, TEXT(",\r\n\t\t\t"), false); - FString ParameterSuffix = MethodInfo.Parameters.Declarations(true, TEXT(",\r\n\t\t")); - FString ParameterNamesSuffix = MethodInfo.Parameters.Names(true, TEXT(",\r\n\t\t\t")); - FString RigVMParameterPrefix2 = RigVMParameterPrefix + FString((StructMembers.IsEmpty() && ParameterSuffix.IsEmpty()) ? TEXT("") : TEXT(",\r\n\t\t")); - FString RigVMParameterPrefix4 = FString(TEXT("RigVMOperatorName,\r\n\t\t\tRigVMOperatorIndex")) + FString((StructMembersForStub.IsEmpty() && ParameterSuffix.IsEmpty()) ? TEXT("") : TEXT(",\r\n\t\t\t")); - - RigVMMethodsDeclarations += FString::Printf(TEXT("\tstatic %s Static%s(\r\n\t\t%s%s%s\r\n\t);\r\n"), *MethodInfo.ReturnType, *MethodInfo.Name, *RigVMParameterPrefix2, *StructMembers, *ParameterSuffix); - RigVMMethodsDeclarations += FString::Printf(TEXT("\tFORCEINLINE static %s RigVM%s(\r\n\t\t%s,\r\n\t\tFRigVMOperandMemory RigVMOperandMemory,\r\n\t\tconst FRigVMUserDataArray& RigVMUserData\r\n\t)\r\n"), *MethodInfo.ReturnType, *MethodInfo.Name, *RigVMParameterPrefix); - RigVMMethodsDeclarations += FString::Printf(TEXT("\t{\r\n")); - - // implement inline stub method body - if (MethodInfo.Parameters.Num() > 0) - { - //RigVMMethodsDeclarations += FString::Printf(TEXT("\t\tensure(RigVMUserData.Num() == %d);\r\n"), MethodInfo.Parameters.Num()); - for (int32 ParameterIndex = 0; ParameterIndex < MethodInfo.Parameters.Num(); ParameterIndex++) - { - const FRigVMParameter& Parameter = MethodInfo.Parameters[ParameterIndex]; - RigVMMethodsDeclarations += FString::Printf(TEXT("\t\t%s = *(%s*)RigVMUserData[%d];\r\n"), *Parameter.Declaration(), *Parameter.TypeNoRef(), ParameterIndex); - } - RigVMMethodsDeclarations += FString::Printf(TEXT("\t\t\r\n")); - } - - if (RigVMStubProlog.Num() > 0) - { - for (const FString& RigVMStubPrologLine : RigVMStubProlog) - { - RigVMMethodsDeclarations += FString::Printf(TEXT("\t\t%s\r\n"), *RigVMStubPrologLine); - } - RigVMMethodsDeclarations += FString::Printf(TEXT("\t\t\r\n")); - } - - RigVMMethodsDeclarations += FString::Printf(TEXT("\t\t%sStatic%s(\r\n\t\t\t%s%s%s\r\n\t\t);\r\n"), *MethodInfo.ReturnPrefix(), *MethodInfo.Name, *RigVMParameterPrefix4, *StructMembersForStub, *ParameterNamesSuffix); - RigVMMethodsDeclarations += FString::Printf(TEXT("\t}\r\n")); - } - - for (const FRigVMParameter& StructMember : StructRigVMInfo->Members) - { - if (!StructMember.MaxArraySize.IsEmpty()) - { - RigVMMethodsDeclarations += TEXT("\tvirtual int32 GetMaxArraySize(const FName& InMemberName, const FRigVMUserDataArray& RigVMUserData) override;\r\n"); - break; - } - } - } - - const FString SuperTypedef = BaseStruct ? FString::Printf(TEXT("\ttypedef %s Super;\r\n"), *FNameLookupCPP::GetNameCPP(BaseStruct)) : FString(); - - FString CombinedLine = FString::Printf(TEXT("%s%s%s%s"), *FriendLine, *StaticClassLine, *RigVMMethodsDeclarations, *SuperTypedef); - const FString MacroName = SourceFile.GetGeneratedBodyMacroName(Struct->StructMacroDeclaredLineNumber); - - const FString Macroized = Macroize(*MacroName, MoveTemp(CombinedLine)); - OutGeneratedHeaderText.Log(Macroized); - - FString GetHashName = FString::Printf(TEXT("Get_%s_Hash"), *ChoppedSingletonName); - - Out.Logf(TEXT("class UScriptStruct* %s::StaticStruct()\r\n"), *StructNameCPP); - Out.Logf(TEXT("{\r\n")); - - // UStructs can have UClass or UPackage outer (if declared in non-UClass headers). - const FString& OuterName (bIsDynamic ? STRING_StructPackage : GetPackageSingletonName(CastChecked(Struct->GetOuter()), OutReferenceGatherers.UniqueCrossModuleReferences)); - if (!bIsDynamic) - { - Out.Logf(TEXT("\tstatic class UScriptStruct* Singleton = NULL;\r\n")); - } - else - { - Out.Logf(TEXT("\tclass UPackage* %s = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *OuterName, *FClass::GetTypePackageName(Struct)); - Out.Logf(TEXT("\tclass UScriptStruct* Singleton = Cast(StaticFindObjectFast(UScriptStruct::StaticClass(), %s, TEXT(\"%s\")));\r\n"), *OuterName, *ActualStructName); - } - - Out.Logf(TEXT("\tif (!Singleton)\r\n")); - Out.Logf(TEXT("\t{\r\n")); - Out.Logf(TEXT("\t\textern %suint32 %s();\r\n"), *FriendApiString, *GetHashName); - - Out.Logf(TEXT("\t\tSingleton = GetStaticStruct(%s, %s, TEXT(\"%s\"), sizeof(%s), %s());\r\n"), - *ChoppedSingletonName, *OuterName, *ActualStructName, *StructNameCPP, *GetHashName); - - // if this struct has RigVM methods - we need to register the method to our central - // registry on construction of the static struct - if (StructRigVMInfo) - { - for (const FRigVMMethodInfo& MethodInfo : StructRigVMInfo->Methods) - { - Out.Logf(TEXT("\t\tFRigVMRegistry::Get().Register(TEXT(\"%s::%s\"), &%s::RigVM%s, Singleton);\r\n"), - *StructNameCPP, *MethodInfo.Name, *StructNameCPP, *MethodInfo.Name); - } - } - - Out.Logf(TEXT("\t}\r\n")); - Out.Logf(TEXT("\treturn Singleton;\r\n")); - Out.Logf(TEXT("}\r\n")); - - // Forward declare the StaticStruct specialisation in the header - OutGeneratedHeaderText.Logf(TEXT("template<> %sUScriptStruct* StaticStruct();\r\n\r\n"), *GetAPIString(), *StructNameCPP); - - // Generate the StaticStruct specialisation - Out.Logf(TEXT("template<> %sUScriptStruct* StaticStruct<%s>()\r\n"), *GetAPIString(), *StructNameCPP); - Out.Logf(TEXT("{\r\n")); - Out.Logf(TEXT("\treturn %s::StaticStruct();\r\n"), *StructNameCPP); - Out.Logf(TEXT("}\r\n")); - - if (bIsDynamic) - { - const FString& StructPackageName = FClass::GetTypePackageName(Struct); - Out.Logf(TEXT("static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_%s(%s::StaticStruct, TEXT(\"%s\"), TEXT(\"%s\"), true, %s, %s);\r\n"), - *StructNameCPP, - *StructNameCPP, - *StructPackageName, - *ActualStructName, - *AsTEXT(StructPackageName), - *AsTEXT(FNativeClassHeaderGenerator::GetOverriddenPathName(Struct))); - } - else - { - Out.Logf(TEXT("static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_%s(%s::StaticStruct, TEXT(\"%s\"), TEXT(\"%s\"), false, nullptr, nullptr);\r\n"), - *StructNameCPP, - *StructNameCPP, - *Struct->GetOutermost()->GetName(), - *ActualStructName); - } - - // Generate StaticRegisterNatives equivalent for structs without classes. - if (!Struct->GetOuter()->IsA(UStruct::StaticClass())) - { - const FString ShortPackageName = FPackageName::GetShortName(Struct->GetOuter()->GetName()); - Out.Logf(TEXT("static struct FScriptStruct_%s_StaticRegisterNatives%s\r\n"), *ShortPackageName, *StructNameCPP); - Out.Logf(TEXT("{\r\n")); - Out.Logf(TEXT("\tFScriptStruct_%s_StaticRegisterNatives%s()\r\n"), *ShortPackageName, *StructNameCPP); - Out.Logf(TEXT("\t{\r\n")); - - Out.Logf(TEXT("\t\tUScriptStruct::DeferCppStructOps(FName(TEXT(\"%s\")),new UScriptStruct::TCppStructOps<%s>);\r\n"), *ActualStructName, *StructNameCPP); - - Out.Logf(TEXT("\t}\r\n")); - Out.Logf(TEXT("} ScriptStruct_%s_StaticRegisterNatives%s;\r\n"), *ShortPackageName, *StructNameCPP); - } - } - - FString StaticsStructName = ChoppedSingletonName + TEXT("_Statics"); - - FUHTStringBuilder GeneratedStructRegisterFunctionText; - FUHTStringBuilder StaticDefinitions; - - GeneratedStructRegisterFunctionText.Logf(TEXT("\tstruct %s\r\n"), *StaticsStructName); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t{\r\n")); - - // if this is a no export struct, we will put a local struct here for offset determination - TArray NoExportStructs = FindNoExportStructs(Struct); - for (UScriptStruct* NoExportStruct : NoExportStructs) - { - ExportMirrorsForNoexportStruct(GeneratedStructRegisterFunctionText, NoExportStruct, /*Indent=*/ 2); - } - - if (BaseStruct) - { - CastChecked(BaseStruct); // this better actually be a script struct - GetSingletonName(BaseStruct, OutReferenceGatherers.UniqueCrossModuleReferences); // Call to potentially collect references - } - - EStructFlags UncomputedFlags = (EStructFlags)(Struct->StructFlags & ~STRUCT_ComputedFlags); - - FString OuterFunc; - if (!bIsDynamic) - { - OuterFunc = GetPackageSingletonName(CastChecked(Struct->GetOuter()), OutReferenceGatherers.UniqueCrossModuleReferences).LeftChop(2); - } - else - { - GeneratedStructRegisterFunctionText.Log(TEXT("\t\tstatic UObject* OuterFuncGetter();\r\n")); - - StaticDefinitions.Logf(TEXT("\tUObject* %s::OuterFuncGetter()\r\n"), *StaticsStructName); - StaticDefinitions.Log (TEXT("\t{\r\n")); - StaticDefinitions.Logf(TEXT("\t\treturn FindOrConstructDynamicTypePackage(TEXT(\"%s\"));"), *FClass::GetTypePackageName(Struct)); - StaticDefinitions.Log (TEXT("\t}\r\n")); - - OuterFunc = TEXT("&OuterFuncGetter"); - } - - FString MetaDataParams = OutputMetaDataCodeForObject(GeneratedStructRegisterFunctionText, StaticDefinitions, Struct, *FString::Printf(TEXT("%s::Struct_MetaDataParams"), *StaticsStructName), TEXT("\t\t"), TEXT("\t")); - - TArray Props; - Algo::Copy(TFieldRange(Struct, EFieldIteratorFlags::ExcludeSuper), Props, Algo::NoRef); - - FString NewStructOps; - if (Struct->StructFlags & STRUCT_Native) - { - GeneratedStructRegisterFunctionText.Log(TEXT("\t\tstatic void* NewStructOps();\r\n")); - - StaticDefinitions.Logf(TEXT("\tvoid* %s::NewStructOps()\r\n"), *StaticsStructName); - StaticDefinitions.Log (TEXT("\t{\r\n")); - StaticDefinitions.Logf(TEXT("\t\treturn (UScriptStruct::ICppStructOps*)new UScriptStruct::TCppStructOps<%s>();\r\n"), *StructNameCPP); - StaticDefinitions.Log (TEXT("\t}\r\n")); - - NewStructOps = TEXT("&NewStructOps"); - } - else - { - NewStructOps = TEXT("nullptr"); - } - - TTuple PropertyRange = OutputProperties(GeneratedStructRegisterFunctionText, StaticDefinitions, OutReferenceGatherers, *FString::Printf(TEXT("%s::"), *StaticsStructName), Props, TEXT("\t\t"), TEXT("\t")); - - GeneratedStructRegisterFunctionText.Log (TEXT("\t\tstatic const UE4CodeGen_Private::FStructParams ReturnStructParams;\r\n")); - - StaticDefinitions.Logf(TEXT("\tconst UE4CodeGen_Private::FStructParams %s::ReturnStructParams = {\r\n"), *StaticsStructName); - StaticDefinitions.Logf(TEXT("\t\t(UObject* (*)())%s,\r\n"), *OuterFunc); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), *GetSingletonNameFuncAddr(BaseStruct, OutReferenceGatherers.UniqueCrossModuleReferences)); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), *NewStructOps); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), *CreateUTF8LiteralString(ActualStructName)); - StaticDefinitions.Logf(TEXT("\t\tsizeof(%s),\r\n"), *StructNameCPP); - StaticDefinitions.Logf(TEXT("\t\talignof(%s),\r\n"), *StructNameCPP); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), *PropertyRange.Get<0>()); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), *PropertyRange.Get<1>()); - StaticDefinitions.Logf(TEXT("\t\t%s,\r\n"), bIsDynamic ? TEXT("RF_Public|RF_Transient") : TEXT("RF_Public|RF_Transient|RF_MarkAsNative")); - StaticDefinitions.Logf(TEXT("\t\tEStructFlags(0x%08X),\r\n"), (uint32)UncomputedFlags); - StaticDefinitions.Logf(TEXT("\t\t%s\r\n"), *MetaDataParams); - StaticDefinitions.Log (TEXT("\t};\r\n")); - - GeneratedStructRegisterFunctionText.Log (TEXT("\t};\r\n")); - - GeneratedStructRegisterFunctionText.Log(StaticDefinitions); - - GeneratedStructRegisterFunctionText.Logf(TEXT("\tUScriptStruct* %s\r\n"), *SingletonName); - GeneratedStructRegisterFunctionText.Log (TEXT("\t{\r\n")); - - FString NoExportStructNameCPP; - if (NoExportStructs.Contains(Struct)) - { - NoExportStructNameCPP = FString::Printf(TEXT("%s::%s"), *StaticsStructName, *StructNameCPP); - } - else - { - NoExportStructNameCPP = StructNameCPP; - } - - FString HashFuncName = FString::Printf(TEXT("Get_%s_Hash"), *SingletonName.Replace(TEXT("()"), TEXT(""), ESearchCase::CaseSensitive)); - // Structs can either have a UClass or UPackage as outer (if declared in non-UClass header). - if (!bIsDynamic) - { - GeneratedStructRegisterFunctionText.Log (TEXT("#if WITH_HOT_RELOAD\r\n")); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\textern uint32 %s();\r\n"), *HashFuncName); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer = %s;\r\n"), *GetPackageSingletonName(CastChecked(Struct->GetOuter()), OutReferenceGatherers.UniqueCrossModuleReferences)); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tstatic UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT(\"%s\"), sizeof(%s), %s(), false);\r\n"), *ActualStructName, *NoExportStructNameCPP, *HashFuncName); - GeneratedStructRegisterFunctionText.Log (TEXT("#else\r\n")); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tstatic UScriptStruct* ReturnStruct = nullptr;\r\n")); - GeneratedStructRegisterFunctionText.Log (TEXT("#endif\r\n")); - } - else - { - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\textern uint32 %s();\r\n"), *HashFuncName); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *FClass::GetTypePackageName(Struct)); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT(\"%s\"), sizeof(%s), %s(), true);\r\n"), *ActualStructName, *NoExportStructNameCPP, *HashFuncName); - } - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tif (!ReturnStruct)\r\n")); - GeneratedStructRegisterFunctionText.Log (TEXT("\t\t{\r\n")); - - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\t\tUE4CodeGen_Private::ConstructUScriptStruct(ReturnStruct, %s::ReturnStructParams);\r\n"), *StaticsStructName); - GeneratedStructRegisterFunctionText.Log (TEXT("\t\t}\r\n")); - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\treturn ReturnStruct;\r\n")); - GeneratedStructRegisterFunctionText.Log (TEXT("\t}\r\n")); - - uint32 StructHash = GenerateTextHash(*GeneratedStructRegisterFunctionText); - AddGeneratedCodeHash(Struct, StructHash); - - Out.Log(GeneratedStructRegisterFunctionText); - Out.Logf(TEXT("\tuint32 %s() { return %uU; }\r\n"), *HashFuncName, StructHash); - - // if this struct has RigVM methods we need to implement both the - // virtual function as well as the stub method here. - // The static method is implemented by the user using a macro. - if (StructRigVMInfo) - { - FString StructMembersForVirtualFunc = StructRigVMInfo->Members.Names(false, TEXT(",\r\n\t\t"), true); - - for (const FRigVMMethodInfo& MethodInfo : StructRigVMInfo->Methods) - { - Out.Log(TEXT("\r\n")); - - FString ParameterDeclaration = MethodInfo.Parameters.Declarations(false, TEXT(",\r\n\t\t")); - FString ParameterSuffix = MethodInfo.Parameters.Names(true, TEXT(",\r\n\t\t")); - FString RigVMParameterPrefix2 = RigVMParameterPrefix + FString((StructMembersForVirtualFunc.IsEmpty() && ParameterSuffix.IsEmpty()) ? TEXT("") : TEXT(",\r\n\t\t")); - FString RigVMParameterPrefix3 = FString(TEXT("NAME_None,\r\n\t\tINDEX_NONE")) + FString((StructMembersForVirtualFunc.IsEmpty() && ParameterSuffix.IsEmpty()) ? TEXT("") : TEXT(",\r\n\t\t")); - - // implement the virtual function body. - Out.Logf(TEXT("%s %s::%s(%s)\r\n"), *MethodInfo.ReturnType, *StructNameCPP, *MethodInfo.Name, *ParameterDeclaration); - Out.Log(TEXT("{\r\n")); - - if(RigVMVirtualFuncProlog.Num() > 0) - { - for (const FString& RigVMVirtualFuncPrologLine : RigVMVirtualFuncProlog) - { - Out.Logf(TEXT("\t%s\r\n"), *RigVMVirtualFuncPrologLine); - } - Out.Log(TEXT("\t\r\n")); - } - - Out.Logf(TEXT(" %sStatic%s(\r\n\t\t%s%s%s\r\n\t);\n"), *MethodInfo.ReturnPrefix(), *MethodInfo.Name, *RigVMParameterPrefix3, *StructMembersForVirtualFunc, *ParameterSuffix); - Out.Log(TEXT("}\r\n")); - } - - Out.Log(TEXT("\r\n")); - - bool bHasGetMaxArraySize = false; - for (const FRigVMParameter& StructMember : StructRigVMInfo->Members) - { - if (!StructMember.MaxArraySize.IsEmpty()) - { - bHasGetMaxArraySize = true; - break; - } - } - - if (bHasGetMaxArraySize) - { - Out.Logf(TEXT("int32 %s::GetMaxArraySize(const FName& InMemberName, const FRigVMUserDataArray& RigVMUserData)\r\n"), *StructNameCPP); - Out.Log(TEXT("{\r\n")); - for (const FRigVMParameter& StructMember : StructRigVMInfo->Members) - { - if (!StructMember.MaxArraySize.IsEmpty()) - { - Out.Logf(TEXT("\tif(InMemberName == TEXT(\"%s\"))\r\n"), *StructMember.Name); - Out.Log(TEXT("\t{\r\n")); - Out.Logf(TEXT("\t\treturn %s;\r\n"), *StructMember.MaxArraySize); - Out.Log(TEXT("\t}\r\n")); - } - } - Out.Log(TEXT("\treturn INDEX_NONE;\r\n")); - Out.Log(TEXT("}\r\n\r\n")); - } - } -} - -void FNativeClassHeaderGenerator::ExportGeneratedEnumInitCode(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UEnum* Enum) const -{ - const bool bIsDynamic = FClass::IsDynamic(static_cast(Enum)); - const FString SingletonName = GetSingletonNameFuncAddr(Enum, OutReferenceGatherers.UniqueCrossModuleReferences); - const FString EnumNameCpp = Enum->GetName(); //UserDefinedEnum should already have a valid cpp name. - const FString OverriddenEnumNameCpp = FNativeClassHeaderGenerator::GetOverriddenName(Enum); - - const bool bIsEditorOnlyDataType = HeaderParser.EditorOnlyDataTypes.Contains(Enum); - - FMacroBlockEmitter EditorOnlyData(Out, TEXT("WITH_EDITORONLY_DATA")); - EditorOnlyData(bIsEditorOnlyDataType); - - const FString& PackageSingletonName = (bIsDynamic ? FClass::GetTypePackageName(static_cast(Enum)) : GetPackageSingletonName(CastChecked(Enum->GetOuter()), OutReferenceGatherers.UniqueCrossModuleReferences)); - - Out.Logf(TEXT("\tstatic UEnum* %s_StaticEnum()\r\n"), *Enum->GetName()); - Out.Logf(TEXT("\t{\r\n")); - - if (!bIsDynamic) - { - Out.Logf(TEXT("\t\tstatic UEnum* Singleton = nullptr;\r\n")); - } - else - { - Out.Logf(TEXT("\t\tclass UPackage* EnumPackage = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *PackageSingletonName); - Out.Logf(TEXT("\t\tclass UEnum* Singleton = Cast(StaticFindObjectFast(UEnum::StaticClass(), EnumPackage, TEXT(\"%s\")));\r\n"), *OverriddenEnumNameCpp); - } - Out.Logf(TEXT("\t\tif (!Singleton)\r\n")); - Out.Logf(TEXT("\t\t{\r\n")); - if (!bIsDynamic) - { - Out.Logf(TEXT("\t\t\tSingleton = GetStaticEnum(%s, %s, TEXT(\"%s\"));\r\n"), *SingletonName, *PackageSingletonName, *Enum->GetName()); - } - else - { - Out.Logf(TEXT("\t\t\tSingleton = GetStaticEnum(%s, EnumPackage, TEXT(\"%s\"));\r\n"), *SingletonName, *OverriddenEnumNameCpp); - } - - Out.Logf(TEXT("\t\t}\r\n")); - Out.Logf(TEXT("\t\treturn Singleton;\r\n")); - Out.Logf(TEXT("\t}\r\n")); - - Out.Logf(TEXT("\ttemplate<> %sUEnum* StaticEnum<%s>()\r\n"), *GetAPIString(), *Enum->CppType); - Out.Logf(TEXT("\t{\r\n")); - Out.Logf(TEXT("\t\treturn %s_StaticEnum();\r\n"), *Enum->GetName()); - Out.Logf(TEXT("\t}\r\n")); - - if (bIsDynamic) - { - const FString& EnumPackageName = FClass::GetTypePackageName(static_cast(Enum)); - Out.Logf( - TEXT("\tstatic FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_%s(%s_StaticEnum, TEXT(\"%s\"), TEXT(\"%s\"), true, %s, %s);\r\n"), - *EnumNameCpp, - *EnumNameCpp, - *EnumPackageName, - *OverriddenEnumNameCpp, - *AsTEXT(EnumPackageName), - *AsTEXT(FNativeClassHeaderGenerator::GetOverriddenPathName(static_cast(Enum))) - ); - } - else - { - Out.Logf( - TEXT("\tstatic FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_%s(%s_StaticEnum, TEXT(\"%s\"), TEXT(\"%s\"), false, nullptr, nullptr);\r\n"), - *EnumNameCpp, - *EnumNameCpp, - *Enum->GetOutermost()->GetName(), - *OverriddenEnumNameCpp - ); - } - - const FString& EnumSingletonName = GetSingletonName(Enum, OutReferenceGatherers.UniqueCrossModuleReferences); - const FString HashFuncName = FString::Printf(TEXT("Get_%s_Hash"), *SingletonName); - - FUHTStringBuilder GeneratedEnumRegisterFunctionText; - - GeneratedEnumRegisterFunctionText.Logf(TEXT("\tUEnum* %s\r\n"), *EnumSingletonName); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t{\r\n")); - - // Enums can either have a UClass or UPackage as outer (if declared in non-UClass header). - FString OuterString; - if (!bIsDynamic) - { - OuterString = PackageSingletonName; - GeneratedEnumRegisterFunctionText.Logf(TEXT("#if WITH_HOT_RELOAD\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer = %s;\r\n"), *OuterString); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tstatic UEnum* ReturnEnum = FindExistingEnumIfHotReloadOrDynamic(Outer, TEXT(\"%s\"), 0, %s(), false);\r\n"), *EnumNameCpp, *HashFuncName); - GeneratedEnumRegisterFunctionText.Logf(TEXT("#else\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tstatic UEnum* ReturnEnum = nullptr;\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("#endif // WITH_HOT_RELOAD\r\n")); - } - else - { - OuterString = FString::Printf(TEXT("[](){ return (UObject*)FindOrConstructDynamicTypePackage(TEXT(\"%s\")); }()"), *PackageSingletonName); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));"), *PackageSingletonName); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tUEnum* ReturnEnum = FindExistingEnumIfHotReloadOrDynamic(Outer, TEXT(\"%s\"), 0, %s(), true);\r\n"), *OverriddenEnumNameCpp, *HashFuncName); - } - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tif (!ReturnEnum)\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t{\r\n")); - - const TCHAR* UEnumObjectFlags = bIsDynamic ? TEXT("RF_Public|RF_Transient") : TEXT("RF_Public|RF_Transient|RF_MarkAsNative"); - - const TCHAR* EnumFormStr = TEXT(""); - switch (Enum->GetCppForm()) - { - case UEnum::ECppForm::Regular: EnumFormStr = TEXT("UEnum::ECppForm::Regular"); break; - case UEnum::ECppForm::Namespaced: EnumFormStr = TEXT("UEnum::ECppForm::Namespaced"); break; - case UEnum::ECppForm::EnumClass: EnumFormStr = TEXT("UEnum::ECppForm::EnumClass"); break; - } - - const FString& EnumDisplayNameFn = Enum->GetMetaData(TEXT("EnumDisplayNameFn")); - - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\tstatic const UE4CodeGen_Private::FEnumeratorParam Enumerators[] = {\r\n")); - for (int32 Index = 0; Index != Enum->NumEnums(); ++Index) - { - const TCHAR* OverridenNameMetaDatakey = TEXT("OverrideName"); - const FString KeyName = Enum->HasMetaData(OverridenNameMetaDatakey, Index) ? Enum->GetMetaData(OverridenNameMetaDatakey, Index) : Enum->GetNameByIndex(Index).ToString(); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t{ %s, (int64)%s },\r\n"), *CreateUTF8LiteralString(KeyName), *Enum->GetNameByIndex(Index).ToString()); - } - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t};\r\n")); - - FOutputDeviceNull OutputDeviceNull; - FString MetaDataParams = OutputMetaDataCodeForObject(OutputDeviceNull, GeneratedEnumRegisterFunctionText, Enum, TEXT("Enum_MetaDataParams"), TEXT(""), TEXT("\t\t\t")); - - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\tstatic const UE4CodeGen_Private::FEnumParams EnumParams = {\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t(UObject*(*)())%s,\r\n"), *OuterString.LeftChop(2)); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t%s,\r\n"), EnumDisplayNameFn.IsEmpty() ? TEXT("nullptr") : *EnumDisplayNameFn); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t%s,\r\n"), *CreateUTF8LiteralString(OverriddenEnumNameCpp)); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t%s,\r\n"), *CreateUTF8LiteralString(Enum->CppType)); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\tEnumerators,\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\tUE_ARRAY_COUNT(Enumerators),\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t%s,\r\n"), UEnumObjectFlags); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\tUE4CodeGen_Private::EDynamicType::%s,\r\n"), bIsDynamic ? TEXT("Dynamic") : TEXT("NotDynamic")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t(uint8)%s,\r\n"), EnumFormStr); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t\t%s\r\n"), *MetaDataParams); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\t};\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\tUE4CodeGen_Private::ConstructUEnum(ReturnEnum, EnumParams);\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t}\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\treturn ReturnEnum;\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t}\r\n")); - - uint32 EnumHash = GenerateTextHash(*GeneratedEnumRegisterFunctionText); - Out.Logf(TEXT("\tuint32 %s() { return %uU; }\r\n"), *HashFuncName, EnumHash); - Out.Log(GeneratedEnumRegisterFunctionText); -} - -void FNativeClassHeaderGenerator::ExportMirrorsForNoexportStruct(FOutputDevice& Out, UScriptStruct* Struct, int32 TextIndent) const -{ - // Export struct. - const FString StructName = FNameLookupCPP::GetNameCPP(Struct); - Out.Logf(TEXT("%sstruct %s"), FCString::Tab(TextIndent), *StructName); - if (Struct->GetSuperStruct() != NULL) - { - Out.Logf(TEXT(" : public %s"), *FNameLookupCPP::GetNameCPP(Struct->GetSuperStruct())); - } - Out.Logf(TEXT("\r\n%s{\r\n"), FCString::Tab(TextIndent)); - - // Export the struct's CPP properties. - ExportProperties(Out, Struct, TextIndent); - - Out.Logf(TEXT("%s};\r\n\r\n"), FCString::Tab(TextIndent)); -} - -bool FNativeClassHeaderGenerator::WillExportEventParms( UFunction* Function ) -{ - TFieldIterator It(Function); - return It && (It->PropertyFlags&CPF_Parm); -} - -void WriteEventFunctionPrologue(FOutputDevice& Output, int32 Indent, const FParmsAndReturnProperties& Parameters, UObject* FunctionOuter, const TCHAR* FunctionName) -{ - // now the body - first we need to declare a struct which will hold the parameters for the event/delegate call - Output.Logf(TEXT("\r\n%s{\r\n"), FCString::Tab(Indent)); - - // declare and zero-initialize the parameters and return value, if applicable - if (!Parameters.HasParms()) - return; - - FString EventStructName = GetEventStructParamsName(FunctionOuter, FunctionName); - - Output.Logf(TEXT("%s%s Parms;\r\n"), FCString::Tab(Indent + 1), *EventStructName ); - - // Declare a parameter struct for this event/delegate and assign the struct members using the values passed into the event/delegate call. - for (FProperty* Prop : Parameters.Parms) - { - const FString PropertyName = Prop->GetName(); - if (Prop->ArrayDim > 1) - { - Output.Logf(TEXT("%sFMemory::Memcpy(Parms.%s,%s,sizeof(Parms.%s));\r\n"), FCString::Tab(Indent + 1), *PropertyName, *PropertyName, *PropertyName); - } - else - { - FString ValueAssignmentText = PropertyName; - if (Prop->IsA()) - { - ValueAssignmentText += TEXT(" ? true : false"); - } - - Output.Logf(TEXT("%sParms.%s=%s;\r\n"), FCString::Tab(Indent + 1), *PropertyName, *ValueAssignmentText); - } - } -} - -void WriteEventFunctionEpilogue(FOutputDevice& Output, int32 Indent, const FParmsAndReturnProperties& Parameters) -{ - // Out parm copying. - for (FProperty* Prop : Parameters.Parms) - { - if ((Prop->PropertyFlags & (CPF_OutParm | CPF_ConstParm)) == CPF_OutParm) - { - const FString PropertyName = Prop->GetName(); - if ( Prop->ArrayDim > 1 ) - { - Output.Logf(TEXT("%sFMemory::Memcpy(&%s,&Parms.%s,sizeof(%s));\r\n"), FCString::Tab(Indent + 1), *PropertyName, *PropertyName, *PropertyName); - } - else - { - Output.Logf(TEXT("%s%s=Parms.%s;\r\n"), FCString::Tab(Indent + 1), *PropertyName, *PropertyName); - } - } - } - - // Return value. - if (Parameters.Return) - { - // Make sure uint32 -> bool is supported - bool bBoolProperty = Parameters.Return->IsA(FBoolProperty::StaticClass()); - Output.Logf(TEXT("%sreturn %sParms.%s;\r\n"), FCString::Tab(Indent + 1), bBoolProperty ? TEXT("!!") : TEXT(""), *Parameters.Return->GetName()); - } - Output.Logf(TEXT("%s}\r\n"), FCString::Tab(Indent)); -} - -void FNativeClassHeaderGenerator::ExportDelegateDeclaration(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UFunction* Function) const -{ - static const TCHAR DelegateStr[] = TEXT("delegate"); - - check(Function->HasAnyFunctionFlags(FUNC_Delegate)); - - const bool bIsMulticastDelegate = Function->HasAnyFunctionFlags( FUNC_MulticastDelegate ); - - // Unmangle the function name - const FString DelegateName = Function->GetName().LeftChop(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH); - - const FFunctionData* CompilerInfo = FFunctionData::FindForFunction(Function); - - FFuncInfo FunctionData = CompilerInfo->GetFunctionData(); - - // Add class name to beginning of function, to avoid collisions with other classes with the same delegate name in this scope - check(FunctionData.MarshallAndCallName.StartsWith(DelegateStr)); - FString ShortName = *FunctionData.MarshallAndCallName + UE_ARRAY_COUNT(DelegateStr) - 1; - FunctionData.MarshallAndCallName = FString::Printf( TEXT( "F%s_DelegateWrapper" ), *ShortName ); - - // Setup delegate parameter - const FString ExtraParam = FString::Printf( - TEXT( "const %s& %s" ), - bIsMulticastDelegate ? TEXT( "FMulticastScriptDelegate" ) : TEXT( "FScriptDelegate" ), - *DelegateName - ); - - FUHTStringBuilder DelegateOutput; - DelegateOutput.Log(TEXT("static ")); - - // export the line that looks like: int32 Main(const FString& Parms) - ExportNativeFunctionHeader(DelegateOutput, OutReferenceGatherers.ForwardDeclarations, FunctionData, EExportFunctionType::Event, EExportFunctionHeaderStyle::Declaration, *ExtraParam, *GetAPIString()); - - // Only exporting function prototype - DelegateOutput.Logf(TEXT(";\r\n")); - - ExportFunction(Out, OutReferenceGatherers, SourceFile, Function, false); -} - -void FNativeClassHeaderGenerator::ExportDelegateDefinition(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UFunction* Function) const -{ - const TCHAR DelegateStr[] = TEXT("delegate"); - - check(Function->HasAnyFunctionFlags(FUNC_Delegate)); - - // Export parameters structs for all delegates. We'll need these to declare our delegate execution function. - FUHTStringBuilder DelegateOutput; - ExportEventParm(DelegateOutput, OutReferenceGatherers.ForwardDeclarations, Function, /*Indent=*/ 0, /*bOutputConstructor=*/ true, EExportingState::Normal); - - const bool bIsMulticastDelegate = Function->HasAnyFunctionFlags( FUNC_MulticastDelegate ); - - // Unmangle the function name - const FString DelegateName = Function->GetName().LeftChop(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH); - - const FFunctionData* CompilerInfo = FFunctionData::FindForFunction(Function); - - FFuncInfo FunctionData = CompilerInfo->GetFunctionData(); - - // Always export delegate wrapper functions as inline - FunctionData.FunctionExportFlags |= FUNCEXPORT_Inline; - - // Add class name to beginning of function, to avoid collisions with other classes with the same delegate name in this scope - check(FunctionData.MarshallAndCallName.StartsWith(DelegateStr)); - FString ShortName = *FunctionData.MarshallAndCallName + UE_ARRAY_COUNT(DelegateStr) - 1; - FunctionData.MarshallAndCallName = FString::Printf( TEXT( "F%s_DelegateWrapper" ), *ShortName ); - - // Setup delegate parameter - const FString ExtraParam = FString::Printf( - TEXT( "const %s& %s" ), - bIsMulticastDelegate ? TEXT( "FMulticastScriptDelegate" ) : TEXT( "FScriptDelegate" ), - *DelegateName - ); - - DelegateOutput.Log(TEXT("static ")); - - // export the line that looks like: int32 Main(const FString& Parms) - ExportNativeFunctionHeader(DelegateOutput, OutReferenceGatherers.ForwardDeclarations, FunctionData, EExportFunctionType::Event, EExportFunctionHeaderStyle::Declaration, *ExtraParam, *GetAPIString()); - - FParmsAndReturnProperties Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference); - - WriteEventFunctionPrologue(DelegateOutput, 0, Parameters, Function->GetOuter(), *DelegateName); - { - const TCHAR* DelegateType = bIsMulticastDelegate ? TEXT( "ProcessMulticastDelegate" ) : TEXT( "ProcessDelegate" ); - const TCHAR* DelegateArg = Parameters.HasParms() ? TEXT("&Parms") : TEXT("NULL"); - DelegateOutput.Logf(TEXT("\t%s.%s(%s);\r\n"), *DelegateName, DelegateType, DelegateArg); - } - WriteEventFunctionEpilogue(DelegateOutput, 0, Parameters); - - FString MacroName = SourceFile.GetGeneratedMacroName(FunctionData.MacroLine, TEXT("_DELEGATE")); - WriteMacro(Out, MacroName, DelegateOutput); -} - -void FNativeClassHeaderGenerator::ExportEventParm(FUHTStringBuilder& Out, TSet& PropertyFwd, UFunction* Function, int32 Indent, bool bOutputConstructor, EExportingState ExportingState) const -{ - if (!WillExportEventParms(Function)) - { - return; - } - - FString FunctionName = Function->GetName(); - if (Function->HasAnyFunctionFlags(FUNC_Delegate)) - { - FunctionName.LeftChopInline(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH, false); - } - - FString EventParmStructName = GetEventStructParamsName(Function->GetOuter(), *FunctionName); - Out.Logf(TEXT("%sstruct %s\r\n"), FCString::Tab(Indent), *EventParmStructName); - Out.Logf(TEXT("%s{\r\n"), FCString::Tab(Indent)); - - for (FProperty* Prop : TFieldRange(Function)) - { - if (!(Prop->PropertyFlags & CPF_Parm)) - { - continue; - } - - PropertyFwd.Add(Prop->GetCPPTypeForwardDeclaration()); - - FUHTStringBuilder PropertyText; - PropertyText.Log(FCString::Tab(Indent + 1)); - - bool bEmitConst = Prop->HasAnyPropertyFlags(CPF_ConstParm) && Prop->IsA(); - - //@TODO: UCREMOVAL: This is awful code duplication to avoid a double-const - { - // export 'const' for parameters - const bool bIsConstParam = (Prop->IsA(FInterfaceProperty::StaticClass()) && !Prop->HasAllPropertyFlags(CPF_OutParm)); //@TODO: This should be const once that flag exists - const bool bIsOnConstClass = (Prop->IsA(FObjectProperty::StaticClass()) && ((FObjectProperty*)Prop)->PropertyClass != NULL && ((FObjectProperty*)Prop)->PropertyClass->HasAnyClassFlags(CLASS_Const)); - - if (bIsConstParam || bIsOnConstClass) - { - bEmitConst = false; // ExportCppDeclaration will do it for us - } - } - - if (bEmitConst) - { - PropertyText.Logf(TEXT("const ")); - } - - const FString* Dim = HeaderParser.ArrayDimensions.Find(Prop); - Prop->ExportCppDeclaration(PropertyText, EExportedDeclaration::Local, Dim ? **Dim : NULL); - ApplyAlternatePropertyExportText(Prop, PropertyText, ExportingState); - - PropertyText.Log(TEXT(";\r\n")); - Out += *PropertyText; - - } - // constructor must initialize the return property if it needs it - FProperty* Prop = Function->GetReturnProperty(); - if (Prop && bOutputConstructor) - { - FUHTStringBuilder InitializationAr; - - FStructProperty* InnerStruct = CastField(Prop); - bool bNeedsOutput = true; - if (InnerStruct) - { - bNeedsOutput = InnerStruct->HasNoOpConstructor(); - } - else if ( - CastField(Prop) || - CastField(Prop) || - CastField(Prop) || - CastField(Prop) || - CastField(Prop) || - CastField(Prop) || - CastField(Prop) || - CastField(Prop) || - CastField(Prop) || - CastField(Prop) - ) - { - bNeedsOutput = false; - } - if (bNeedsOutput) - { - check(Prop->ArrayDim == 1); // can't return arrays - Out.Logf(TEXT("\r\n%s/** Constructor, initializes return property only **/\r\n"), FCString::Tab(Indent + 1)); - Out.Logf(TEXT("%s%s()\r\n"), FCString::Tab(Indent + 1), *EventParmStructName); - Out.Logf(TEXT("%s%s %s(%s)\r\n"), FCString::Tab(Indent + 2), TEXT(":"), *Prop->GetName(), *GetNullParameterValue(Prop, true)); - Out.Logf(TEXT("%s{\r\n"), FCString::Tab(Indent + 1)); - Out.Logf(TEXT("%s}\r\n"), FCString::Tab(Indent + 1)); - } - } - Out.Logf(TEXT("%s};\r\n"), FCString::Tab(Indent)); -} - -/** - * Get the intrinsic null value for this property - * - * @param Prop the property to get the null value for - * @param bMacroContext true when exporting the P_GET* macro, false when exporting the friendly C++ function header - * - * @return the intrinsic null value for the property (0 for ints, TEXT("") for strings, etc.) - */ -FString FNativeClassHeaderGenerator::GetNullParameterValue( FProperty* Prop, bool bInitializer/*=false*/ ) -{ - FFieldClass* PropClass = Prop->GetClass(); - FObjectPropertyBase* ObjectProperty = CastField(Prop); - if (PropClass == FByteProperty::StaticClass()) - { - FByteProperty* ByteProp = (FByteProperty*)Prop; - - // if it's an enum class then we need an explicit cast - if( ByteProp->Enum && ByteProp->Enum->GetCppForm() == UEnum::ECppForm::EnumClass ) - { - return FString::Printf(TEXT("(%s)0"), *ByteProp->GetCPPType()); - } - - return TEXT("0"); - } - else if (PropClass == FEnumProperty::StaticClass()) - { - FEnumProperty* EnumProp = (FEnumProperty*)Prop; - - return FString::Printf(TEXT("(%s)0"), *EnumProp->Enum->GetName()); - } - else if ( PropClass == FBoolProperty::StaticClass() ) - { - return TEXT("false"); - } - else if ( PropClass == FIntProperty::StaticClass() - || PropClass == FFloatProperty::StaticClass() - || PropClass == FDoubleProperty::StaticClass()) - { - return TEXT("0"); - } - else if ( PropClass == FNameProperty::StaticClass() ) - { - return TEXT("NAME_None"); - } - else if ( PropClass == FStrProperty::StaticClass() ) - { - return TEXT("TEXT(\"\")"); - } - else if ( PropClass == FTextProperty::StaticClass() ) - { - return TEXT("FText::GetEmpty()"); - } - else if ( PropClass == FArrayProperty::StaticClass() - || PropClass == FMapProperty::StaticClass() - || PropClass == FSetProperty::StaticClass() - || PropClass == FDelegateProperty::StaticClass() - || PropClass == FMulticastDelegateProperty::StaticClass() ) - { - FString Type, ExtendedType; - Type = Prop->GetCPPType(&ExtendedType,CPPF_OptionalValue); - return Type + ExtendedType + TEXT("()"); - } - else if ( PropClass == FStructProperty::StaticClass() ) - { - bool bHasNoOpConstuctor = CastFieldChecked(Prop)->HasNoOpConstructor(); - if (bInitializer && bHasNoOpConstuctor) - { - return TEXT("ForceInit"); - } - - FString Type, ExtendedType; - Type = Prop->GetCPPType(&ExtendedType,CPPF_OptionalValue); - return Type + ExtendedType + (bHasNoOpConstuctor ? TEXT("(ForceInit)") : TEXT("()")); - } - else if (ObjectProperty) - { - return TEXT("NULL"); - } - else if ( PropClass == FInterfaceProperty::StaticClass() ) - { - return TEXT("NULL"); - } - else if (PropClass == FFieldPathProperty::StaticClass()) - { - return TEXT("nullptr"); - } - - UE_LOG(LogCompile, Fatal,TEXT("GetNullParameterValue - Unhandled property type '%s': %s"), *Prop->GetClass()->GetName(), *Prop->GetPathName()); - return TEXT(""); -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -FString FNativeClassHeaderGenerator::GetFunctionReturnString(UFunction* Function, FReferenceGatherers& OutReferenceGatherers) -{ - FString Result; - - if (FProperty* Return = Function->GetReturnProperty()) - { - FString ExtendedReturnType; - OutReferenceGatherers.ForwardDeclarations.Add(Return->GetCPPTypeForwardDeclaration()); - FString ReturnType = Return->GetCPPType(&ExtendedReturnType, CPPF_ArgumentOrReturnValue); - FUHTStringBuilder ReplacementText; - ReplacementText += MoveTemp(ReturnType); - ApplyAlternatePropertyExportText(Return, ReplacementText, EExportingState::Normal); - Result = MoveTemp(ReplacementText) + MoveTemp(ExtendedReturnType); - } - else - { - Result = TEXT("void"); - } - - return Result; -} -*/ - -/** - * Converts Position within File to Line and Column. - * - * @param File File contents. - * @param Position Position in string to convert. - * @param OutLine Result line. - * @param OutColumn Result column. - */ -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -void GetLineAndColumnFromPositionInFile(const FString& File, int32 Position, int32& OutLine, int32& OutColumn) -{ - OutLine = 1; - OutColumn = 1; - - int32 i; - for (i = 1; i <= Position; ++i) - { - if (File[i] == '\n') - { - ++OutLine; - OutColumn = 0; - } - else - { - ++OutColumn; - } - } -} -*/ - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -bool FNativeClassHeaderGenerator::IsMissingVirtualSpecifier(const FString& SourceFile, int32 FunctionNamePosition) -{ - auto IsEndOfSearchChar = [](TCHAR C) { return (C == TEXT('}')) || (C == TEXT('{')) || (C == TEXT(';')); }; - - // Find first occurrence of "}", ";", "{" going backwards from ImplementationPosition. - int32 EndOfSearchCharIndex = SourceFile.FindLastCharByPredicate(IsEndOfSearchChar, FunctionNamePosition); - check(EndOfSearchCharIndex != INDEX_NONE); - - // Then find if there is "virtual" keyword starting from position of found character to ImplementationPosition - return !HasIdentifierExactMatch(&SourceFile[EndOfSearchCharIndex], &SourceFile[FunctionNamePosition], TEXT("virtual")); -} -*/ - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -FString CreateClickableErrorMessage(const FString& Filename, int32 Line, int32 Column) -{ - return FString::Printf(TEXT("%s(%d,%d): error: "), *Filename, Line, Column); -} -*/ - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -void FNativeClassHeaderGenerator::CheckRPCFunctions(FReferenceGatherers& OutReferenceGatherers, const FFuncInfo& FunctionData, const FString& ClassName, int32 ImplementationPosition, int32 ValidatePosition, const FUnrealSourceFile& SourceFile) const -{ - bool bHasImplementation = ImplementationPosition != INDEX_NONE; - bool bHasValidate = ValidatePosition != INDEX_NONE; - - UFunction* Function = FunctionData.FunctionReference; - FString FunctionReturnType = GetFunctionReturnString(Function, OutReferenceGatherers); - const TCHAR* ConstModifier = (Function->HasAllFunctionFlags(FUNC_Const) ? TEXT("const ") : TEXT(" ")); - - const bool bIsNative = Function->HasAllFunctionFlags(FUNC_Native); - const bool bIsNet = Function->HasAllFunctionFlags(FUNC_Net); - const bool bIsNetValidate = Function->HasAllFunctionFlags(FUNC_NetValidate); - const bool bIsNetResponse = Function->HasAllFunctionFlags(FUNC_NetResponse); - const bool bIsBlueprintEvent = Function->HasAllFunctionFlags(FUNC_BlueprintEvent); - - bool bNeedsImplementation = (bIsNet && !bIsNetResponse) || bIsBlueprintEvent || bIsNative; - bool bNeedsValidate = (bIsNative || bIsNet) && !bIsNetResponse && bIsNetValidate; - - check(bNeedsImplementation || bNeedsValidate); - - FString ParameterString = GetFunctionParameterString(Function, OutReferenceGatherers); - const FString& Filename = SourceFile.GetFilename(); - const FString& FileContent = SourceFile.GetContent(); - - // - // Get string with function specifiers, listing why we need _Implementation or _Validate functions. - // - TArray> FunctionSpecifiers; - if (bIsNative) { FunctionSpecifiers.Add(TEXT("Native")); } - if (bIsNet) { FunctionSpecifiers.Add(TEXT("Net")); } - if (bIsBlueprintEvent) { FunctionSpecifiers.Add(TEXT("BlueprintEvent")); } - if (bIsNetValidate) { FunctionSpecifiers.Add(TEXT("NetValidate")); } - - check(FunctionSpecifiers.Num() > 0); - - // - // Coin static_assert message - // - FUHTStringBuilder AssertMessage; - AssertMessage.Logf(TEXT("Function %s was marked as %s"), *(Function->GetName()), FunctionSpecifiers[0]); - for (int32 i = 1; i < FunctionSpecifiers.Num(); ++i) - { - AssertMessage.Logf(TEXT(", %s"), FunctionSpecifiers[i]); - } - - AssertMessage.Logf(TEXT(".")); - - // - // Check if functions are missing. - // - int32 Line; - int32 Column; - GetLineAndColumnFromPositionInFile(FileContent, FunctionData.InputPos, Line, Column); - if (bNeedsImplementation && !bHasImplementation) - { - FString ErrorPosition = CreateClickableErrorMessage(Filename, Line, Column); - FString FunctionDecl = FString::Printf(TEXT("virtual %s %s::%s(%s) %s"), *FunctionReturnType, *ClassName, *FunctionData.CppImplName, *ParameterString, *ConstModifier); - FError::Throwf(TEXT("%s%s Declare function %s"), *ErrorPosition, *AssertMessage, *FunctionDecl); - } - - if (bNeedsValidate && !bHasValidate) - { - FString ErrorPosition = CreateClickableErrorMessage(Filename, Line, Column); - FString FunctionDecl = FString::Printf(TEXT("virtual bool %s::%s(%s) %s"), *ClassName, *FunctionData.CppValidationImplName, *ParameterString, *ConstModifier); - FError::Throwf(TEXT("%s%s Declare function %s"), *ErrorPosition, *AssertMessage, *FunctionDecl); - } - - // - // If all needed functions are declared, check if they have virtual specifiers. - // - if (bNeedsImplementation && bHasImplementation && IsMissingVirtualSpecifier(FileContent, ImplementationPosition)) - { - GetLineAndColumnFromPositionInFile(FileContent, ImplementationPosition, Line, Column); - FString ErrorPosition = CreateClickableErrorMessage(Filename, Line, Column); - FString FunctionDecl = FString::Printf(TEXT("%s %s::%s(%s) %s"), *FunctionReturnType, *ClassName, *FunctionData.CppImplName, *ParameterString, *ConstModifier); - FError::Throwf(TEXT("%sDeclared function %sis not marked as virtual."), *ErrorPosition, *FunctionDecl); - } - - if (bNeedsValidate && bHasValidate && IsMissingVirtualSpecifier(FileContent, ValidatePosition)) - { - GetLineAndColumnFromPositionInFile(FileContent, ValidatePosition, Line, Column); - FString ErrorPosition = CreateClickableErrorMessage(Filename, Line, Column); - FString FunctionDecl = FString::Printf(TEXT("bool %s::%s(%s) %s"), *ClassName, *FunctionData.CppValidationImplName, *ParameterString, *ConstModifier); - FError::Throwf(TEXT("%sDeclared function %sis not marked as virtual."), *ErrorPosition, *FunctionDecl); - } -} -*/ - -void FNativeClassHeaderGenerator::ExportNativeFunctionHeader( - FOutputDevice& Out, - TSet& OutFwdDecls, - const FFuncInfo& FunctionData, - EExportFunctionType::Type FunctionType, - EExportFunctionHeaderStyle::Type FunctionHeaderStyle, - const TCHAR* ExtraParam, - const TCHAR* APIString) const -{ - UFunction* Function = FunctionData.FunctionReference; - - const bool bIsDelegate = Function->HasAnyFunctionFlags( FUNC_Delegate ); - const bool bIsInterface = !bIsDelegate && Function->GetOwnerClass()->HasAnyClassFlags(CLASS_Interface); - const bool bIsK2Override = Function->HasAnyFunctionFlags( FUNC_BlueprintEvent ); - - if (!bIsDelegate) - { - Out.Log(TEXT("\t")); - } - - if (FunctionHeaderStyle == EExportFunctionHeaderStyle::Declaration) - { - // cpp implementation of functions never have these appendages - - // If the function was marked as 'RequiredAPI', then add the *_API macro prefix. Note that if the class itself - // was marked 'RequiredAPI', this is not needed as C++ will exports all methods automatically. - if (FunctionType != EExportFunctionType::Event && - !Function->GetOwnerClass()->HasAnyClassFlags(CLASS_RequiredAPI) && - (FunctionData.FunctionExportFlags & FUNCEXPORT_RequiredAPI)) - { - Out.Log(APIString); - } - - if(FunctionType == EExportFunctionType::Interface) - { - Out.Log(TEXT("static ")); - } - else if (bIsK2Override) - { - Out.Log(TEXT("virtual ")); - } - // if the owning class is an interface class - else if ( bIsInterface ) - { - Out.Log(TEXT("virtual ")); - } - // this is not an event, the function is not a static function and the function is not marked final - else if ( FunctionType != EExportFunctionType::Event && !Function->HasAnyFunctionFlags(FUNC_Static) && !(FunctionData.FunctionExportFlags & FUNCEXPORT_Final) ) - { - Out.Log(TEXT("virtual ")); - } - else if( FunctionData.FunctionExportFlags & FUNCEXPORT_Inline ) - { - Out.Log(TEXT("inline ")); - } - } - - FProperty* ReturnProperty = Function->GetReturnProperty(); - if (ReturnProperty != nullptr) - { - if (ReturnProperty->HasAnyPropertyFlags(EPropertyFlags::CPF_ConstParm)) - { - Out.Log(TEXT("const ")); - } - - FString ExtendedReturnType; - FString ReturnType = ReturnProperty->GetCPPType(&ExtendedReturnType, (FunctionHeaderStyle == EExportFunctionHeaderStyle::Definition && (FunctionType != EExportFunctionType::Interface) ? CPPF_Implementation : 0) | CPPF_ArgumentOrReturnValue); - OutFwdDecls.Add(ReturnProperty->GetCPPTypeForwardDeclaration()); - FUHTStringBuilder ReplacementText; - ReplacementText += ReturnType; - ApplyAlternatePropertyExportText(ReturnProperty, ReplacementText, EExportingState::Normal); - Out.Logf(TEXT("%s%s"), *ReplacementText, *ExtendedReturnType); - } - else - { - Out.Log( TEXT("void") ); - } - - FString FunctionName; - if (FunctionHeaderStyle == EExportFunctionHeaderStyle::Definition) - { - FunctionName = FString::Printf(TEXT("%s::"), *FNameLookupCPP::GetNameCPP(CastChecked(Function->GetOuter()), bIsInterface || FunctionType == EExportFunctionType::Interface)); - } - - if (FunctionType == EExportFunctionType::Interface) - { - FunctionName += FString::Printf(TEXT("Execute_%s"), *Function->GetName()); - } - else if (FunctionType == EExportFunctionType::Event) - { - FunctionName += FunctionData.MarshallAndCallName; - } - else - { - FunctionName += FunctionData.CppImplName; - } - - Out.Logf(TEXT(" %s("), *FunctionName); - - int32 ParmCount=0; - - // Emit extra parameter if we have one - if( ExtraParam ) - { - Out.Logf(TEXT("%s"), ExtraParam); - ++ParmCount; - } - - for (FProperty* Property : TFieldRange(Function)) - { - if ((Property->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) != CPF_Parm) - { - continue; - } - - OutFwdDecls.Add(Property->GetCPPTypeForwardDeclaration()); - - if( ParmCount++ ) - { - Out.Log(TEXT(", ")); - } - - FUHTStringBuilder PropertyText; - - const FString* Dim = HeaderParser.ArrayDimensions.Find(Property); - Property->ExportCppDeclaration( PropertyText, EExportedDeclaration::Parameter, Dim ? **Dim : NULL ); - ApplyAlternatePropertyExportText(Property, PropertyText, EExportingState::Normal); - - Out.Logf(TEXT("%s"), *PropertyText); - } - - Out.Log( TEXT(")") ); - if (FunctionType != EExportFunctionType::Interface) - { - if (!bIsDelegate && Function->HasAllFunctionFlags(FUNC_Const)) - { - Out.Log( TEXT(" const") ); - } - - if (bIsInterface && FunctionHeaderStyle == EExportFunctionHeaderStyle::Declaration) - { - // all methods in interface classes are pure virtuals - if (bIsK2Override) - { - // For BlueprintNativeEvent methods we emit a stub implementation. This allows Blueprints that implement the interface class to be nativized. - FString ReturnValue; - if (ReturnProperty != nullptr) - { - FByteProperty* ByteProperty = CastField(ReturnProperty); - if (ByteProperty != nullptr && ByteProperty->Enum != nullptr && ByteProperty->Enum->GetCppForm() != UEnum::ECppForm::EnumClass) - { - ReturnValue = FString::Printf(TEXT(" return TEnumAsByte<%s>(%s); "), *ByteProperty->Enum->CppType, *GetNullParameterValue(ReturnProperty, false)); - } - else - { - ReturnValue = FString::Printf(TEXT(" return %s; "), *GetNullParameterValue(ReturnProperty, false)); - } - } - - Out.Logf(TEXT(" {%s}"), *ReturnValue); - } - else - { - Out.Log(TEXT("=0")); - } - } - } -} - -/** - * Export the actual internals to a standard thunk function - * - * @param RPCWrappers output device for writing - * @param FunctionData function data for the current function - * @param Parameters list of parameters in the function - * @param Return return parameter for the function - * @param DeprecationWarningOutputDevice Device to output deprecation warnings for _Validate and _Implementation functions. - */ -void FNativeClassHeaderGenerator::ExportFunctionThunk(FUHTStringBuilder& RPCWrappers, FReferenceGatherers& OutReferenceGatherers, UFunction* Function, const FFuncInfo& FunctionData, const TArray& Parameters, FProperty* Return) const -{ - // export the GET macro for this parameter - FString ParameterList; - for (int32 ParameterIndex = 0; ParameterIndex < Parameters.Num(); ParameterIndex++) - { - FProperty* Param = Parameters[ParameterIndex]; - OutReferenceGatherers.ForwardDeclarations.Add(Param->GetCPPTypeForwardDeclaration()); - - FString EvalBaseText = TEXT("P_GET_"); // e.g. P_GET_STR - FString EvalModifierText; // e.g. _REF - FString EvalParameterText; // e.g. (UObject*,NULL) - - FString TypeText; - - if (Param->ArrayDim > 1) - { - EvalBaseText += TEXT("ARRAY"); - TypeText = Param->GetCPPType(); - } - else - { - EvalBaseText += Param->GetCPPMacroType(TypeText); - - FArrayProperty* ArrayProperty = CastField(Param); - if (ArrayProperty) - { - FInterfaceProperty* InterfaceProperty = CastField(ArrayProperty->Inner); - if (InterfaceProperty) - { - FString InterfaceTypeText; - InterfaceProperty->GetCPPMacroType(InterfaceTypeText); - TypeText += FString::Printf(TEXT("<%s>"), *InterfaceTypeText); - } - } - } - - bool bPassAsNoPtr = Param->HasAllPropertyFlags(CPF_UObjectWrapper | CPF_OutParm) && Param->IsA(FClassProperty::StaticClass()); - if (bPassAsNoPtr) - { - TypeText = Param->GetCPPType(); - } - - FUHTStringBuilder ReplacementText; - ReplacementText += TypeText; - - ApplyAlternatePropertyExportText(Param, ReplacementText, EExportingState::Normal); - TypeText = ReplacementText; - - FString DefaultValueText; - FString ParamPrefix = TEXT("Z_Param_"); - - // if this property is an out parm, add the REF tag - if (Param->PropertyFlags & CPF_OutParm) - { - if (!bPassAsNoPtr) - { - EvalModifierText += TEXT("_REF"); - } - else - { - // Parameters passed as TSubclassOf& shouldn't have asterisk added. - EvalModifierText += TEXT("_REF_NO_PTR"); - } - - ParamPrefix += TEXT("Out_"); - } - - // if this property requires a specialization, add a comma to the type name so we can print it out easily - if (TypeText != TEXT("")) - { - TypeText += TCHAR(','); - } - - FString ParamName = ParamPrefix + Param->GetName(); - - EvalParameterText = FString::Printf(TEXT("(%s%s%s)"), *TypeText, *ParamName, *DefaultValueText); - - RPCWrappers.Logf(TEXT("\t\t%s%s%s;") LINE_TERMINATOR, *EvalBaseText, *EvalModifierText, *EvalParameterText); - - // add this property to the parameter list string - if (ParameterList.Len()) - { - ParameterList += TCHAR(','); - } - - { - FDelegateProperty* DelegateProp = CastField< FDelegateProperty >(Param); - if (DelegateProp != NULL) - { - // For delegates, add an explicit conversion to the specific type of delegate before passing it along - const FString FunctionName = DelegateProp->SignatureFunction->GetName().LeftChop(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH); - ParamName = FString::Printf(TEXT("F%s(%s)"), *FunctionName, *ParamName); - } - } - - { - FMulticastDelegateProperty* MulticastDelegateProp = CastField< FMulticastDelegateProperty >(Param); - if (MulticastDelegateProp != NULL) - { - // For delegates, add an explicit conversion to the specific type of delegate before passing it along - const FString FunctionName = MulticastDelegateProp->SignatureFunction->GetName().LeftChop(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX_LENGTH); - ParamName = FString::Printf(TEXT("F%s(%s)"), *FunctionName, *ParamName); - } - } - - UEnum* Enum = nullptr; - FByteProperty* ByteProp = CastField(Param); - if (ByteProp && ByteProp->Enum) - { - Enum = ByteProp->Enum; - } - else if (Param->IsA()) - { - Enum = ((FEnumProperty*)Param)->Enum; - } - - if (Enum) - { - // For enums, add an explicit conversion - if (!(Param->PropertyFlags & CPF_OutParm)) - { - ParamName = FString::Printf(TEXT("%s(%s)"), *Enum->CppType, *ParamName); - } - else - { - if (Enum->GetCppForm() == UEnum::ECppForm::EnumClass) - { - // If we're an enum class don't require the wrapper - ParamName = FString::Printf(TEXT("(%s&)(%s)"), *Enum->CppType, *ParamName); - } - else - { - ParamName = FString::Printf(TEXT("(TEnumAsByte<%s>&)(%s)"), *Enum->CppType, *ParamName); - } - } - } - - ParameterList += ParamName; - } - - RPCWrappers += TEXT("\t\tP_FINISH;") LINE_TERMINATOR; - RPCWrappers += TEXT("\t\tP_NATIVE_BEGIN;") LINE_TERMINATOR; - - ClassDefinitionRange ClassRange; - if (HeaderParser.ClassDefinitionRanges.Contains(Function->GetOwnerClass())) - { - ClassRange = HeaderParser.ClassDefinitionRanges[Function->GetOwnerClass()]; - ClassRange.Validate(); - } - - const TCHAR* ClassStart = ClassRange.Start; - const TCHAR* ClassEnd = ClassRange.End; - FString ClassName = Function->GetOwnerClass()->GetName(); - - FString ClassDefinition(UE_PTRDIFF_TO_INT32(ClassEnd - ClassStart), ClassStart); - - bool bHasImplementation = HasIdentifierExactMatch(ClassDefinition, FunctionData.CppImplName); - bool bHasValidate = HasIdentifierExactMatch(ClassDefinition, FunctionData.CppValidationImplName); - - bool bShouldEnableImplementationDeprecation = - // Enable deprecation warnings only if GENERATED_BODY is used inside class or interface (not GENERATED_UCLASS_BODY etc.) - ClassRange.bHasGeneratedBody - // and implementation function is called, but not the one declared by user - && (FunctionData.CppImplName != Function->GetName() && !bHasImplementation); - - bool bShouldEnableValidateDeprecation = - // Enable deprecation warnings only if GENERATED_BODY is used inside class or interface (not GENERATED_UCLASS_BODY etc.) - ClassRange.bHasGeneratedBody - // and validation function is called - && (FunctionData.FunctionFlags & FUNC_NetValidate) && !bHasValidate; - - //Emit warning here if necessary - FUHTStringBuilder FunctionDeclaration; - ExportNativeFunctionHeader(FunctionDeclaration, OutReferenceGatherers.ForwardDeclarations, FunctionData, EExportFunctionType::Function, EExportFunctionHeaderStyle::Declaration, nullptr, *GetAPIString()); - - // Call the validate function if there is one - if (!(FunctionData.FunctionExportFlags & FUNCEXPORT_CppStatic) && (FunctionData.FunctionFlags & FUNC_NetValidate)) - { - RPCWrappers.Logf(TEXT("\t\tif (!P_THIS->%s(%s))") LINE_TERMINATOR, *FunctionData.CppValidationImplName, *ParameterList); - RPCWrappers.Logf(TEXT("\t\t{") LINE_TERMINATOR); - RPCWrappers.Logf(TEXT("\t\t\tRPC_ValidateFailed(TEXT(\"%s\"));") LINE_TERMINATOR, *FunctionData.CppValidationImplName); - RPCWrappers.Logf(TEXT("\t\t\treturn;") LINE_TERMINATOR); // If we got here, the validation function check failed - RPCWrappers.Logf(TEXT("\t\t}") LINE_TERMINATOR); - } - - // write out the return value - RPCWrappers.Log(TEXT("\t\t")); - if (Return) - { - OutReferenceGatherers.ForwardDeclarations.Add(Return->GetCPPTypeForwardDeclaration()); - - FUHTStringBuilder ReplacementText; - FString ReturnExtendedType; - ReplacementText += Return->GetCPPType(&ReturnExtendedType); - ApplyAlternatePropertyExportText(Return, ReplacementText, EExportingState::Normal); - - FString ReturnType = ReplacementText; - RPCWrappers.Logf(TEXT("*(%s%s*)") TEXT(PREPROCESSOR_TO_STRING(RESULT_PARAM)) TEXT("="), *ReturnType, *ReturnExtendedType); - } - - // export the call to the C++ version - if (FunctionData.FunctionExportFlags & FUNCEXPORT_CppStatic) - { - RPCWrappers.Logf(TEXT("%s::%s(%s);") LINE_TERMINATOR, *FNameLookupCPP::GetNameCPP(Function->GetOwnerClass()), *FunctionData.CppImplName, *ParameterList); - } - else - { - RPCWrappers.Logf(TEXT("P_THIS->%s(%s);") LINE_TERMINATOR, *FunctionData.CppImplName, *ParameterList); - } - RPCWrappers += TEXT("\t\tP_NATIVE_END;") LINE_TERMINATOR; -} - -FString FNativeClassHeaderGenerator::GetFunctionParameterString(UFunction* Function, FReferenceGatherers& OutReferenceGatherers) const -{ - FString ParameterList; - FUHTStringBuilder PropertyText; - - for (FProperty* Property : TFieldRange(Function)) - { - OutReferenceGatherers.ForwardDeclarations.Add(Property->GetCPPTypeForwardDeclaration()); - - if ((Property->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) != CPF_Parm) - { - break; - } - - if (ParameterList.Len()) - { - ParameterList += TEXT(", "); - } - - const FString* Dim = HeaderParser.ArrayDimensions.Find(Property); - Property->ExportCppDeclaration(PropertyText, EExportedDeclaration::Parameter, Dim ? **Dim : nullptr, 0, true); - ApplyAlternatePropertyExportText(Property, PropertyText, EExportingState::Normal); - - ParameterList += PropertyText; - PropertyText.Reset(); - } - - return ParameterList; -} - -struct FNativeFunctionStringBuilder -{ - FUHTStringBuilder RPCWrappers; - FUHTStringBuilder RPCImplementations; - FUHTStringBuilder AutogeneratedBlueprintFunctionDeclarations; - FUHTStringBuilder AutogeneratedBlueprintFunctionDeclarationsOnlyNotDeclared; - FUHTStringBuilder AutogeneratedStaticData; - FUHTStringBuilder AutogeneratedStaticDataFuncs; -}; - -void FNativeClassHeaderGenerator::ExportNativeFunctions(FOutputDevice& OutGeneratedHeaderText, FOutputDevice& OutGeneratedCPPText, FOutputDevice& OutMacroCalls, FOutputDevice& OutNoPureDeclsMacroCalls, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UClass* Class, FClassMetaData* ClassData) const -{ - FNativeFunctionStringBuilder RuntimeStringBuilders; - FNativeFunctionStringBuilder EditorStringBuilders; - - const FString ClassCPPName = FNameLookupCPP::GetNameCPP(Class, Class->HasAnyClassFlags(CLASS_Interface)); - - ClassDefinitionRange ClassRange; - if (HeaderParser.ClassDefinitionRanges.Contains(Class)) - { - ClassRange = HeaderParser.ClassDefinitionRanges[Class]; - ClassRange.Validate(); - } - - // gather static class data - TArray SparseClassDataTypes; - ((FClass*)Class)->GetSparseClassDataTypes(SparseClassDataTypes); - FString FullClassName = ((FClass*)Class)->GetNameWithPrefix(); - for (const FString& SparseClassDataString : SparseClassDataTypes) - { - RuntimeStringBuilders.AutogeneratedStaticData.Logf(TEXT("F%s* Get%s()\r\n"), *SparseClassDataString, *SparseClassDataString); - RuntimeStringBuilders.AutogeneratedStaticData += TEXT("{\r\n"); - RuntimeStringBuilders.AutogeneratedStaticData.Logf(TEXT("\treturn (F%s*)(GetClass()->GetOrCreateSparseClassData());\r\n"), *SparseClassDataString); - RuntimeStringBuilders.AutogeneratedStaticData += TEXT("}\r\n"); - - RuntimeStringBuilders.AutogeneratedStaticData.Logf(TEXT("F%s* Get%s() const\r\n"), *SparseClassDataString, *SparseClassDataString); - RuntimeStringBuilders.AutogeneratedStaticData += TEXT("{\r\n"); - RuntimeStringBuilders.AutogeneratedStaticData.Logf(TEXT("\treturn (F%s*)(GetClass()->GetOrCreateSparseClassData());\r\n"), *SparseClassDataString); - RuntimeStringBuilders.AutogeneratedStaticData += TEXT("}\r\n"); - - UScriptStruct* SparseClassDataStruct = FindObjectSafe(ANY_PACKAGE, *SparseClassDataString); - while (SparseClassDataStruct != nullptr) - { - const FProperty* Child = CastField(SparseClassDataStruct->ChildProperties); - while (Child) - { - FString ReturnExtendedType; - FString VarType = Child->GetCPPType(&ReturnExtendedType, EPropertyExportCPPFlags::CPPF_ArgumentOrReturnValue | EPropertyExportCPPFlags::CPPF_Implementation); - if (!ReturnExtendedType.IsEmpty()) - { - VarType.Append(ReturnExtendedType); - } - FString VarName = Child->GetName(); - FString CleanVarName = VarName; - if (CastField(Child) && VarName.StartsWith(TEXT("b"), ESearchCase::CaseSensitive)) - { - CleanVarName = VarName.RightChop(1); - } - - if (!Child->HasMetaData(NAME_NoGetter)) - { - if (Child->HasMetaData(NAME_GetByRef)) - { - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("const %s& Get%s()\r\n"), *VarType, *CleanVarName); - } - else - { - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("%s Get%s()\r\n"), *VarType, *CleanVarName); - } - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("{\r\n")); - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("\treturn Get%s()->%s;\r\n"), *SparseClassDataString, *VarName); - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("}\r\n")); - - if (Child->HasMetaData(NAME_GetByRef)) - { - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("const %s& Get%s() const\r\n"), *VarType, *CleanVarName); - } - else - { - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("%s Get%s() const\r\n"), *VarType, *CleanVarName); - } - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("{\r\n")); - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("\treturn Get%s()->%s;\r\n"), *SparseClassDataString, *VarName); - RuntimeStringBuilders.AutogeneratedStaticDataFuncs.Logf(TEXT("}\r\n")); - } - - Child = CastField(Child->Next); - } - - SparseClassDataStruct = Cast(SparseClassDataStruct->GetSuperStruct()); - } - } - - // export the C++ stubs - - for (UFunction* Function : TFieldRange(Class, EFieldIteratorFlags::ExcludeSuper)) - { - if (!(Function->FunctionFlags & FUNC_Native)) - { - continue; - } - - const bool bEditorOnlyFunc = Function->HasAnyFunctionFlags(FUNC_EditorOnly); - FNativeFunctionStringBuilder& FuncStringBuilders = bEditorOnlyFunc ? EditorStringBuilders : RuntimeStringBuilders; - - FFunctionData* CompilerInfo = FFunctionData::FindForFunction(Function); - - const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData(); - - // Custom thunks don't get any C++ stub function generated - if (FunctionData.FunctionExportFlags & FUNCEXPORT_CustomThunk) - { - continue; - } - - // Should we emit these to RPC wrappers or just ignore them? - const bool bWillBeProgrammerTyped = FunctionData.CppImplName == Function->GetName(); - - if (!bWillBeProgrammerTyped) - { - const TCHAR* ClassStart = ClassRange.Start; - const TCHAR* ClassEnd = ClassRange.End; - FString ClassDefinition(UE_PTRDIFF_TO_INT32(ClassEnd - ClassStart), ClassStart); - - FString FunctionName = Function->GetName(); - int32 ClassDefinitionStartPosition = UE_PTRDIFF_TO_INT32(ClassStart - *SourceFile.GetContent()); - - int32 ImplementationPosition = FindIdentifierExactMatch(ClassDefinition, FunctionData.CppImplName); - bool bHasImplementation = ImplementationPosition != INDEX_NONE; - if (bHasImplementation) - { - ImplementationPosition += ClassDefinitionStartPosition; - } - - int32 ValidatePosition = FindIdentifierExactMatch(ClassDefinition, FunctionData.CppValidationImplName); - bool bHasValidate = ValidatePosition != INDEX_NONE; - if (bHasValidate) - { - ValidatePosition += ClassDefinitionStartPosition; - } - - //Emit warning here if necessary - FUHTStringBuilder FunctionDeclaration; - ExportNativeFunctionHeader(FunctionDeclaration, OutReferenceGatherers.ForwardDeclarations, FunctionData, EExportFunctionType::Function, EExportFunctionHeaderStyle::Declaration, nullptr, *GetAPIString()); - FunctionDeclaration.Log(TEXT(";\r\n")); - - // Declare validation function if needed - if (FunctionData.FunctionFlags & FUNC_NetValidate) - { - FString ParameterList = GetFunctionParameterString(Function, OutReferenceGatherers); - - const TCHAR* Virtual = (!FunctionData.FunctionReference->HasAnyFunctionFlags(FUNC_Static) && !(FunctionData.FunctionExportFlags & FUNCEXPORT_Final)) ? TEXT("virtual") : TEXT(""); - FStringOutputDevice ValidDecl; - ValidDecl.Logf(TEXT("\t%s bool %s(%s);\r\n"), Virtual, *FunctionData.CppValidationImplName, *ParameterList); - FuncStringBuilders.AutogeneratedBlueprintFunctionDeclarations.Log(*ValidDecl); - if (!bHasValidate) - { - FuncStringBuilders.AutogeneratedBlueprintFunctionDeclarationsOnlyNotDeclared.Logf(TEXT("%s"), *ValidDecl); - } - } - - FuncStringBuilders.AutogeneratedBlueprintFunctionDeclarations.Log(*FunctionDeclaration); - if (!bHasImplementation && FunctionData.CppImplName != FunctionName) - { - FuncStringBuilders.AutogeneratedBlueprintFunctionDeclarationsOnlyNotDeclared.Log(*FunctionDeclaration); - } - - // Versions that skip function autodeclaration throw an error when a function is missing. - if (ClassRange.bHasGeneratedBody && (SourceFile.GetGeneratedCodeVersionForStruct(HeaderParser, Class) > EGeneratedCodeVersion::V1)) - { - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - CheckRPCFunctions(OutReferenceGatherers, FunctionData, ClassCPPName, ImplementationPosition, ValidatePosition, SourceFile); - */ - } - } - - FuncStringBuilders.RPCWrappers.Log(TEXT("\r\n")); - - // if this function was originally declared in a base class, and it isn't a static function, - // only the C++ function header will be exported - if (!ShouldExportUFunction(Function)) - { - continue; - } - - // export the script wrappers - FuncStringBuilders.RPCWrappers.Logf(TEXT("\tDECLARE_FUNCTION(%s);"), *FunctionData.UnMarshallAndCallName); - FuncStringBuilders.RPCImplementations.Logf(TEXT("\tDEFINE_FUNCTION(%s::%s)"), *ClassCPPName, *FunctionData.UnMarshallAndCallName); - FuncStringBuilders.RPCImplementations += LINE_TERMINATOR TEXT("\t{") LINE_TERMINATOR; - - FParmsAndReturnProperties Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference); - ExportFunctionThunk(FuncStringBuilders.RPCImplementations, OutReferenceGatherers, Function, FunctionData, Parameters.Parms, Parameters.Return); - - FuncStringBuilders.RPCImplementations += TEXT("\t}") LINE_TERMINATOR; - } - - // static class data - { - FString MacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_SPARSE_DATA")); - - WriteMacro(OutGeneratedHeaderText, MacroName, RuntimeStringBuilders.AutogeneratedStaticData + RuntimeStringBuilders.AutogeneratedStaticDataFuncs); - OutMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); - OutNoPureDeclsMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); - } - - // Write runtime wrappers - { - FString MacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_RPC_WRAPPERS")); - - // WriteMacro has an assumption about what will be at the end of this block that is no longer true due to splitting the - // definition and implementation, so add on a line terminator to satisfy it - if (RuntimeStringBuilders.RPCWrappers.Len() > 0) - { - RuntimeStringBuilders.RPCWrappers += LINE_TERMINATOR; - } - - WriteMacro(OutGeneratedHeaderText, MacroName, RuntimeStringBuilders.AutogeneratedBlueprintFunctionDeclarations + RuntimeStringBuilders.RPCWrappers); - OutMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); - - // Put static checks before RPCWrappers to get proper messages from static asserts before compiler errors. - FString NoPureDeclsMacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_RPC_WRAPPERS_NO_PURE_DECLS")); - if (SourceFile.GetGeneratedCodeVersionForStruct(HeaderParser, Class) > EGeneratedCodeVersion::V1) - { - WriteMacro(OutGeneratedHeaderText, NoPureDeclsMacroName, RuntimeStringBuilders.RPCWrappers); - } - else - { - WriteMacro(OutGeneratedHeaderText, NoPureDeclsMacroName, RuntimeStringBuilders.AutogeneratedBlueprintFunctionDeclarationsOnlyNotDeclared + RuntimeStringBuilders.RPCWrappers); - } - - OutNoPureDeclsMacroCalls.Logf(TEXT("\t%s\r\n"), *NoPureDeclsMacroName); - - OutGeneratedCPPText.Log(RuntimeStringBuilders.RPCImplementations); - } - - // Write editor only RPC wrappers if they exist - if (EditorStringBuilders.RPCWrappers.Len() > 0) - { - OutGeneratedHeaderText.Log( BeginEditorOnlyGuard ); - - FString MacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_EDITOR_ONLY_RPC_WRAPPERS")); - - // WriteMacro has an assumption about what will be at the end of this block that is no longer true due to splitting the - // definition and implementation, so add on a line terminator to satisfy it - if (EditorStringBuilders.RPCWrappers.Len() > 0) - { - EditorStringBuilders.RPCWrappers += LINE_TERMINATOR; - } - - WriteMacro(OutGeneratedHeaderText, MacroName, EditorStringBuilders.AutogeneratedBlueprintFunctionDeclarations + EditorStringBuilders.RPCWrappers); - OutMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); - - // Put static checks before RPCWrappers to get proper messages from static asserts before compiler errors. - FString NoPureDeclsMacroName = SourceFile.GetGeneratedMacroName(ClassData, TEXT("_EDITOR_ONLY_RPC_WRAPPERS_NO_PURE_DECLS")); - if (SourceFile.GetGeneratedCodeVersionForStruct(HeaderParser, Class) > EGeneratedCodeVersion::V1) - { - WriteMacro(OutGeneratedHeaderText, NoPureDeclsMacroName, EditorStringBuilders.RPCWrappers); - } - else - { - WriteMacro(OutGeneratedHeaderText, NoPureDeclsMacroName, EditorStringBuilders.AutogeneratedBlueprintFunctionDeclarationsOnlyNotDeclared + EditorStringBuilders.RPCWrappers); - } - - // write out an else preprocessor block for when not compiling for the editor. The generated macros should be empty then since the functions are compiled out - { - OutGeneratedHeaderText.Log(TEXT("#else\r\n")); - - WriteMacro(OutGeneratedHeaderText, MacroName, TEXT("")); - WriteMacro(OutGeneratedHeaderText, NoPureDeclsMacroName, TEXT("")); - - OutGeneratedHeaderText.Log(EndEditorOnlyGuard); - } - - OutNoPureDeclsMacroCalls.Logf(TEXT("\t%s\r\n"), *NoPureDeclsMacroName); - - OutGeneratedCPPText.Log(BeginEditorOnlyGuard); - OutGeneratedCPPText.Log(EditorStringBuilders.RPCImplementations); - OutGeneratedCPPText.Log(EndEditorOnlyGuard); - } -} - -/** - * Exports the methods which trigger UnrealScript events and delegates. - * - * @param CallbackFunctions the functions to export - */ -void FNativeClassHeaderGenerator::ExportCallbackFunctions( - FOutputDevice& OutGeneratedHeaderText, - FOutputDevice& OutCpp, - TSet& OutFwdDecls, - const TArray& CallbackFunctions, - const TCHAR* CallbackWrappersMacroName, - EExportCallbackType ExportCallbackType, - const TCHAR* APIString) const -{ - FUHTStringBuilder RPCWrappers; - - FMacroBlockEmitter OutCppEditorOnly(OutCpp, TEXT("WITH_EDITOR")); - for (UFunction* Function : CallbackFunctions) - { - // Never expecting to export delegate functions this way - check(!Function->HasAnyFunctionFlags(FUNC_Delegate)); - - FFunctionData* CompilerInfo = FFunctionData::FindForFunction(Function); - const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData(); - FString FunctionName = Function->GetName(); - UClass* Class = CastChecked(Function->GetOuter()); - const FString ClassName = FNameLookupCPP::GetNameCPP(Class); - - if (FunctionData.FunctionFlags & FUNC_NetResponse) - { - // Net response functions don't go into the VM - continue; - } - - const bool bIsEditorOnly = Function->HasAnyFunctionFlags(FUNC_EditorOnly); - - OutCppEditorOnly(bIsEditorOnly); - - const bool bWillBeProgrammerTyped = FunctionName == FunctionData.MarshallAndCallName; - - // Emit the declaration if the programmer isn't responsible for declaring this wrapper - if (!bWillBeProgrammerTyped) - { - // export the line that looks like: int32 Main(const FString& Parms) - ExportNativeFunctionHeader(RPCWrappers, OutFwdDecls, FunctionData, EExportFunctionType::Event, EExportFunctionHeaderStyle::Declaration, nullptr, APIString); - - RPCWrappers.Log(TEXT(";\r\n")); - RPCWrappers.Log(TEXT("\r\n")); - } - - FString FunctionNameName; - if (ExportCallbackType != EExportCallbackType::Interface) - { - FunctionNameName = FString::Printf(TEXT("NAME_%s_%s"), *ClassName, *FunctionName); - OutCpp.Logf(TEXT("\tstatic FName %s = FName(TEXT(\"%s\"));") LINE_TERMINATOR, *FunctionNameName, *GetOverriddenFName(Function).ToString()); - } - - // Emit the thunk implementation - ExportNativeFunctionHeader(OutCpp, OutFwdDecls, FunctionData, EExportFunctionType::Event, EExportFunctionHeaderStyle::Definition, nullptr, APIString); - - FParmsAndReturnProperties Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference); - - if (ExportCallbackType != EExportCallbackType::Interface) - { - WriteEventFunctionPrologue(OutCpp, /*Indent=*/ 1, Parameters, Class, *FunctionName); - { - // Cast away const just in case, because ProcessEvent isn't const - OutCpp.Logf( - TEXT("\t\t%sProcessEvent(FindFunctionChecked(%s),%s);\r\n"), - (Function->HasAllFunctionFlags(FUNC_Const)) ? *FString::Printf(TEXT("const_cast<%s*>(this)->"), *ClassName) : TEXT(""), - *FunctionNameName, - Parameters.HasParms() ? TEXT("&Parms") : TEXT("NULL") - ); - } - WriteEventFunctionEpilogue(OutCpp, /*Indent=*/ 1, Parameters); - } - else - { - OutCpp.Log(LINE_TERMINATOR); - OutCpp.Log(TEXT("\t{") LINE_TERMINATOR); - - // assert if this is ever called directly - OutCpp.Logf(TEXT("\t\tcheck(0 && \"Do not directly call Event functions in Interfaces. Call Execute_%s instead.\");") LINE_TERMINATOR, *FunctionName); - - // satisfy compiler if it's expecting a return value - if (Parameters.Return) - { - FString EventParmStructName = GetEventStructParamsName(Class, *FunctionName); - OutCpp.Logf(TEXT("\t\t%s Parms;") LINE_TERMINATOR, *EventParmStructName); - OutCpp.Log(TEXT("\t\treturn Parms.ReturnValue;") LINE_TERMINATOR); - } - OutCpp.Log(TEXT("\t}") LINE_TERMINATOR); - } - } - - WriteMacro(OutGeneratedHeaderText, CallbackWrappersMacroName, RPCWrappers); -} - - -/** - * Determines if the property has alternate export text associated with it and if so replaces the text in PropertyText with the - * alternate version. (for example, structs or properties that specify a native type using export-text). Should be called immediately - * after ExportCppDeclaration() - * - * @param Prop the property that is being exported - * @param PropertyText the string containing the text exported from ExportCppDeclaration - */ -void FNativeClassHeaderGenerator::ApplyAlternatePropertyExportText(FProperty* Prop, FUHTStringBuilder& PropertyText, EExportingState ExportingState) -{ - FArrayProperty* ArrayProperty = CastField(Prop); - FProperty* InnerProperty = ArrayProperty ? ArrayProperty->Inner : nullptr; - if (InnerProperty && ( - (InnerProperty->IsA() && ((FByteProperty*)InnerProperty)->Enum && FClass::IsDynamic(static_cast(((FByteProperty*)InnerProperty)->Enum))) || - (InnerProperty->IsA() && FClass::IsDynamic(static_cast(((FEnumProperty*)InnerProperty)->Enum))) - ) - ) - { - const FString Original = InnerProperty->GetCPPType(); - const FString RawByte = InnerProperty->GetCPPType(nullptr, EPropertyExportCPPFlags::CPPF_BlueprintCppBackend); - if (Original != RawByte) - { - PropertyText.ReplaceInline(*Original, *RawByte, ESearchCase::CaseSensitive); - } - return; - } - - if (ExportingState == EExportingState::TypeEraseDelegates) - { - FDelegateProperty* DelegateProperty = CastField(Prop); - FMulticastDelegateProperty* MulticastDelegateProperty = CastField(Prop); - if (DelegateProperty || MulticastDelegateProperty) - { - FString Original = Prop->GetCPPType(); - const TCHAR* PlaceholderOfSameSizeAndAlignemnt; - if (DelegateProperty) - { - PlaceholderOfSameSizeAndAlignemnt = TEXT("FScriptDelegate"); - } - else - { - PlaceholderOfSameSizeAndAlignemnt = TEXT("FMulticastScriptDelegate"); - } - PropertyText.ReplaceInline(*Original, PlaceholderOfSameSizeAndAlignemnt, ESearchCase::CaseSensitive); - } - } -} - -void FNativeClassHeaderGenerator::GetSourceFilesInDependencyOrderRecursive(TArray& OutTest, FUnrealSourceFile* SourceFile, TSet& VisitedSet, bool bCheckDependenciesOnly, const TSet& Ignore) const -{ - // Check if the Class has already been exported, after we've checked for circular header dependencies. - if (OutTest.Contains(SourceFile) || Ignore.Contains(SourceFile)) - { - return; - } - - // Check for circular dependencies. - if (VisitedSet.Contains(SourceFile)) - { - UE_LOG(LogCompile, Error, TEXT("Circular dependency detected for filename %s!"), *SourceFile->GetFilename()); - return; - } - - // Check for circular header dependencies between export classes. - bCheckDependenciesOnly = bCheckDependenciesOnly || SourceFile->GetPackage() != Package; - - VisitedSet.Add(SourceFile); - for (FHeaderProvider& Include : SourceFile->GetIncludes()) - { - if (FUnrealSourceFile* IncludeFile = Include.Resolve(HeaderParser.UnrealSourceFilesMap, HeaderParser.TypeDefinitionInfoMap)) - { - GetSourceFilesInDependencyOrderRecursive(OutTest, IncludeFile, VisitedSet, bCheckDependenciesOnly, Ignore); - } - } - VisitedSet.Remove(SourceFile); - - if (!bCheckDependenciesOnly) - { - OutTest.Add(SourceFile); - } -} - -TArray FNativeClassHeaderGenerator::GetSourceFilesInDependencyOrder(const TSet& SourceFiles, const TSet& Ignore) const -{ - TArray Result; - TSet VisitedSet; - for (FUnrealSourceFile* SourceFile : SourceFiles) - { - if (SourceFile->GetPackage() == Package) - { - GetSourceFilesInDependencyOrderRecursive(Result, SourceFile, VisitedSet, false, Ignore); - } - } - - return Result; -} - -TMap GClassToSourceFileMap; - -static bool HasDynamicOuter(UField* Field) -{ - UField* FieldOuter = Cast(Field->GetOuter()); - return FieldOuter && FClass::IsDynamic(FieldOuter); -} - -static void RecordPackageSingletons( - const UPackage& Package, - const TArray& Structs, - const TArray& Delegates, - TMap>& PackageSingletons, - FCriticalSection& PackageSingletonsCriticalSection) -{ - TArray Singletons; - Singletons.Reserve(Structs.Num() + Delegates.Num()); - for (UScriptStruct* Struct : Structs) - { - if (Struct->StructFlags & STRUCT_NoExport && !HasDynamicOuter(Struct)) - { - Singletons.Add(Struct); - } - } - - for (UDelegateFunction* Delegate : Delegates) - { - if (!HasDynamicOuter(Delegate)) - { - Singletons.Add(Delegate); - } - } - - if (Singletons.Num()) - { - FScopeLock PackageSingletonLock(&PackageSingletonsCriticalSection); - - TArray& CurrentPackageSingletons = PackageSingletons.FindOrAdd(&Package); - CurrentPackageSingletons.Append(MoveTemp(Singletons)); - } -} - -struct FPreloadHeaderFileInfo -{ - FPreloadHeaderFileInfo() - : bFinishedLoading(true) - { - } - - ~FPreloadHeaderFileInfo() - { - EnsureLoadComplete(); - } - - void Load(FString&& InHeaderPath) - { - if (!bFinishedLoading || HeaderFileContents.Len() > 0) - { - if (ensureMsgf(InHeaderPath == HeaderPath, TEXT("FPreloadHeaderFileInfo::Load called twice with different paths."))) - { - // If we've done an async load but now a sync load has been requested we need to wait on it - EnsureLoadComplete(); - } - } - else - { - HeaderPath = MoveTemp(InHeaderPath); - - SCOPE_SECONDS_COUNTER_UHT(LoadHeaderContentFromFile); - FFileHelper::LoadFileToString(HeaderFileContents, *HeaderPath); - } - } - - void StartLoad(FString&& InHeaderPath) - { - if (!bFinishedLoading || HeaderFileContents.Len() > 0) - { - ensureMsgf(InHeaderPath == HeaderPath, TEXT("FPreloadHeaderFileInfo::Load called twice with different paths.")); - return; - } - auto LoadFileContentsTask = [this]() - { - SCOPE_SECONDS_COUNTER_UHT(LoadHeaderContentFromFile); - FFileHelper::LoadFileToString(HeaderFileContents, *HeaderPath); - - bFinishedLoading = true; - }; - - HeaderPath = MoveTemp(InHeaderPath); - bFinishedLoading = false; - - LoadTaskRef = FFunctionGraphTask::CreateAndDispatchWhenReady(MoveTemp(LoadFileContentsTask), TStatId()); - } - - const FString& GetFileContents() const - { - EnsureLoadComplete(); - return HeaderFileContents; - } - - FString& GetHeaderPath() { return HeaderPath; } - const FString& GetHeaderPath() const { return HeaderPath; } - -private: - - void EnsureLoadComplete() const - { - if (!bFinishedLoading) - { - FTaskGraphInterface::Get().WaitUntilTaskCompletes(LoadTaskRef); - } - } - - FString HeaderPath; - FString HeaderFileContents; - FGraphEventRef LoadTaskRef; - bool bFinishedLoading; -}; - -// Constructor. -FNativeClassHeaderGenerator::FNativeClassHeaderGenerator( - const FHeaderParser& InHeaderParser, - const UPackage* InPackage, - const TSet& SourceFiles, - FClasses& AllClasses, - bool InAllowSaveExportedHeaders, - const FPublicSourceFileSet& PublicSourceFileSet, - const TMap& PackageToManifestModuleMap -) - : HeaderParser(InHeaderParser) - , API (FPackageName::GetShortName(InPackage).ToUpper()) - , APIStringPrivate (FString::Printf(TEXT("%s_API "), *API)) - , Package (InPackage) - , bAllowSaveExportedHeaders (InAllowSaveExportedHeaders) -{ - FString PackageName = FPackageName::GetShortName(Package); - - FManifestModule* PackageManifest = GetPackageManifest(PackageName); - if (!PackageManifest) - { - UE_LOG(LogCompile, Error, TEXT("Failed to find path for package %s"), *PackageName); - } - - bool bWriteClassesH = false; - const bool bPackageHasAnyExportClasses = AllClasses.GetClassesInPackage(Package).ContainsByPredicate([](FClass* Class) - { - return Class->HasAnyClassFlags(CLASS_Native) && !Class->HasAnyClassFlags(CLASS_NoExport | CLASS_Intrinsic); - }); - if (bPackageHasAnyExportClasses) - { - for (FUnrealSourceFile* SourceFile : SourceFiles) - { - for (const TPair& ClassDataPair : SourceFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - if (!Class->HasAnyClassFlags(CLASS_Native)) - { - Class->UnMark(EObjectMark(OBJECTMARK_TagImp | OBJECTMARK_TagExp)); - } - else if (!Class->HasAnyClassFlags(CLASS_NoExport) && HeaderParser.TypeDefinitionInfoMap.Contains(Class)) - { - bWriteClassesH = true; - Class->UnMark(OBJECTMARK_TagImp); - Class->Mark(OBJECTMARK_TagExp); - } - } - } - } - - TArray Exported; - { - // Get source files and ignore them next time round - static TSet ExportedSourceFiles; - Exported = GetSourceFilesInDependencyOrder(SourceFiles, ExportedSourceFiles); - ExportedSourceFiles.Append(Exported); - } - - struct FGeneratedCPP - { - explicit FGeneratedCPP(FString&& InGeneratedCppFullFilename) - : GeneratedCppFullFilename(MoveTemp(InGeneratedCppFullFilename)) - { - } - - FString GeneratedCppFullFilename; - TArray RelativeIncludes; - FUHTStringBuilderLineCounter GeneratedText; - TSet CrossModuleReferences; - TSet PackageHeaderPaths; - TArray TempHeaderPaths; - FUHTStringBuilder GeneratedFunctionDeclarations; - }; - - TMap GeneratedCPPs; - GeneratedCPPs.Reserve(Exported.Num()); - - // Set up the generated cpp map - for (FUnrealSourceFile* SourceFile : Exported) - { - FString ModuleRelativeFilename = SourceFile->GetFilename(); - ConvertToBuildIncludePath(Package, ModuleRelativeFilename, PackageToManifestModuleMap); - - FString StrippedName = FPaths::GetBaseFilename(ModuleRelativeFilename); - FString GeneratedSourceFilename = (PackageManifest->GeneratedIncludeDirectory / StrippedName) + TEXT(".gen.cpp"); - - FGeneratedCPP& GeneratedCPP = GeneratedCPPs.Emplace(SourceFile, MoveTemp(GeneratedSourceFilename)); - GeneratedCPP.RelativeIncludes.Add(MoveTemp(ModuleRelativeFilename)); - - // This needs to be done outside of parallel blocks because it will modify UClass memory. - // Later calls to SetUpUhtReplicationData inside parallel blocks should be fine, because - // they will see the memory has already been set up, and just return the parent pointer. - for (const TPair& ClassDataPair : SourceFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - if (ClassHasReplicatedProperties(Class)) - { - Class->SetUpUhtReplicationData(); - } - } - } - - const FManifestModule* ConstPackageManifest = GetPackageManifest(PackageName); - const FNativeClassHeaderGenerator* ConstThis = this; - - TempSaveTasks.SetNum(Exported.Num()); - - TArray PreloadedFiles; - PreloadedFiles.SetNum(Exported.Num()); - -#if UHT_USE_PARALLEL_FOR - ParallelFor(Exported.Num(), [&Exported, &PreloadedFiles, Package=Package, ConstPackageManifest, &PackageToManifestModuleMap](int32 Index) -#else - for(int Index = 0; Index < Exported.Num(); Index++) -#endif - { - FUnrealSourceFile* SourceFile = Exported[Index]; - - FString ModuleRelativeFilename = SourceFile->GetFilename(); - ConvertToBuildIncludePath(Package, ModuleRelativeFilename, PackageToManifestModuleMap); - - FString StrippedName = FPaths::GetBaseFilename(MoveTemp(ModuleRelativeFilename)); - FString HeaderPath = (ConstPackageManifest->GeneratedIncludeDirectory / StrippedName) + TEXT(".generated.h"); - - PreloadedFiles[Index].Load(MoveTemp(HeaderPath)); - } -#if UHT_USE_PARALLEL_FOR - ); -#endif - - // UHTLite - TMap> PackageSingletons; - FCriticalSection PackageSingletonsCriticalSection; - - FString ExceptionMessage; - -#if UHT_USE_PARALLEL_FOR - ParallelFor(Exported.Num(), [&Exported, &PreloadedFiles, &GeneratedCPPs, &TempSaveTasks=TempSaveTasks, &ExceptionMessage, ConstThis, &PackageSingletons, &PackageSingletonsCriticalSection](int32 Index) -#else - for(int Index = 0; Index < Exported.Num(); Index++) -#endif - { -#if !PLATFORM_EXCEPTIONS_DISABLED - try - { -#endif - FUnrealSourceFile* SourceFile = Exported[Index]; - - /** Forward declarations that we need for this sourcefile. */ - TSet ForwardDeclarations; - - FUHTStringBuilder GeneratedHeaderText; - FGeneratedCPP& GeneratedCPP = GeneratedCPPs.FindChecked(SourceFile); - FUHTStringBuilder& GeneratedFunctionDeclarations = GeneratedCPP.GeneratedFunctionDeclarations; - - FReferenceGatherers ReferenceGatherers(&GeneratedCPP.CrossModuleReferences, GeneratedCPP.PackageHeaderPaths, GeneratedCPP.TempHeaderPaths); - - FUHTStringBuilderLineCounter& OutText = GeneratedCPP.GeneratedText; - - TArray Enums; - TArray Structs; - TArray DelegateFunctions; - SourceFile->GetScope()->SplitTypesIntoArrays(Enums, Structs, DelegateFunctions); - - RecordPackageSingletons(*SourceFile->GetPackage(), Structs, DelegateFunctions, PackageSingletons, PackageSingletonsCriticalSection); - - // Reverse the containers as they come out in the reverse order of declaration - Algo::Reverse(Enums); - Algo::Reverse(Structs); - Algo::Reverse(DelegateFunctions); - - const FString FileDefineName = SourceFile->GetFileDefineName(); - const FString& StrippedFilename = SourceFile->GetStrippedFilename(); - - GeneratedHeaderText.Logf( - TEXT("#ifdef %s") LINE_TERMINATOR - TEXT("#error \"%s.generated.h already included, missing '#pragma once' in %s.h\"") LINE_TERMINATOR - TEXT("#endif") LINE_TERMINATOR - TEXT("#define %s") LINE_TERMINATOR - LINE_TERMINATOR, - *FileDefineName, *StrippedFilename, *StrippedFilename, *FileDefineName); - - // export delegate definitions - for (UDelegateFunction* Func : DelegateFunctions) - { - GeneratedFunctionDeclarations.Log(FTypeSingletonCache::Get(Func).GetExternDecl()); - ConstThis->ExportDelegateDeclaration(OutText, ReferenceGatherers, *SourceFile, Func); - } - - // Export enums declared in non-UClass headers. - for (UEnum* Enum : Enums) - { - // Is this ever not the case? - if (Enum->GetOuter()->IsA(UPackage::StaticClass())) - { - GeneratedFunctionDeclarations.Log(FTypeSingletonCache::Get(Enum).GetExternDecl()); - ConstThis->ExportGeneratedEnumInitCode(OutText, ReferenceGatherers, *SourceFile, Enum); - } - } - - // export boilerplate macros for structs - // reverse the order. - for (UScriptStruct* Struct : Structs) - { - GeneratedFunctionDeclarations.Log(FTypeSingletonCache::Get(Struct).GetExternDecl()); - ConstThis->ExportGeneratedStructBodyMacros(GeneratedHeaderText, OutText, ReferenceGatherers, *SourceFile, Struct); - } - - // export delegate wrapper function implementations - for (UDelegateFunction* Func : DelegateFunctions) - { - ConstThis->ExportDelegateDefinition(GeneratedHeaderText, ReferenceGatherers, *SourceFile, Func); - } - - EExportClassOutFlags ExportFlags = EExportClassOutFlags::None; - TSet AdditionalHeaders; - for (const TPair& ClassDataPair : SourceFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - if (!(Class->ClassFlags & CLASS_Intrinsic)) - { - ConstThis->ExportClassFromSourceFileInner(GeneratedHeaderText, OutText, GeneratedFunctionDeclarations, ReferenceGatherers, (FClass*)Class, *SourceFile, ExportFlags); - } - } - - if (EnumHasAnyFlags(ExportFlags, EExportClassOutFlags::NeedsPushModelHeaders)) - { - AdditionalHeaders.Add(FString(TEXT("Net/Core/PushModel/PushModelMacros.h"))); - } - - GeneratedHeaderText.Log(TEXT("#undef CURRENT_FILE_ID\r\n")); - GeneratedHeaderText.Logf(TEXT("#define CURRENT_FILE_ID %s\r\n\r\n\r\n"), *SourceFile->GetFileId()); - - for (UEnum* Enum : Enums) - { - ConstThis->ExportEnum(GeneratedHeaderText, Enum); - } - - FPreloadHeaderFileInfo& FileInfo = PreloadedFiles[Index]; - bool bHasChanged = ConstThis->WriteHeader(FileInfo, GeneratedHeaderText, AdditionalHeaders, ReferenceGatherers, TempSaveTasks[Index]); - - SourceFile->SetGeneratedFilename(MoveTemp(FileInfo.GetHeaderPath())); - SourceFile->SetHasChanged(bHasChanged); -#if !PLATFORM_EXCEPTIONS_DISABLED - } - catch (const TCHAR* Ex) - { - // Capture the first exception message from the loop and issue it out on the gamethread after the loop completes - - static FCriticalSection ExceptionCS; - FScopeLock Lock(&ExceptionCS); - - if (ExceptionMessage.Len() == 0) - { - ExceptionMessage = FString(Ex); - } - } -#endif - } -#if UHT_USE_PARALLEL_FOR - ); -#endif - - if (ExceptionMessage.Len() > 0) - { - FError::Throwf(*ExceptionMessage); - } - - FPreloadHeaderFileInfo FileInfo; - if (bWriteClassesH) - { - // Start loading the original header file for comparison - FString ClassesHeaderPath = PackageManifest->GeneratedIncludeDirectory / (PackageName + TEXT("Classes.h")); - FileInfo.StartLoad(MoveTemp(ClassesHeaderPath)); - } - - // Export an include line for each header - TSet PublicHeaderGroupIncludes; - FUHTStringBuilder GeneratedFunctionDeclarations; - - for (FUnrealSourceFile* SourceFile : Exported) - { - for (const TPair& ClassDataPair : SourceFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - GClassToSourceFileMap.Add(Class, SourceFile); - } - - if (PublicSourceFileSet.Contains(SourceFile)) - { - PublicHeaderGroupIncludes.Add(SourceFile); - } - - const FGeneratedCPP& GeneratedCPP = GeneratedCPPs.FindChecked(SourceFile); - GeneratedFunctionDeclarations.Log(GeneratedCPP.GeneratedFunctionDeclarations); - } - - // Add includes for 'Within' classes - for (FUnrealSourceFile* SourceFile : Exported) - { - bool bAddedStructuredArchiveFromArchiveHeader = false; - bool bAddedArchiveUObjectFromStructuredArchiveHeader = false; - - TArray& RelativeIncludes = GeneratedCPPs[SourceFile].RelativeIncludes; - for (const TPair& ClassDataPair : SourceFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - if (Class->ClassWithin && Class->ClassWithin != UObject::StaticClass()) - { - if (FUnrealSourceFile** WithinSourceFile = GClassToSourceFileMap.Find(Class->ClassWithin)) - { - FString Header = GetBuildPath(**WithinSourceFile, PackageToManifestModuleMap); - RelativeIncludes.AddUnique(MoveTemp(Header)); - } - } - - { - // UHTLite - FRWScopeLock Lock(HeaderParser.ClassSerializerMapLock, SLT_ReadOnly); - - if (const FArchiveTypeDefinePair* ArchiveTypeDefinePair = HeaderParser.ClassSerializerMap.Find(Class)) - { - if (!bAddedStructuredArchiveFromArchiveHeader && ArchiveTypeDefinePair->ArchiveType == ESerializerArchiveType::StructuredArchiveRecord) - { - RelativeIncludes.AddUnique(TEXT("Serialization/StructuredArchive.h")); - bAddedStructuredArchiveFromArchiveHeader = true; - } - - if (!bAddedArchiveUObjectFromStructuredArchiveHeader && ArchiveTypeDefinePair->ArchiveType == ESerializerArchiveType::Archive) - { - RelativeIncludes.AddUnique(TEXT("Serialization/ArchiveUObjectFromStructuredArchive.h")); - bAddedArchiveUObjectFromStructuredArchiveHeader = true; - } - } - } - } - } - - TSet PackageHeaderPaths; - TArray TempHeaderPaths; - if (bWriteClassesH) - { - // Write the classes and enums header prefixes. - - FUHTStringBuilder ClassesHText; - ClassesHText.Log(HeaderCopyright); - ClassesHText.Log(TEXT("#pragma once\r\n")); - ClassesHText.Log(TEXT("\r\n")); - ClassesHText.Log(TEXT("\r\n")); - - // Fill with the rest source files from this package. - if (const TArray* SourceFilesForPackage = PublicSourceFileSet.FindFilesForPackage(InPackage)) - { - PublicHeaderGroupIncludes.Append(*SourceFilesForPackage); - } - - for (FUnrealSourceFile* SourceFile : PublicHeaderGroupIncludes) - { - ClassesHText.Logf(TEXT("#include \"%s\"") LINE_TERMINATOR, *GetBuildPath(*SourceFile, PackageToManifestModuleMap)); - } - - ClassesHText.Log(LINE_TERMINATOR); - - FReferenceGatherers ReferenceGatherers(nullptr, PackageHeaderPaths, TempHeaderPaths); - - // Save the classes header if it has changed. - FGraphEventRef& SaveTaskRef = TempSaveTasks.AddDefaulted_GetRef(); - SaveHeaderIfChanged(ReferenceGatherers, FileInfo, MoveTemp(ClassesHText), SaveTaskRef); - } - - // now export the names for the functions in this package - // notice we always export this file (as opposed to only exporting if we have any marked names) - // because there would be no way to know when the file was created otherwise - UE_LOG(LogCompile, Log, TEXT("Generating code for module '%s'"), *PackageName); - - if (GeneratedFunctionDeclarations.Len()) - { - uint32 CombinedHash = 0; - for (const TPair& GeneratedCPP : GeneratedCPPs) - { - uint32 SplitHash = GenerateTextHash(*GeneratedCPP.Value.GeneratedText); - if (CombinedHash == 0) - { - // Don't combine in the first case because it keeps GUID backwards compatibility - CombinedHash = SplitHash; - } - else - { - CombinedHash = HashCombine(SplitHash, CombinedHash); - } - } - - FGeneratedCPP& GeneratedCPP = GeneratedCPPs.Emplace(nullptr, PackageManifest->GeneratedIncludeDirectory / FString::Printf(TEXT("%s.init.gen.cpp"), *PackageName)); - ExportGeneratedPackageInitCode(GeneratedCPP.GeneratedText, *GeneratedFunctionDeclarations, Package, CombinedHash, PackageSingletons); - } - - const FManifestModule* ModuleInfo = PackageToManifestModuleMap.FindChecked(Package); - - struct FGeneratedCPPInfo - { - FGeneratedCPP* GeneratedCPP; - FPreloadHeaderFileInfo FileInfo; - }; - - TArray GeneratedCPPArray; - GeneratedCPPArray.SetNum(GeneratedCPPs.Num()); - - int32 Index = 0; - for (TPair& Pair : GeneratedCPPs) - { - GeneratedCPPArray[Index++].GeneratedCPP = &Pair.Value; - } - - if (bAllowSaveExportedHeaders) - { -#if UHT_USE_PARALLEL_FOR - ParallelFor(GeneratedCPPArray.Num(), [&](int32 Index) -#else - for(Index = 0; Index < GeneratedCPPArray.Num(); Index++) -#endif - { - FGeneratedCPPInfo& CPPInfo = GeneratedCPPArray[Index]; - CPPInfo.FileInfo.Load(FString(CPPInfo.GeneratedCPP->GeneratedCppFullFilename)); - } -#if UHT_USE_PARALLEL_FOR - ); -#endif - } - - const int32 SaveTaskStartIndex = TempSaveTasks.Num(); - TempSaveTasks.AddDefaulted(GeneratedCPPArray.Num()); - - // Generate CPP files -#if UHT_USE_PARALLEL_FOR - ParallelFor(GeneratedCPPArray.Num(), [ConstThis, &GeneratedCPPArray, &TempSaveTasks=TempSaveTasks, SaveTaskStartIndex](int32 Index) -#else - for(Index = 0; Index < GeneratedCPPArray.Num(); Index++) -#endif - { - FGeneratedCPPInfo& CPPInfo = GeneratedCPPArray[Index]; - FGeneratedCPP* GeneratedCPP = CPPInfo.GeneratedCPP; - FReferenceGatherers ReferenceGatherers(nullptr, GeneratedCPP->PackageHeaderPaths, GeneratedCPP->TempHeaderPaths); - - FUHTStringBuilder FileText; - - FString GeneratedIncludes; - for (const FString& RelativeInclude : GeneratedCPP->RelativeIncludes) - { - GeneratedIncludes += FString::Printf(TEXT("#include \"%s\"\r\n"), *RelativeInclude); - } - - FString CleanFilename = FPaths::GetCleanFilename(GeneratedCPP->GeneratedCppFullFilename); - - CleanFilename.ReplaceInline(TEXT(".gen.cpp"), TEXT(""), ESearchCase::CaseSensitive); - CleanFilename.ReplaceInline(TEXT("."), TEXT("_"), ESearchCase::CaseSensitive); - - ExportGeneratedCPP( - FileText, - GeneratedCPP->CrossModuleReferences, - *CleanFilename, - *GeneratedCPP->GeneratedText, - *GeneratedIncludes - ); - - ConstThis->SaveHeaderIfChanged(ReferenceGatherers, CPPInfo.FileInfo, MoveTemp(FileText), TempSaveTasks[SaveTaskStartIndex+Index]); - } -#if UHT_USE_PARALLEL_FOR - ); -#endif - - if (bAllowSaveExportedHeaders) - { - TArray GeneratedCPPNames; - GeneratedCPPNames.Reserve(GeneratedCPPs.Num()); - for (const TPair& GeneratedCPP : GeneratedCPPs) - { - GeneratedCPPNames.Add(FPaths::GetCleanFilename(GeneratedCPP.Value.GeneratedCppFullFilename)); - } - - // Delete old generated .cpp files which we don't need because we generated less code than last time. - TArray FoundFiles; - FString BaseDir = FPaths::GetPath(ModuleInfo->GeneratedCPPFilenameBase); - IFileManager::Get().FindFiles(FoundFiles, *FPaths::Combine(BaseDir, TEXT("*.generated.cpp")), true, false); - IFileManager::Get().FindFiles(FoundFiles, *FPaths::Combine(BaseDir, TEXT("*.generated.*.cpp")), true, false); - IFileManager::Get().FindFiles(FoundFiles, *FPaths::Combine(BaseDir, TEXT("*.gen.cpp")), true, false); - IFileManager::Get().FindFiles(FoundFiles, *FPaths::Combine(BaseDir, TEXT("*.gen.*.cpp")), true, false); - for (FString& File : FoundFiles) - { - if (!GeneratedCPPNames.Contains(File)) - { - IFileManager::Get().Delete(*FPaths::Combine(*BaseDir, *File)); - } - } - } - - for (TPair& GeneratedCPP : GeneratedCPPs) - { - TempHeaderPaths.Append(MoveTemp(GeneratedCPP.Value.TempHeaderPaths)); - PackageHeaderPaths.Append(MoveTemp(GeneratedCPP.Value.PackageHeaderPaths)); - } - - // Export all changed headers from their temp files to the .h files - ExportUpdatedHeaders(MoveTemp(PackageName), MoveTemp(TempHeaderPaths), TempSaveTasks); - - // Delete stale *.generated.h files - DeleteUnusedGeneratedHeaders(MoveTemp(PackageHeaderPaths)); -} - -void FNativeClassHeaderGenerator::DeleteUnusedGeneratedHeaders(TSet&& PackageHeaderPathSet) -{ - auto DeleteUnusedGeneratedHeadersTask = [PackageHeaderPathSet = MoveTemp(PackageHeaderPathSet)]() - { - TSet AllIntermediateFolders; - - for (const FString& PackageHeader : PackageHeaderPathSet) - { - FString IntermediatePath = FPaths::GetPath(PackageHeader); - - if (AllIntermediateFolders.Contains(IntermediatePath)) - { - continue; - } - - TArray AllHeaders; - IFileManager::Get().FindFiles(AllHeaders, *(IntermediatePath / TEXT("*.generated.h")), true, false); - - for (const FString& Header : AllHeaders) - { - const FString HeaderPath = IntermediatePath / Header; - - if (PackageHeaderPathSet.Contains(HeaderPath)) - { - continue; - } - - // Check intrinsic classes. Get the class name from file name by removing .generated.h. - FString HeaderFilename = FPaths::GetBaseFilename(HeaderPath); - const int32 GeneratedIndex = HeaderFilename.Find(TEXT(".generated"), ESearchCase::IgnoreCase, ESearchDir::FromEnd); - const FString ClassName = MoveTemp(HeaderFilename).Mid(0, GeneratedIndex); - UClass* IntrinsicClass = FindObject(ANY_PACKAGE, *ClassName); - if (!IntrinsicClass || !IntrinsicClass->HasAnyClassFlags(CLASS_Intrinsic)) - { - IFileManager::Get().Delete(*HeaderPath); - } - } - - AllIntermediateFolders.Add(MoveTemp(IntermediatePath)); - } - }; - - GAsyncFileTasks.Add(FFunctionGraphTask::CreateAndDispatchWhenReady(MoveTemp(DeleteUnusedGeneratedHeadersTask), TStatId())); -} - -/** - * Dirty hack global variable to allow different result codes passed through - * exceptions. Needs to be fixed in future versions of UHT. - */ -ECompilationResult::Type GCompilationResult = ECompilationResult::OtherCompilationError; - -bool FNativeClassHeaderGenerator::SaveHeaderIfChanged(FReferenceGatherers& OutReferenceGatherers, const FPreloadHeaderFileInfo& FileInfo, FString&& InNewHeaderContents, FGraphEventRef& OutSaveTaskRef) const -{ - if ( !bAllowSaveExportedHeaders ) - { - // Return false indicating that the header did not need updating - return false; - } - - static bool bTestedCmdLine = false; - if (!bTestedCmdLine) - { - bTestedCmdLine = true; - - const FString& ProjectSavedDir = FPaths::ProjectSavedDir(); - - if (FParse::Param(FCommandLine::Get(), TEXT("WRITEREF"))) - { - const FString ReferenceGeneratedCodePath = ProjectSavedDir / TEXT("ReferenceGeneratedCode/"); - - bWriteContents = true; - UE_LOG(LogCompile, Log, TEXT("********************************* Writing reference generated code to %s."), *ReferenceGeneratedCodePath); - UE_LOG(LogCompile, Log, TEXT("********************************* Deleting all files in ReferenceGeneratedCode.")); - IFileManager::Get().DeleteDirectory(*ReferenceGeneratedCodePath, false, true); - IFileManager::Get().MakeDirectory(*ReferenceGeneratedCodePath); - } - else if (FParse::Param( FCommandLine::Get(), TEXT("VERIFYREF"))) - { - const FString ReferenceGeneratedCodePath = ProjectSavedDir / TEXT("ReferenceGeneratedCode/"); - const FString VerifyGeneratedCodePath = ProjectSavedDir / TEXT("VerifyGeneratedCode/"); - - bVerifyContents = true; - UE_LOG(LogCompile, Log, TEXT("********************************* Writing generated code to %s and comparing to %s"), *VerifyGeneratedCodePath, *ReferenceGeneratedCodePath); - UE_LOG(LogCompile, Log, TEXT("********************************* Deleting all files in VerifyGeneratedCode.")); - IFileManager::Get().DeleteDirectory(*VerifyGeneratedCodePath, false, true); - IFileManager::Get().MakeDirectory(*VerifyGeneratedCodePath); - } - } - - if (bWriteContents || bVerifyContents) - { - const FString& ProjectSavedDir = FPaths::ProjectSavedDir(); - const FString CleanFilename = FPaths::GetCleanFilename(FileInfo.GetHeaderPath()); - const FString Ref = ProjectSavedDir / TEXT("ReferenceGeneratedCode") / CleanFilename; - - if (bWriteContents) - { - int32 i; - for (i = 0 ;i < 10; i++) - { - if (FFileHelper::SaveStringToFile(InNewHeaderContents, *Ref)) - { - break; - } - FPlatformProcess::Sleep(1.0f); // I don't know why this fails after we delete the directory - } - check(i<10); - } - else - { - const FString Verify = ProjectSavedDir / TEXT("VerifyGeneratedCode") / CleanFilename; - - int32 i; - for (i = 0 ;i < 10; i++) - { - if (FFileHelper::SaveStringToFile(InNewHeaderContents, *Verify)) - { - break; - } - FPlatformProcess::Sleep(1.0f); // I don't know why this fails after we delete the directory - } - check(i<10); - FString RefHeader; - FString Message; - { - SCOPE_SECONDS_COUNTER_UHT(LoadHeaderContentFromFile); - if (!FFileHelper::LoadFileToString(RefHeader, *Ref)) - { - Message = FString::Printf(TEXT("********************************* %s appears to be a new generated file."), *CleanFilename); - } - else - { - if (FCString::Strcmp(*InNewHeaderContents, *RefHeader) != 0) - { - Message = FString::Printf(TEXT("********************************* %s has changed."), *CleanFilename); - } - } - } - if (Message.Len()) - { - UE_LOG(LogCompile, Log, TEXT("%s"), *Message); - ChangeMessages.AddUnique(MoveTemp(Message)); - } - } - } - - FString HeaderPathStr = FileInfo.GetHeaderPath(); - const FString& OriginalHeaderLocal = FileInfo.GetFileContents(); - - const bool bHasChanged = OriginalHeaderLocal.Len() == 0 || FCString::Strcmp(*OriginalHeaderLocal, *InNewHeaderContents); - if (bHasChanged) - { - // UHTLite NOTE: Always fail on differences, because we test against UHT output to ensure consistency. - static const bool bFailIfGeneratedCodeChanges = true; - UE_DEBUG_BREAK(); - /* - static const bool bFailIfGeneratedCodeChanges = FParse::Param(FCommandLine::Get(), TEXT("FailIfGeneratedCodeChanges")); - */ - - if (bFailIfGeneratedCodeChanges) - { - FString ConflictPath = HeaderPathStr + TEXT(".conflict"); - FFileHelper::SaveStringToFile(InNewHeaderContents, *ConflictPath); - - GCompilationResult = ECompilationResult::FailedDueToHeaderChange; - FError::Throwf(TEXT("ERROR: '%s': Changes to generated code are not allowed - conflicts written to '%s'"), *HeaderPathStr, *ConflictPath); - } - - // save the updated version to a tmp file so that the user can see what will be changing - FString TmpHeaderFilename = GenerateTempHeaderName(HeaderPathStr, false); - - auto SaveTempTask = [TmpHeaderFilename, InNewHeaderContents = MoveTemp(InNewHeaderContents)]() - { - // delete any existing temp file - IFileManager::Get().Delete(*TmpHeaderFilename, false, true); - if (!FFileHelper::SaveStringToFile(InNewHeaderContents, *TmpHeaderFilename)) - { - UE_LOG_WARNING_UHT(TEXT("Failed to save header export preview: '%s'"), *TmpHeaderFilename); - } - }; - - OutSaveTaskRef = FFunctionGraphTask::CreateAndDispatchWhenReady(MoveTemp(SaveTempTask), TStatId()); - - OutReferenceGatherers.TempHeaderPaths.Add(MoveTemp(TmpHeaderFilename)); - } - - // Remember this header filename to be able to check for any old (unused) headers later. - HeaderPathStr.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive); - - OutReferenceGatherers.PackageHeaderPaths.Add(MoveTemp(HeaderPathStr)); - - return bHasChanged; -} - -FString FNativeClassHeaderGenerator::GenerateTempHeaderName( const FString& CurrentFilename, bool bReverseOperation ) -{ - return bReverseOperation - ? CurrentFilename.Replace(TEXT(".tmp"), TEXT(""), ESearchCase::CaseSensitive) - : CurrentFilename + TEXT(".tmp"); -} - -void FNativeClassHeaderGenerator::ExportUpdatedHeaders(FString&& PackageName, TArray&& TempHeaderPaths, FGraphEventArray& InTempSaveTasks) -{ - // Asynchronously move the headers to the correct locations - if (TempHeaderPaths.Num() > 0) - { - auto MoveHeadersTask = [PackageName = MoveTemp(PackageName), TempHeaderPaths = MoveTemp(TempHeaderPaths)]() - { -#if UHT_USE_PARALLEL_FOR - ParallelFor(TempHeaderPaths.Num(), [&](int32 Index) -#else - for(int Index = 0; Index < TempHeaderPaths.Num(); Index++) -#endif - { - const FString& TmpFilename = TempHeaderPaths[Index]; - FString Filename = GenerateTempHeaderName(TmpFilename, true); - if (!IFileManager::Get().Move(*Filename, *TmpFilename, true, true)) - { - UE_LOG(LogCompile, Error, TEXT("Error exporting %s: couldn't write file '%s'"), *PackageName, *Filename); - } - else - { - UE_LOG(LogCompile, Log, TEXT("Exported updated C++ header: %s"), *Filename); - } - } -#if UHT_USE_PARALLEL_FOR - ); -#endif - }; - - FTaskGraphInterface::Get().WaitUntilTasksComplete(InTempSaveTasks); - GAsyncFileTasks.Add(FFunctionGraphTask::CreateAndDispatchWhenReady(MoveTemp(MoveHeadersTask), TStatId())); - } -} - -/** - * Exports C++ definitions for boilerplate that was generated for a package. - */ -void FNativeClassHeaderGenerator::ExportGeneratedCPP(FOutputDevice& Out, const TSet& InCrossModuleReferences, const TCHAR* EmptyLinkFunctionPostfix, const TCHAR* Body, const TCHAR* OtherIncludes) -{ - static const TCHAR DisableWarning4883 [] = TEXT("#ifdef _MSC_VER") LINE_TERMINATOR TEXT("#pragma warning (push)") LINE_TERMINATOR TEXT("#pragma warning (disable : 4883)") LINE_TERMINATOR TEXT("#endif") LINE_TERMINATOR; - static const TCHAR EnableWarning4883 [] = TEXT("#ifdef _MSC_VER") LINE_TERMINATOR TEXT("#pragma warning (pop)") LINE_TERMINATOR TEXT("#endif") LINE_TERMINATOR; - - Out.Log(HeaderCopyright); - Out.Log(RequiredCPPIncludes); - Out.Log(OtherIncludes); - Out.Log(DisableWarning4883); - Out.Log(DisableDeprecationWarnings); - - Out.Logf(TEXT("void EmptyLinkFunctionForGeneratedCode%s() {}") LINE_TERMINATOR, EmptyLinkFunctionPostfix); - - if (InCrossModuleReferences.Num() > 0) - { - Out.Logf(TEXT("// Cross Module References\r\n")); - for (const FString& Ref : InCrossModuleReferences) - { - Out.Log(*Ref); - } - Out.Logf(TEXT("// End Cross Module References\r\n")); - } - Out.Log(Body); - Out.Log(EnableDeprecationWarnings); - Out.Log(EnableWarning4883); -} diff --git a/Engine/Source/Programs/UHTLite/Private/FileLineException.cpp b/Engine/Source/Programs/UHTLite/Private/FileLineException.cpp deleted file mode 100644 index 555a019a2f96..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/FileLineException.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "FileLineException.h" -#include "UnrealHeaderTool.h" - -void VARARGS FFileLineException::ThrowfImpl(FString&& InFilename, int32 InLine, const TCHAR* Fmt, ...) -{ - int32 BufferSize = 512; - TCHAR StartingBuffer[512]; - TCHAR* Buffer = StartingBuffer; - int32 Result = -1; - - // First try to print to a stack allocated location - GET_VARARGS_RESULT( Buffer, BufferSize, BufferSize-1, Fmt, Fmt, Result ); - - // If that fails, start allocating regular memory - if( Result == -1 ) - { - Buffer = nullptr; - while(Result == -1) - { - BufferSize *= 2; - Buffer = (TCHAR*) FMemory::Realloc( Buffer, BufferSize * sizeof(TCHAR) ); - GET_VARARGS_RESULT( Buffer, BufferSize, BufferSize-1, Fmt, Fmt, Result ); - } - } - - Buffer[Result] = 0; - - FString ResultString(Buffer); - - if( BufferSize != 512 ) - { - FMemory::Free( Buffer ); - } - - throw FFileLineException(MoveTemp(ResultString), MoveTemp(InFilename), InLine); -} - -FFileLineException::FFileLineException(FString&& InMessage, FString&& InFilename, int32 InLine) - : Message (MoveTemp(InMessage)) - , Filename(MoveTemp(InFilename)) - , Line (InLine) -{ -} diff --git a/Engine/Source/Programs/UHTLite/Private/FileLineException.h b/Engine/Source/Programs/UHTLite/Private/FileLineException.h deleted file mode 100644 index 5fd94f752e5f..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/FileLineException.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Containers/UnrealString.h" -#include "Templates/IsValidVariadicFunctionArg.h" - -struct FFileLineException -{ - FString Message; - FString Filename; - int32 Line; - - template - UE_NORETURN static void VARARGS Throwf(FString&& Filename, int32 Line, const TCHAR* Fmt, Types... Args) - { - static_assert(TAnd...>::Value, "Invalid argument(s) passed to FError::Throwf"); - - ThrowfImpl(MoveTemp(Filename), Line, Fmt, Args...); - } - -private: - UE_NORETURN static void VARARGS ThrowfImpl(FString&& Filename, int32 Line, const TCHAR* Fmt, ...); - - FFileLineException(FString&& InMessage, FString&& InFilename, int32 InLine); -}; diff --git a/Engine/Source/Programs/UHTLite/Private/GeneratedCodeVersion.h b/Engine/Source/Programs/UHTLite/Private/GeneratedCodeVersion.h deleted file mode 100644 index e62c3b55fe24..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/GeneratedCodeVersion.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "Containers/UnrealString.h" - -class FArchive; -class FString; - -// -// This MUST be kept in sync with EGeneratedBodyVersion in UBT defined in ExternalExecution.cs -// and with ToGeneratedBodyVersion function below -// -enum class EGeneratedCodeVersion : uint8 -{ - None, - V1, - V2, - VLatest = V2 -}; - -inline FArchive& operator<<(FArchive& Ar, EGeneratedCodeVersion& Type) -{ - if (Ar.IsLoading()) - { - uint8 Value; - Ar << Value; - Type = (EGeneratedCodeVersion)Value; - } - else if (Ar.IsSaving()) - { - uint8 Value = (uint8)Type; - Ar << Value; - } - return Ar; -} - -inline EGeneratedCodeVersion ToGeneratedCodeVersion(const FString& InString) -{ - if (InString.Compare(TEXT("V1")) == 0) - { - return EGeneratedCodeVersion::V1; - } - - if (InString.Compare(TEXT("V2")) == 0) - { - return EGeneratedCodeVersion::V2; - } - - if (InString.Compare(TEXT("VLatest")) == 0) - { - return EGeneratedCodeVersion::VLatest; - } - - return EGeneratedCodeVersion::None; -} diff --git a/Engine/Source/Programs/UHTLite/Private/HeaderParser.cpp b/Engine/Source/Programs/UHTLite/Private/HeaderParser.cpp deleted file mode 100644 index fdcd9ddb7ec4..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/HeaderParser.cpp +++ /dev/null @@ -1,10828 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - - -#include "HeaderParser.h" -#include "UnrealHeaderTool.h" -#include "HAL/FileManager.h" -#include "Misc/CommandLine.h" -#include "Misc/ConfigCacheIni.h" -#include "Misc/FeedbackContext.h" -#include "UObject/Interface.h" -#include "ParserClass.h" -#include "GeneratedCodeVersion.h" -#include "ClassDeclarationMetaData.h" -#include "ProfilingDebugging/ScopedTimers.h" -#include "NativeClassExporter.h" -#include "Classes.h" -#include "StringUtils.h" -#include "Misc/DefaultValueHelper.h" -#include "Manifest.h" -#include "Math/UnitConversion.h" -#include "FileLineException.h" -#include "UnrealTypeDefinitionInfo.h" -#include "Containers/EnumAsByte.h" -#include "Algo/AllOf.h" -#include "Algo/Find.h" -#include "Algo/FindSortedStringCaseInsensitive.h" -#include "Misc/ScopeExit.h" - -#include "Specifiers/CheckedMetadataSpecifiers.h" -#include "Specifiers/FunctionSpecifiers.h" -#include "Specifiers/InterfaceSpecifiers.h" -#include "Specifiers/StructSpecifiers.h" -#include "Specifiers/VariableSpecifiers.h" - -double GPluginOverheadTime = 0.0; -double GHeaderCodeGenTime = 0.0; - -/*----------------------------------------------------------------------------- - Constants & declarations. ------------------------------------------------------------------------------*/ - -/** - * 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 AnnotatedSet; -} static FailedFilesAnnotation; - -enum {MAX_ARRAY_SIZE=2048}; - -namespace -{ - static const FName NAME_Comment(TEXT("Comment")); - static const FName NAME_ToolTip(TEXT("ToolTip")); - static const FName NAME_DocumentationPolicy(TEXT("DocumentationPolicy")); - static const FName NAME_AllowPrivateAccess(TEXT("AllowPrivateAccess")); - static const FName NAME_ExposeOnSpawn(TEXT("ExposeOnSpawn")); - static const FName NAME_NativeConst(TEXT("NativeConst")); - static const FName NAME_NativeConstTemplateArg(TEXT("NativeConstTemplateArg")); - static const FName NAME_BlueprintInternalUseOnly(TEXT("BlueprintInternalUseOnly")); - static const FName NAME_DeprecatedFunction(TEXT("DeprecatedFunction")); - static const FName NAME_BlueprintSetter(TEXT("BlueprintSetter")); - static const FName NAME_BlueprintGetter(TEXT("BlueprintGetter")); - static const FName NAME_Category(TEXT("Category")); - static const FName NAME_ReturnValue(TEXT("ReturnValue")); - static const FName NAME_CppFromBpEvent(TEXT("CppFromBpEvent")); - static const FName NAME_CustomThunk(TEXT("CustomThunk")); - static const FName NAME_ArraySizeEnum(TEXT("ArraySizeEnum")); - static const FName NAME_ClassGroupNames(TEXT("ClassGroupNames")); - static const FName NAME_AutoCollapseCategories(TEXT("AutoCollapseCategories")); - static const FName NAME_HideFunctions(TEXT("HideFunctions")); - static const FName NAME_AutoExpandCategories(TEXT("AutoExpandCategories")); - static const FName NAME_EditInline(TEXT("EditInline")); - static const FName NAME_IncludePath(TEXT("IncludePath")); - static const FName NAME_ModuleRelativePath(TEXT("ModuleRelativePath")); - static const FName NAME_CannotImplementInterfaceInBlueprint(TEXT("CannotImplementInterfaceInBlueprint")); - static const FName NAME_UIMin(TEXT("UIMin")); - static const FName NAME_UIMax(TEXT("UIMax")); - static const FName NAME_BlueprintType(TEXT("BlueprintType")); -} - -const FName FHeaderParserNames::NAME_HideCategories(TEXT("HideCategories")); -const FName FHeaderParserNames::NAME_ShowCategories(TEXT("ShowCategories")); -const FName FHeaderParserNames::NAME_SparseClassDataTypes(TEXT("SparseClassDataTypes")); -const FName FHeaderParserNames::NAME_IsConversionRoot(TEXT("IsConversionRoot")); -const FName FHeaderParserNames::NAME_AdvancedClassDisplay(TEXT("AdvancedClassDisplay")); - -const TArray FHeaderParser::PropertyCPPTypesRequiringUIRanges = { TEXT("float"), TEXT("double") }; -const TArray FHeaderParser::ReservedTypeNames = { TEXT("none") }; -TMap ClassDefinitionRanges; - -/** - * 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; - -/*----------------------------------------------------------------------------- - Utility functions. ------------------------------------------------------------------------------*/ - -namespace -{ - bool ProbablyAMacro(const TCHAR* Identifier) - { - // Macros must start with a capitalized alphanumeric character or underscore - TCHAR FirstChar = Identifier[0]; - if (FirstChar != TEXT('_') && (FirstChar < TEXT('A') || FirstChar > TEXT('Z'))) - { - return false; - } - - // Test for known delegate and event macros. - TCHAR MulticastDelegateStart[] = TEXT("DECLARE_MULTICAST_DELEGATE"); - if (!FCString::Strncmp(Identifier, MulticastDelegateStart, UE_ARRAY_COUNT(MulticastDelegateStart) - 1)) - { - return true; - } - - TCHAR DelegateStart[] = TEXT("DECLARE_DELEGATE"); - if (!FCString::Strncmp(Identifier, DelegateStart, UE_ARRAY_COUNT(DelegateStart) - 1)) - { - return true; - } - - TCHAR DelegateEvent[] = TEXT("DECLARE_EVENT"); - if (!FCString::Strncmp(Identifier, DelegateEvent, UE_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')) && (Ch < TEXT('0') || Ch > TEXT('9'))) - { - return false; - } - } - - return true; - } - - /** - * Tests if an identifier looks like a macro which doesn't have a following open parenthesis. - * - * @param HeaderParser The parser to retrieve the next token. - * @param Token The token to test for being callable-macro-like. - * - * @return true if it looks like a non-callable macro, false otherwise. - */ - bool ProbablyAnUnknownObjectLikeMacro(FHeaderParser& HeaderParser, FToken Token) - { - // Non-identifiers are not macros - if (Token.TokenType != TOKEN_Identifier) - { - return false; - } - - // Macros must start with a capitalized alphanumeric character or underscore - TCHAR FirstChar = Token.Identifier[0]; - if (FirstChar != TEXT('_') && (FirstChar < TEXT('A') || FirstChar > TEXT('Z'))) - { - return false; - } - - // We'll guess about it being a macro based on it being fully-capitalized with at least one underscore. - const TCHAR* IdentPtr = Token.Identifier; - int32 UnderscoreCount = 0; - while (TCHAR Ch = *++IdentPtr) - { - if (Ch == TEXT('_')) - { - ++UnderscoreCount; - } - else if ((Ch < TEXT('A') || Ch > TEXT('Z')) && (Ch < TEXT('0') || Ch > TEXT('9'))) - { - return false; - } - } - - // We look for at least one underscore as a convenient way of whitelisting many known macros - // like FORCEINLINE and CONSTEXPR, and non-macros like FPOV and TCHAR. - if (UnderscoreCount == 0) - { - return false; - } - - // Identifiers which end in _API are known - if (IdentPtr - Token.Identifier > 4 && IdentPtr[-4] == TEXT('_') && IdentPtr[-3] == TEXT('A') && IdentPtr[-2] == TEXT('P') && IdentPtr[-1] == TEXT('I')) - { - return false; - } - - // Ignore certain known macros or identifiers that look like macros. - // IMPORTANT: needs to be in lexicographical order. - static const TCHAR* Whitelist[] = - { - TEXT("FORCEINLINE_DEBUGGABLE"), - TEXT("FORCEINLINE_STATS"), - TEXT("SIZE_T") - }; - if (Algo::FindSortedStringCaseInsensitive(Token.Identifier, Whitelist, UE_ARRAY_COUNT(Whitelist)) >= 0) - { - return false; - } - - // Check if there's an open parenthesis following the token. - // - // Rather than ungetting the bracket token, we unget the original identifier token, - // then get it again, so we don't lose any comments which may exist between the token - // and the non-bracket. - FToken PossibleBracketToken; - HeaderParser.GetToken(PossibleBracketToken); - HeaderParser.UngetToken(Token); - HeaderParser.GetToken(Token); - - bool bResult = PossibleBracketToken.TokenType != TOKEN_Symbol || !PossibleBracketToken.Matches(TEXT('(')); - return bResult; - } - - /** - * 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& Identifiers) - { - static const TCHAR IdTag [] = TEXT("Id"); - static const TCHAR ResponseIdTag [] = TEXT("ResponseId"); - static const TCHAR JSBridgePriTag[] = TEXT("Priority"); - - for (const FString& Identifier : Identifiers) - { - const TCHAR* IdentifierPtr = *Identifier; - - if (const TCHAR* Equals = FCString::Strchr(IdentifierPtr, TEXT('='))) - { - // It's a tag with an argument - - if (FCString::Strnicmp(IdentifierPtr, IdTag, UE_ARRAY_COUNT(IdTag) - 1) == 0) - { - int32 TempInt = FCString::Atoi(Equals + 1); - if (TempInt <= 0 || TempInt > MAX_uint16) - { - FError::Throwf(TEXT("Invalid network identifier %s for function"), IdentifierPtr); - } - FuncInfo.RPCId = (uint16)TempInt; - } - else if (FCString::Strnicmp(IdentifierPtr, ResponseIdTag, UE_ARRAY_COUNT(ResponseIdTag) - 1) == 0 || - FCString::Strnicmp(IdentifierPtr, JSBridgePriTag, UE_ARRAY_COUNT(JSBridgePriTag) - 1) == 0) - { - int32 TempInt = FCString::Atoi(Equals + 1); - if (TempInt <= 0 || TempInt > MAX_uint16) - { - FError::Throwf(TEXT("Invalid network identifier %s for function"), IdentifierPtr); - } - FuncInfo.RPCResponseId = (uint16)TempInt; - } - } - else - { - // Assume it's an endpoint name - - if (FuncInfo.EndpointName.Len()) - { - FError::Throwf(TEXT("Function should not specify multiple endpoints - '%s' found but already using '%s'"), *Identifier); - } - - FuncInfo.EndpointName = Identifier; - } - } - } - - /** - * 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& Specifiers, TMap& MetaData) - { - bool bSpecifiedUnreliable = false; - bool bSawPropertyAccessor = false; - - for (const FPropertySpecifier& Specifier : Specifiers) - { - switch ((EFunctionSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GFunctionSpecifierStrings)) - { - default: - { - FError::Throwf(TEXT("Unknown function specifier '%s'"), *Specifier.Key); - } - break; - - case EFunctionSpecifier::BlueprintNativeEvent: - { - if (FuncInfo.FunctionFlags & FUNC_Net) - { - UE_LOG_ERROR_UHT(TEXT("BlueprintNativeEvent functions cannot be replicated!") ); - } - else if ( (FuncInfo.FunctionFlags & FUNC_BlueprintEvent) && !(FuncInfo.FunctionFlags & FUNC_Native) ) - { - // already a BlueprintImplementableEvent - UE_LOG_ERROR_UHT(TEXT("A function cannot be both BlueprintNativeEvent and BlueprintImplementableEvent!") ); - } - else if (bSawPropertyAccessor) - { - UE_LOG_ERROR_UHT(TEXT("A function cannot be both BlueprintNativeEvent and a Blueprint Property accessor!")); - } - else if ( (FuncInfo.FunctionFlags & FUNC_Private) ) - { - UE_LOG_ERROR_UHT(TEXT("A Private function cannot be a BlueprintNativeEvent!") ); - } - - FuncInfo.FunctionFlags |= FUNC_Event; - FuncInfo.FunctionFlags |= FUNC_BlueprintEvent; - } - break; - - case EFunctionSpecifier::BlueprintImplementableEvent: - { - if (FuncInfo.FunctionFlags & FUNC_Net) - { - UE_LOG_ERROR_UHT(TEXT("BlueprintImplementableEvent functions cannot be replicated!") ); - } - else if ( (FuncInfo.FunctionFlags & FUNC_BlueprintEvent) && (FuncInfo.FunctionFlags & FUNC_Native) ) - { - // already a BlueprintNativeEvent - UE_LOG_ERROR_UHT(TEXT("A function cannot be both BlueprintNativeEvent and BlueprintImplementableEvent!") ); - } - else if (bSawPropertyAccessor) - { - UE_LOG_ERROR_UHT(TEXT("A function cannot be both BlueprintImplementableEvent and a Blueprint Property accessor!")); - } - else if ( (FuncInfo.FunctionFlags & FUNC_Private) ) - { - UE_LOG_ERROR_UHT(TEXT("A Private function cannot be a BlueprintImplementableEvent!") ); - } - - FuncInfo.FunctionFlags |= FUNC_Event; - FuncInfo.FunctionFlags |= FUNC_BlueprintEvent; - FuncInfo.FunctionFlags &= ~FUNC_Native; - } - break; - - case EFunctionSpecifier::Exec: - { - FuncInfo.FunctionFlags |= FUNC_Exec; - if( FuncInfo.FunctionFlags & FUNC_Net ) - { - UE_LOG_ERROR_UHT(TEXT("Exec functions cannot be replicated!") ); - } - } - break; - - case EFunctionSpecifier::SealedEvent: - { - FuncInfo.bSealedEvent = true; - } - break; - - case EFunctionSpecifier::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; - - if (Specifier.Values.Num()) - { - FuncInfo.CppImplName = Specifier.Values[0]; - } - - if( FuncInfo.FunctionFlags & FUNC_Exec ) - { - UE_LOG_ERROR_UHT(TEXT("Exec functions cannot be replicated!") ); - } - } - break; - - case EFunctionSpecifier::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; - - if (Specifier.Values.Num()) - { - FuncInfo.CppImplName = Specifier.Values[0]; - } - } - break; - - case EFunctionSpecifier::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; - } - break; - - case EFunctionSpecifier::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); - - if (FuncInfo.EndpointName.Len() == 0) - { - FError::Throwf(TEXT("ServiceRequest needs to specify an endpoint name")); - } - } - break; - - case EFunctionSpecifier::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); - - if (FuncInfo.EndpointName.Len() == 0) - { - FError::Throwf(TEXT("ServiceResponse needs to specify an endpoint name")); - } - } - break; - - case EFunctionSpecifier::Reliable: - { - FuncInfo.FunctionFlags |= FUNC_NetReliable; - } - break; - - case EFunctionSpecifier::Unreliable: - { - bSpecifiedUnreliable = true; - } - break; - - case EFunctionSpecifier::CustomThunk: - { - FuncInfo.FunctionExportFlags |= FUNCEXPORT_CustomThunk; - } - break; - - case EFunctionSpecifier::BlueprintCallable: - { - FuncInfo.FunctionFlags |= FUNC_BlueprintCallable; - } - break; - - case EFunctionSpecifier::BlueprintGetter: - { - if (FuncInfo.FunctionFlags & FUNC_Event) - { - UE_LOG_ERROR_UHT(TEXT("Function cannot be a blueprint event and a blueprint getter.")); - } - - bSawPropertyAccessor = true; - FuncInfo.FunctionFlags |= FUNC_BlueprintCallable; - FuncInfo.FunctionFlags |= FUNC_BlueprintPure; - MetaData.Add(NAME_BlueprintGetter); - } - break; - - case EFunctionSpecifier::BlueprintSetter: - { - if (FuncInfo.FunctionFlags & FUNC_Event) - { - UE_LOG_ERROR_UHT(TEXT("Function cannot be a blueprint event and a blueprint setter.")); - } - - bSawPropertyAccessor = true; - FuncInfo.FunctionFlags |= FUNC_BlueprintCallable; - MetaData.Add(NAME_BlueprintSetter); - } - break; - - case EFunctionSpecifier::BlueprintPure: - { - bool bIsPure = true; - if (Specifier.Values.Num() == 1) - { - FString IsPureStr = Specifier.Values[0]; - bIsPure = IsPureStr.ToBool(); - } - - // This function can be called, and is also pure. - FuncInfo.FunctionFlags |= FUNC_BlueprintCallable; - - if (bIsPure) - { - FuncInfo.FunctionFlags |= FUNC_BlueprintPure; - } - else - { - FuncInfo.bForceBlueprintImpure = true; - } - } - break; - - case EFunctionSpecifier::BlueprintAuthorityOnly: - { - FuncInfo.FunctionFlags |= FUNC_BlueprintAuthorityOnly; - } - break; - - case EFunctionSpecifier::BlueprintCosmetic: - { - FuncInfo.FunctionFlags |= FUNC_BlueprintCosmetic; - } - break; - - case EFunctionSpecifier::WithValidation: - { - FuncInfo.FunctionFlags |= FUNC_NetValidate; - - if (Specifier.Values.Num()) - { - FuncInfo.CppValidationImplName = Specifier.Values[0]; - } - } - break; - } - } - - 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) - { - UE_LOG_ERROR_UHT(TEXT("Static functions can't be replicated")); - } - - if (!bIsNetReliable && !bSpecifiedUnreliable && !bIsNetService) - { - UE_LOG_ERROR_UHT(TEXT("Replicated function: 'reliable' or 'unreliable' is required")); - } - - if (bIsNetReliable && bSpecifiedUnreliable && !bIsNetService) - { - UE_LOG_ERROR_UHT(TEXT("'reliable' and 'unreliable' are mutually exclusive")); - } - } - else if (FuncInfo.FunctionFlags & FUNC_NetReliable) - { - UE_LOG_ERROR_UHT(TEXT("'reliable' specified without 'client' or 'server'")); - } - else if (bSpecifiedUnreliable) - { - UE_LOG_ERROR_UHT(TEXT("'unreliable' specified without 'client' or 'server'")); - } - - if (FuncInfo.bSealedEvent && !(FuncInfo.FunctionFlags & FUNC_Event)) - { - UE_LOG_ERROR_UHT(TEXT("SealedEvent may only be used on events")); - } - - if (FuncInfo.bSealedEvent && FuncInfo.FunctionFlags & FUNC_BlueprintEvent) - { - UE_LOG_ERROR_UHT(TEXT("SealedEvent cannot be used on Blueprint events")); - } - - if (FuncInfo.bForceBlueprintImpure && (FuncInfo.FunctionFlags & FUNC_BlueprintPure) != 0) - { - UE_LOG_ERROR_UHT(TEXT("BlueprintPure (or BlueprintPure=true) and BlueprintPure=false should not both appear on the same function, they are mutually exclusive")); - } - } - - void AddEditInlineMetaData(TMap& MetaData) - { - MetaData.Add(NAME_EditInline, TEXT("true")); - } - - 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 - check(false); - return nullptr; - } - - /** - * 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(FFieldVariant Field, const FName InKey, const FString& InValue) - { - switch (GetCheckedMetadataSpecifier(InKey)) - { - default: - { - // Don't need to validate this specifier - } - break; - - case ECheckedMetadataSpecifier::UIMin: - case ECheckedMetadataSpecifier::UIMax: - case ECheckedMetadataSpecifier::ClampMin: - case ECheckedMetadataSpecifier::ClampMax: - { - if (!InValue.IsNumeric()) - { - FError::Throwf(TEXT("Metadata value for '%s' is non-numeric : '%s'"), *InKey.ToString(), *InValue); - } - } - break; - - case ECheckedMetadataSpecifier::BlueprintProtected: - { - if (Field.IsUObject()) - { - UFunction* Function = Field.Get(); - if (Function->HasAnyFunctionFlags(FUNC_Static)) - { - // Determine if it's a function library - UClass* Class = 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.ToString(), *Function->GetName()); - } - } - } - } - break; - - case ECheckedMetadataSpecifier::CommutativeAssociativeBinaryOperator: - { - if (UFunction* Function = Field.Get()) - { - bool bGoodParams = (Function->NumParms == 3); - if (bGoodParams) - { - FProperty* FirstParam = nullptr; - FProperty* SecondParam = nullptr; - FProperty* ReturnValue = nullptr; - - TFieldIterator It(Function); - - auto GetNextParam = [&]() - { - if (It) - { - if (It->HasAnyPropertyFlags(CPF_ReturnParm)) - { - ReturnValue = *It; - } - else - { - if (FirstParam == nullptr) - { - FirstParam = *It; - } - else if (SecondParam == nullptr) - { - SecondParam = *It; - } - } - ++It; - } - }; - - GetNextParam(); - GetNextParam(); - GetNextParam(); - ensure(!It); - - if (ReturnValue == nullptr || SecondParam == nullptr || !SecondParam->SameType(FirstParam)) - { - bGoodParams = false; - } - } - - if (!bGoodParams) - { - UE_LOG_ERROR_UHT(TEXT("Commutative asssociative binary operators must have exactly 2 parameters of the same type and a return value.")); - } - } - } - break; - - case ECheckedMetadataSpecifier::ExpandBoolAsExecs: - case ECheckedMetadataSpecifier::ExpandEnumAsExecs: - { - if (UFunction* Function = Field.Get()) - { - // multiple entry parsing in the same format as eg SetParam. - TArray RawGroupings; - InValue.ParseIntoArray(RawGroupings, TEXT(","), false); - - FProperty* FirstInput = nullptr; - for (const FString& RawGroup : RawGroupings) - { - TArray IndividualEntries; - RawGroup.ParseIntoArray(IndividualEntries, TEXT("|")); - - for (const FString& Entry : IndividualEntries) - { - if (Entry.IsEmpty()) - { - continue; - } - - FField* FoundField = FHeaderParser::FindProperty(Function, *Entry, false); - if (!FoundField) - { - UE_LOG_ERROR_UHT(TEXT("Function does not have a parameter named '%s'"), *Entry); - } - else if (FProperty* Prop = CastField(FoundField)) - { - if (!Prop->HasAnyPropertyFlags(CPF_ReturnParm) && - - (!Prop->HasAnyPropertyFlags(CPF_OutParm) || - Prop->HasAnyPropertyFlags(CPF_ReferenceParm))) - { - if (!FirstInput) - { - FirstInput = Prop; - } - else - { - UE_LOG_ERROR_UHT(TEXT("Function already specified an ExpandEnumAsExec input (%s), but '%s' is also an input parameter. Only one is permitted."), *FirstInput->GetName(), *Entry); - } - } - } - } - } - } - } - break; - - case ECheckedMetadataSpecifier::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.ToString(), *InValue, *ExperimentalValue, *EarlyAccessValue); - } - } - break; - - case ECheckedMetadataSpecifier::Units: - { - // Check for numeric property - FField* MaybeProperty = Field.ToField(); - if (!MaybeProperty->IsA() && !MaybeProperty->IsA()) - { - FError::Throwf(TEXT("'Units' meta data can only be applied to numeric and struct properties")); - } - - if (!FUnitConversion::UnitFromString(*InValue)) - { - FError::Throwf(TEXT("Unrecognized units (%s) specified for property '%s'"), *InValue, *Field.GetFullName()); - } - } - break; - - case ECheckedMetadataSpecifier::DocumentationPolicy: - { - const TCHAR* StrictValue = TEXT("Strict"); - if (InValue != StrictValue) - { - FError::Throwf(TEXT("'%s' metadata was '%s' but it must be %s"), *InKey.ToString(), *InValue, *StrictValue); - } - } - break; - } - } - - // Ensures at script compile time that the metadata formatting is correct - void ValidateMetaDataFormat(FFieldVariant Field, const TMap& MetaData) - { - for (const TPair& Pair : MetaData) - { - ValidateMetaDataFormat(Field, Pair.Key, Pair.Value); - } - } - - // Validates the metadata, then adds it to the class data - void AddMetaDataToClassData(FFieldVariant Field, TMap&& InMetaData) - { - // Evaluate any key redirects on the passed in pairs - for (TPair& Pair : InMetaData) - { - FName& CurrentKey = Pair.Key; - FName NewKey = UMetaData::GetRemappedKeyName(CurrentKey); - - if (NewKey != NAME_None) - { - UE_LOG_WARNING_UHT(TEXT("Remapping old metadata key '%s' to new key '%s', please update the declaration."), *CurrentKey.ToString(), *NewKey.ToString()); - CurrentKey = NewKey; - } - } - - // Finish validating and associate the metadata with the field - ValidateMetaDataFormat(Field, InMetaData); - if (Field.IsUObject()) - { - FClassMetaData::AddMetaData(CastChecked(Field.ToUObject()), MoveTemp(InMetaData)); - } - else - { - FClassMetaData::AddMetaData(Field.ToField(), MoveTemp(InMetaData)); - } - } - - bool IsPropertySupportedByBlueprint(const FProperty* Property, bool bMemberVariable) - { - if (Property == nullptr) - { - return false; - } - if (const FArrayProperty* ArrayProperty = CastField(Property)) - { - // Script VM doesn't support array of weak ptrs. - return IsPropertySupportedByBlueprint(ArrayProperty->Inner, false); - } - else if (const FSetProperty* SetProperty = CastField(Property)) - { - return IsPropertySupportedByBlueprint(SetProperty->ElementProp, false); - } - else if (const FMapProperty* MapProperty = CastField(Property)) - { - return IsPropertySupportedByBlueprint(MapProperty->KeyProp, false) && - IsPropertySupportedByBlueprint(MapProperty->ValueProp, false); - } - else if (const FStructProperty* StructProperty = CastField(Property)) - { - return (StructProperty->Struct->GetBoolMetaDataHierarchical(NAME_BlueprintType)); - } - - const bool bSupportedType = Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA() - || Property->IsA(); - - const bool bIsSupportedMemberVariable = Property->IsA() || Property->IsA(); - - return bSupportedType || (bIsSupportedMemberVariable && bMemberVariable); - } - - void SkipAlignasIfNecessary(FBaseParser& Parser) - { - if (Parser.MatchIdentifier(TEXT("alignas"), ESearchCase::CaseSensitive)) - { - Parser.RequireSymbol(TEXT('('), TEXT("'alignas'")); - Parser.RequireAnyConstInt(TEXT("'alignas'")); - Parser.RequireSymbol(TEXT(')'), TEXT("'alignas'")); - } - } - - void SkipDeprecatedMacroIfNecessary(FBaseParser& Parser) - { - FToken MacroToken; - if (!Parser.GetToken(MacroToken)) - { - return; - } - - if (MacroToken.TokenType != TOKEN_Identifier || (FCString::Strcmp(MacroToken.Identifier, TEXT("DEPRECATED")) != 0 && FCString::Strcmp(MacroToken.Identifier, TEXT("UE_DEPRECATED")) != 0)) - { - Parser.UngetToken(MacroToken); - return; - } - - auto ErrorMessageGetter = [&MacroToken]() { return FString::Printf(TEXT("%s macro"), MacroToken.Identifier); }; - - Parser.RequireSymbol(TEXT('('), ErrorMessageGetter); - - FToken Token; - if (Parser.GetToken(Token) && (Token.Type != CPT_Float || Token.TokenType != TOKEN_Const)) - { - FError::Throwf(TEXT("Expected engine version in %s macro"), MacroToken.Identifier); - } - - Parser.RequireSymbol(TEXT(','), ErrorMessageGetter); - if (Parser.GetToken(Token) && (Token.Type != CPT_String || Token.TokenType != TOKEN_Const)) - { - FError::Throwf(TEXT("Expected deprecation message in %s macro"), MacroToken.Identifier); - } - - Parser.RequireSymbol(TEXT(')'), ErrorMessageGetter); - } - - static const TCHAR* GLayoutMacroNames[] = { - TEXT("LAYOUT_ARRAY"), - TEXT("LAYOUT_ARRAY_EDITORONLY"), - TEXT("LAYOUT_BITFIELD"), - TEXT("LAYOUT_BITFIELD_EDITORONLY"), - TEXT("LAYOUT_FIELD"), - TEXT("LAYOUT_FIELD_EDITORONLY"), - TEXT("LAYOUT_FIELD_INITIALIZED"), - }; -} - -// Check to see if anything in the class hierarchy passed in has CLASS_DefaultToInstanced -bool FHeaderParser::DoesAnythingInHierarchyHaveDefaultToInstanced(UClass* TestClass) const -{ - 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. - if (const FClassDeclarationMetaData* ClassDeclarationDataPtr = ClassDeclarations.Find(Search->GetFName())) - { - bDefaultToInstanced = !!(ClassDeclarationDataPtr->ClassFlags & CLASS_DefaultToInstanced); - } - } - Search = Search->GetSuperClass(); - } - - return bDefaultToInstanced; -} - -FProperty* FHeaderParser::CreateVariableProperty(FPropertyBase& VarProperty, FFieldVariant Scope, const FName& Name, EObjectFlags ObjectFlags, EVariableCategory::Type VariableCategory, FUnrealSourceFile* UnrealSourceFile) const -{ - { - // UHTLite - FRWScopeLock Lock(EnumUnderlyingTypesLock, SLT_ReadOnly); - - // Check if it's an enum class property - if (const EUnderlyingEnumType* EnumPropType = EnumUnderlyingTypes.Find(VarProperty.Enum)) - { - FPropertyBase UnderlyingProperty = VarProperty; - UnderlyingProperty.Enum = nullptr; - switch (*EnumPropType) - { - case EUnderlyingEnumType::int8: UnderlyingProperty.Type = CPT_Int8; break; - case EUnderlyingEnumType::int16: UnderlyingProperty.Type = CPT_Int16; break; - case EUnderlyingEnumType::int32: UnderlyingProperty.Type = CPT_Int; break; - case EUnderlyingEnumType::int64: UnderlyingProperty.Type = CPT_Int64; break; - case EUnderlyingEnumType::uint8: UnderlyingProperty.Type = CPT_Byte; break; - case EUnderlyingEnumType::uint16: UnderlyingProperty.Type = CPT_UInt16; break; - case EUnderlyingEnumType::uint32: UnderlyingProperty.Type = CPT_UInt32; break; - case EUnderlyingEnumType::uint64: UnderlyingProperty.Type = CPT_UInt64; break; - case EUnderlyingEnumType::Unspecified: UnderlyingProperty.Type = CPT_Int; break; - - default: - check(false); - } - - if (*EnumPropType == EUnderlyingEnumType::Unspecified) - { - UnderlyingProperty.IntType = EIntType::Unsized; - } - - FEnumProperty* Result = new FEnumProperty(Scope, Name, ObjectFlags); - FNumericProperty* UnderlyingProp = CastFieldChecked(CreateVariableProperty(UnderlyingProperty, Result, TEXT("UnderlyingType"), ObjectFlags, VariableCategory, UnrealSourceFile)); - Result->UnderlyingProp = MoveTemp(UnderlyingProp); - Result->Enum = VarProperty.Enum; - - return Result; - } - } - - switch (VarProperty.Type) - { - case CPT_Byte: - { - FByteProperty* Result = new FByteProperty(Scope, Name, ObjectFlags); - Result->Enum = VarProperty.Enum; - check(VarProperty.IntType == EIntType::Sized); - return Result; - } - - case CPT_Int8: - { - FInt8Property* Result = new FInt8Property(Scope, Name, ObjectFlags); - check(VarProperty.IntType == EIntType::Sized); - return Result; - } - - case CPT_Int16: - { - FInt16Property* Result = new FInt16Property(Scope, Name, ObjectFlags); - check(VarProperty.IntType == EIntType::Sized); - return Result; - } - - case CPT_Int: - { - FIntProperty* Result = new FIntProperty(Scope, Name, ObjectFlags); - if (VarProperty.IntType == EIntType::Unsized) - { - UnsizedProperties.Add(Result); - } - return Result; - } - - case CPT_Int64: - { - FInt64Property* Result = new FInt64Property(Scope, Name, ObjectFlags); - check(VarProperty.IntType == EIntType::Sized); - return Result; - } - - case CPT_UInt16: - { - FUInt16Property* Result = new FUInt16Property(Scope, Name, ObjectFlags); - check(VarProperty.IntType == EIntType::Sized); - return Result; - } - - case CPT_UInt32: - { - FUInt32Property* Result = new FUInt32Property(Scope, Name, ObjectFlags); - if (VarProperty.IntType == EIntType::Unsized) - { - UnsizedProperties.Add(Result); - } - return Result; - } - - case CPT_UInt64: - { - FUInt64Property* Result = new FUInt64Property(Scope, Name, ObjectFlags); - check(VarProperty.IntType == EIntType::Sized); - return Result; - } - - case CPT_Bool: - { - FBoolProperty* Result = new FBoolProperty(Scope, Name, ObjectFlags); - Result->SetBoolSize(sizeof(bool), true); - return Result; - } - - case CPT_Bool8: - { - FBoolProperty* Result = new FBoolProperty(Scope, Name, ObjectFlags); - Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint8), VariableCategory == EVariableCategory::Return); - return Result; - } - - case CPT_Bool16: - { - FBoolProperty* Result = new FBoolProperty(Scope, Name, ObjectFlags); - Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint16), VariableCategory == EVariableCategory::Return); - return Result; - } - - case CPT_Bool32: - { - FBoolProperty* Result = new FBoolProperty(Scope, Name, ObjectFlags); - Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint32), VariableCategory == EVariableCategory::Return); - return Result; - } - - case CPT_Bool64: - { - FBoolProperty* Result = new FBoolProperty(Scope, Name, ObjectFlags); - Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint64), VariableCategory == EVariableCategory::Return); - return Result; - } - - case CPT_Float: - { - FFloatProperty* Result = new FFloatProperty(Scope, Name, ObjectFlags); - return Result; - } - - case CPT_Double: - { - FDoubleProperty* Result = new FDoubleProperty(Scope, Name, ObjectFlags); - return Result; - } - - case CPT_ObjectReference: - check(VarProperty.PropertyClass); - - if (VarProperty.PropertyClass->IsChildOf(UClass::StaticClass())) - { - FClassProperty* Result = new FClassProperty(Scope, Name, ObjectFlags); - Result->MetaClass = VarProperty.MetaClass; - Result->PropertyClass = VarProperty.PropertyClass; - return Result; - } - else - { - if (DoesAnythingInHierarchyHaveDefaultToInstanced(VarProperty.PropertyClass)) - { - VarProperty.PropertyFlags |= CPF_InstancedReference; - AddEditInlineMetaData(VarProperty.MetaData); - } - - FObjectProperty* Result = new FObjectProperty(Scope, Name, ObjectFlags); - Result->PropertyClass = VarProperty.PropertyClass; - return Result; - } - - case CPT_WeakObjectReference: - { - check(VarProperty.PropertyClass); - - FWeakObjectProperty* Result = new FWeakObjectProperty(Scope, Name, ObjectFlags); - Result->PropertyClass = VarProperty.PropertyClass; - return Result; - } - - case CPT_LazyObjectReference: - { - check(VarProperty.PropertyClass); - - FLazyObjectProperty* Result = new FLazyObjectProperty(Scope, Name, ObjectFlags); - Result->PropertyClass = VarProperty.PropertyClass; - return Result; - } - - case CPT_SoftObjectReference: - check(VarProperty.PropertyClass); - - if (VarProperty.PropertyClass->IsChildOf(UClass::StaticClass())) - { - FSoftClassProperty* Result = new FSoftClassProperty(Scope, Name, ObjectFlags); - Result->MetaClass = VarProperty.MetaClass; - Result->PropertyClass = VarProperty.PropertyClass; - return Result; - } - else - { - FSoftObjectProperty* Result = new FSoftObjectProperty(Scope, Name, ObjectFlags); - Result->PropertyClass = VarProperty.PropertyClass; - return Result; - } - - case CPT_Interface: - { - check(VarProperty.PropertyClass); - check(VarProperty.PropertyClass->HasAnyClassFlags(CLASS_Interface)); - - FInterfaceProperty* Result = new FInterfaceProperty(Scope, Name, ObjectFlags); - Result->InterfaceClass = VarProperty.PropertyClass; - return Result; - } - - case CPT_Name: - { - FNameProperty* Result = new FNameProperty(Scope, Name, ObjectFlags); - return Result; - } - - case CPT_String: - { - FStrProperty* Result = new FStrProperty(Scope, Name, ObjectFlags); - return Result; - } - - case CPT_Text: - { - FTextProperty* Result = new FTextProperty(Scope, Name, ObjectFlags); - return Result; - } - - case CPT_Struct: - { - if (VarProperty.Struct->StructFlags & STRUCT_HasInstancedReference) - { - VarProperty.PropertyFlags |= CPF_ContainsInstancedReference; - } - - FStructProperty* Result = new FStructProperty(Scope, Name, ObjectFlags); - Result->Struct = VarProperty.Struct; - return Result; - } - - case CPT_Delegate: - { - FDelegateProperty* Result = new FDelegateProperty(Scope, Name, ObjectFlags); - return Result; - } - - case CPT_MulticastDelegate: - { - FMulticastDelegateProperty* Result; - if (VarProperty.Function->IsA()) - { - Result = new FMulticastSparseDelegateProperty(Scope, Name, ObjectFlags); - } - else - { - Result = new FMulticastInlineDelegateProperty(Scope, Name, ObjectFlags); - } - return Result; - } - - case CPT_FieldPath: - { - FFieldPathProperty* Result = new FFieldPathProperty(Scope, Name, ObjectFlags); - Result->PropertyClass = VarProperty.PropertyPathClass; - - return Result; - } - - default: - FError::Throwf(TEXT("Unknown property type %i"), (uint8)VarProperty.Type); - } - - // Unreachable - check(false); //-V779 - return nullptr; -} - -///////////////////////////////////////////////////// -// FScriptLocation - -FHeaderParser* FScriptLocation::Compiler = NULL; - -FScriptLocation::FScriptLocation() -{ - if ( Compiler != NULL ) - { - Compiler->InitScriptLocation(*this); - } -} - -///////////////////////////////////////////////////// -// FHeaderParser - -FString FHeaderParser::GetContext() -{ - FFileScope* FileScope = GetCurrentFileScope(); - FUnrealSourceFile* SourceFile = FileScope ? FileScope->GetSourceFile() : GetCurrentSourceFile(); - FString ScopeFilename = SourceFile - ? IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*SourceFile->GetFilename()) - : TEXT("UNKNOWN"); - - return FString::Printf(TEXT("%s(%i)"), *ScopeFilename, InputLine); -} - -/*----------------------------------------------------------------------------- - Code emitting. ------------------------------------------------------------------------------*/ - - -// -// Get a qualified class. -// -FClass* FHeaderParser::GetQualifiedClass(const FClasses& AllClasses, const TCHAR* Thing) -{ - TCHAR ClassName[256]=TEXT(""); - - FToken Token; - if (GetIdentifier(Token)) - { - RedirectTypeIdentifier(Token); - - FCString::Strncat( ClassName, Token.Identifier, UE_ARRAY_COUNT(ClassName) ); - } - - if (!ClassName[0]) - { - FError::Throwf(TEXT("%s: Missing class name"), Thing ); - } - - return AllClasses.FindScriptClassOrThrow(ClassName); -} - -/*----------------------------------------------------------------------------- - 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); - if (InName != NAME_None) - { - for( ; Scope; Scope = Cast(Scope->GetOuter()) ) - { - for (TFieldIterator It(Scope); It; ++It) - { - if (It->GetFName() == InName) - { - if (Thing) - { - FError::Throwf(TEXT("%s: expecting @todo: FProp, got %s"), Thing, /**FieldClass->GetName(),*/ *It->GetClass()->GetName()); - } - return nullptr; - } - } - for( TFieldIterator 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 nullptr; - } - return *It; - } - } - - if (!bIncludeParents) - { - break; - } - } - } - - return NULL; -} - -FField* FHeaderParser::FindProperty(UStruct* Scope, const TCHAR* InIdentifier, bool bIncludeParents, FFieldClass* FieldClass, const TCHAR* Thing) -{ - check(InIdentifier); - FName InName(InIdentifier, FNAME_Find); - if (InName != NAME_None) - { - for (; Scope; Scope = Cast(Scope->GetOuter())) - { - for (TFieldIterator It(Scope); It; ++It) - { - if (It->GetFName() == InName) - { - if (Thing) - { - FError::Throwf(TEXT("%s: expecting a property, got %s"), Thing, *It->GetClass()->GetName()); - } - return nullptr; - } - } - for (TFieldIterator It(Scope); It; ++It) - { - if (It->GetFName() == InName) - { - if (!It->IsA(FieldClass)) - { - if (Thing) - { - FError::Throwf(TEXT("%s: expecting %s: FProp, got %s"), Thing, *FieldClass->GetName(), *It->GetClass()->GetName()); - } - return nullptr; - } - return *It; - } - } - - if (!bIncludeParents) - { - break; - } - } - } - - return NULL; -} - -/** - * @return true if Scope has FProperty objects in its list of fields - */ -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -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 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 - */ -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -UStruct* FHeaderParser::GetSuperScope( UStruct* CurrentScope, const FName& SearchName ) -{ - UStruct* SuperScope = CurrentScope; - while (SuperScope && !SuperScope->GetInheritanceSuper()) - { - SuperScope = CastChecked(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; -} -*/ - -/** - * 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 FHeaderParser::AddIncludePathToMetadata(UField* Type, TMap &MetaData) const -{ - // Add metadata for the include path. - const TSharedRef* TypeDefinitionPtr = TypeDefinitionInfoMap.Find(Type); - if (TypeDefinitionPtr != nullptr) - { - MetaData.Add(NAME_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 FHeaderParser::AddModuleRelativePathToMetadata(FUnrealSourceFile& SourceFile, TMap &MetaData) const -{ - MetaData.Add(NAME_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 FHeaderParser::AddModuleRelativePathToMetadata(UField* Type, TMap &MetaData) const -{ - // Add metadata for the module relative path. - const TSharedRef* TypeDefinitionPtr = TypeDefinitionInfoMap.Find(Type); - if (TypeDefinitionPtr != nullptr) - { - MetaData.Add(NAME_ModuleRelativePath, (*TypeDefinitionPtr)->GetUnrealSourceFile().GetModuleRelativePath()); - } -} - -/*----------------------------------------------------------------------------- - Variables. ------------------------------------------------------------------------------*/ - -// -// Compile an enumeration definition. -// -UEnum* FHeaderParser::CompileEnum() -{ - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - TSharedPtr Scope = CurrentSrcFile->GetScope(); - - CheckAllow( TEXT("'Enum'"), ENestAllowFlags::TypeDecl ); - - // Get the enum specifier list - FToken EnumToken; - TArray SpecifiersFound; - ReadSpecifierSetInsideMacro(SpecifiersFound, TEXT("Enum"), EnumToken.MetaData); - - // We don't handle any non-metadata enum specifiers at the moment - if (SpecifiersFound.Num() != 0) - { - FError::Throwf(TEXT("Unknown enum specifier '%s'"), *SpecifiersFound[0].Key); - } - - FScriptLocation DeclarationPosition; - - // Check enum type. This can be global 'enum', 'namespace' or 'enum class' enums. - bool bReadEnumName = false; - UEnum::ECppForm CppForm = UEnum::ECppForm::Regular; - if (!GetIdentifier(EnumToken)) - { - 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)) - { - SkipAlignasIfNecessary(*this); - - if (!GetIdentifier(EnumToken)) - { - FError::Throwf(TEXT("Missing identifier after enum") ); - } - - if (EnumToken.Matches(TEXT("class"), ESearchCase::CaseSensitive) || EnumToken.Matches(TEXT("struct"), ESearchCase::CaseSensitive)) - { - // You can't actually have an alignas() before the class/struct keyword, but this - // makes the parsing easier and illegal syntax will be caught by the compiler anyway. - SkipAlignasIfNecessary(*this); - - CppForm = UEnum::ECppForm::EnumClass; - bReadEnumName = GetIdentifier(EnumToken); - } - else - { - CppForm = UEnum::ECppForm::Regular; - bReadEnumName = true; - } - } - else - { - FError::Throwf(TEXT("UENUM() should be followed by \'enum\' or \'namespace\' keywords.") ); - } - - // Get enumeration name. - if (!bReadEnumName) - { - FError::Throwf(TEXT("Missing enumeration name") ); - } - - // Verify that the enumeration definition is unique within this scope. - UField* Existing = Scope->FindTypeByName(EnumToken.Identifier); - if (Existing) - { - FError::Throwf(TEXT("enum: '%s' already defined here"), *EnumToken.GetTokenName().ToString()); - } - - // Check if the enum name is using a reserved keyword - if (FHeaderParser::IsReservedTypeName(EnumToken)) - { - FError::Throwf(TEXT("enum: '%s' uses a reserved type name."), *EnumToken.GetTokenName().ToString()); - } - - ParseFieldMetaData(EnumToken.MetaData, EnumToken.Identifier); - // Create enum definition. - UEnum* Enum = new(EC_InternalUseOnlyConstructor, CurrentSrcFile->GetPackage(), EnumToken.Identifier, RF_Public) UEnum(FObjectInitializer()); - Scope->AddType(Enum); - - if (CompilerDirectiveStack.Num() > 0 && (CompilerDirectiveStack.Last() & ECompilerDirective::WithEditorOnlyData) != 0) - { - EditorOnlyDataTypes.Add(Enum); - } - - TypeDefinitionInfoMap.Add(Enum, MakeShared(*CurrentSrcFile, InputLine)); - - // Validate the metadata for the enum - ValidateMetaDataFormat(Enum, EnumToken.MetaData); - - // Read base for enum class - EUnderlyingEnumType UnderlyingType = EUnderlyingEnumType::uint8; - if (CppForm == UEnum::ECppForm::EnumClass) - { - if (MatchSymbol(TEXT(':'))) - { - FToken BaseToken; - if (!GetIdentifier(BaseToken)) - { - FError::Throwf(TEXT("Missing enum base") ); - } - - if (!FCString::Strcmp(BaseToken.Identifier, TEXT("uint8"))) - { - UnderlyingType = EUnderlyingEnumType::uint8; - } - else if (!FCString::Strcmp(BaseToken.Identifier, TEXT("uint16"))) - { - UnderlyingType = EUnderlyingEnumType::uint16; - } - else if (!FCString::Strcmp(BaseToken.Identifier, TEXT("uint32"))) - { - UnderlyingType = EUnderlyingEnumType::uint32; - } - else if (!FCString::Strcmp(BaseToken.Identifier, TEXT("uint64"))) - { - UnderlyingType = EUnderlyingEnumType::uint64; - } - else if (!FCString::Strcmp(BaseToken.Identifier, TEXT("int8"))) - { - UnderlyingType = EUnderlyingEnumType::int8; - } - else if (!FCString::Strcmp(BaseToken.Identifier, TEXT("int16"))) - { - UnderlyingType = EUnderlyingEnumType::int16; - } - else if (!FCString::Strcmp(BaseToken.Identifier, TEXT("int32"))) - { - UnderlyingType = EUnderlyingEnumType::int32; - } - else if (!FCString::Strcmp(BaseToken.Identifier, TEXT("int64"))) - { - UnderlyingType = EUnderlyingEnumType::int64; - } - else - { - FError::Throwf(TEXT("Unsupported enum class base type: %s"), BaseToken.Identifier); - } - } - else - { - UnderlyingType = EUnderlyingEnumType::Unspecified; - } - - { - // UHTLite - FRWScopeLock Lock(EnumUnderlyingTypesLock, SLT_Write); - - EnumUnderlyingTypes.Add(Enum, UnderlyingType); - } - } - - if (UnderlyingType != EUnderlyingEnumType::uint8 && EnumToken.MetaData.Contains(NAME_BlueprintType)) - { - FError::Throwf(TEXT("Invalid BlueprintType enum base - currently only uint8 supported")); - } - - // Get opening brace. - RequireSymbol( TEXT('{'), TEXT("'Enum'") ); - - switch (CppForm) - { - case UEnum::ECppForm::Namespaced: - { - // Now handle the inner true enum portion - RequireIdentifier(TEXT("enum"), ESearchCase::CaseSensitive, TEXT("'Enum'")); - - SkipAlignasIfNecessary(*this); - - 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; - } - - // List of all metadata generated for this enum - TMap EnumValueMetaData = EnumToken.MetaData; - - AddModuleRelativePathToMetadata(Enum, EnumValueMetaData); - AddFormattedPrevCommentAsTooltipMetaData(EnumValueMetaData); - - // Parse all enums tags. - FToken TagToken; - TArray> EntryMetaData; - - TArray> EnumNames; - int64 CurrentEnumValue = 0; - while (GetIdentifier(TagToken)) - { - AddFormattedPrevCommentAsTooltipMetaData(TagToken.MetaData); - - // Try to read an optional explicit enum value specification - if (MatchSymbol(TEXT('='))) - { - FToken InitToken; - if (!GetToken(InitToken)) - { - FError::Throwf(TEXT("UENUM: missing enumerator initializer")); - } - - int64 NewEnumValue = -1; - if (!InitToken.GetConstInt64(NewEnumValue)) - { - // We didn't parse a literal, so set an invalid value - NewEnumValue = -1; - } - - // Skip tokens until we encounter a comma, a closing brace or a UMETA declaration - for (;;) - { - if (!GetToken(InitToken)) - { - FError::Throwf(TEXT("Enumerator: end of file encountered while parsing the initializer")); - } - - if (InitToken.TokenType == TOKEN_Symbol) - { - if (InitToken.Matches(TEXT(',')) || InitToken.Matches(TEXT('}'))) - { - UngetToken(InitToken); - break; - } - } - else if (InitToken.TokenType == TOKEN_Identifier) - { - if (FCString::Stricmp(InitToken.Identifier, TEXT("UMETA")) == 0) - { - UngetToken(InitToken); - break; - } - } - - // There are tokens after the initializer so it's not a standalone literal, - // so set it to an invalid value. - NewEnumValue = -1; - } - - CurrentEnumValue = NewEnumValue; - } - - FName NewTag; - switch (CppForm) - { - case UEnum::ECppForm::Namespaced: - case UEnum::ECppForm::EnumClass: - { - NewTag = FName(*FString::Printf(TEXT("%s::%s"), EnumToken.Identifier, TagToken.Identifier), FNAME_Add); - } - break; - - case UEnum::ECppForm::Regular: - { - NewTag = FName(TagToken.Identifier, FNAME_Add); - } - break; - } - - // Save the new tag - EnumNames.Emplace(NewTag, CurrentEnumValue); - - // Autoincrement the current enumeration value - if (CurrentEnumValue != -1) - { - ++CurrentEnumValue; - } - - TagToken.MetaData.Add(NAME_Name, NewTag.ToString()); - EntryMetaData.Add(TagToken.MetaData); - - // 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.Emplace(*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(','))) - { - FToken ClosingBrace; - if (!GetToken(ClosingBrace)) - { - FError::Throwf(TEXT("UENUM: end of file encountered")); - } - - if (ClosingBrace.TokenType == TOKEN_Symbol && ClosingBrace.Matches(TEXT('}'))) - { - UngetToken(ClosingBrace); - break; - } - } - } - - // Trailing brace and semicolon for the enum - RequireSymbol( TEXT('}'), TEXT("'Enum'") ); - MatchSemi(); - - if (CppForm == UEnum::ECppForm::Namespaced) - { - // Trailing brace for the namespace. - RequireSymbol( TEXT('}'), TEXT("'Enum'") ); - } - - // Register the list of enum names. - if (!Enum->SetEnums(EnumNames, CppForm, EEnumFlags::None, false)) - { - const FName MaxEnumItem = *(Enum->GenerateEnumPrefix() + TEXT("_MAX")); - const int32 MaxEnumItemIndex = Enum->GetIndexByName(MaxEnumItem); - if (MaxEnumItemIndex != INDEX_NONE) - { - FError::Throwf(TEXT("Illegal enumeration tag specified. Conflicts with auto-generated tag '%s'"), *MaxEnumItem.ToString()); - } - - FError::Throwf(TEXT("Unable to generate enum MAX entry '%s' due to name collision"), *MaxEnumItem.ToString()); - } - - CheckDocumentationPolicyForEnum(Enum, EnumValueMetaData, EntryMetaData); - - // Add the metadata gathered for the enum to the package - if (EnumValueMetaData.Num() > 0) - { - UMetaData* PackageMetaData = Enum->GetOutermost()->GetMetaData(); - checkSlow(PackageMetaData); - - PackageMetaData->SetObjectValues(Enum, MoveTemp(EnumValueMetaData)); - } - - if (!Enum->IsValidEnumValue(0) && EnumToken.MetaData.Contains(NAME_BlueprintType)) - { - UE_LOG_WARNING_UHT(TEXT("'%s' does not have a 0 entry! (This is a problem when the enum is initalized by default)"), *Enum->GetName()); - } - - 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; -} - -/** - * @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(); - } - - FString Result(Input); - - // Sweep out comments marked to be ignored. - { - int32 CommentStart, CommentEnd; - // Block comments go first - for (CommentStart = Result.Find(TEXT("/*~"), ESearchCase::CaseSensitive); CommentStart != INDEX_NONE; CommentStart = Result.Find(TEXT("/*~"), ESearchCase::CaseSensitive)) - { - CommentEnd = Result.Find(TEXT("*/"), ESearchCase::CaseSensitive, ESearchDir::FromStart, CommentStart); - if (CommentEnd != INDEX_NONE) - { - Result.RemoveAt(CommentStart, (CommentEnd + 2) - CommentStart, false); - } - else - { - // This looks like an error - an unclosed block comment. - break; - } - } - // Leftover line comments go next - for (CommentStart = Result.Find(TEXT("//~"), ESearchCase::CaseSensitive); CommentStart != INDEX_NONE; CommentStart = Result.Find(TEXT("//~"), ESearchCase::CaseSensitive)) - { - CommentEnd = Result.Find(TEXT("\n"), ESearchCase::CaseSensitive, ESearchDir::FromStart, CommentStart); - if (CommentEnd != INDEX_NONE) - { - Result.RemoveAt(CommentStart, (CommentEnd + 1) - CommentStart, false); - } - else - { - Result.RemoveAt(CommentStart, Result.Len() - CommentStart, false); - break; - } - } - // Finish by shrinking if anything was removed, since we deferred this during the search. - Result.Shrink(); - } - - // Check for known commenting styles. - const bool bJavaDocStyle = Result.Contains(TEXT("/**"), ESearchCase::CaseSensitive); - const bool bCStyle = Result.Contains(TEXT("/*"), ESearchCase::CaseSensitive); - const bool bCPPStyle = Result.StartsWith(TEXT("//"), ESearchCase::CaseSensitive); - - if ( bJavaDocStyle || bCStyle) - { - // Remove beginning and end markers. - if (bJavaDocStyle) - { - Result.ReplaceInline(TEXT("/**"), TEXT(""), ESearchCase::CaseSensitive); - } - if (bCStyle) - { - Result.ReplaceInline(TEXT("/*"), TEXT(""), ESearchCase::CaseSensitive); - } - Result.ReplaceInline(TEXT("*/"), TEXT(""), ESearchCase::CaseSensitive); - } - - if ( bCPPStyle ) - { - // Remove c++-style comment markers. Also handle javadoc-style comments - Result.ReplaceInline(TEXT("///"), TEXT(""), ESearchCase::CaseSensitive); - Result.ReplaceInline(TEXT("//"), TEXT(""), ESearchCase::CaseSensitive); - - // Parser strips cpptext and replaces it with "// (cpptext)" -- prevent - // this from being treated as a comment on variables declared below the - // cpptext section - Result.ReplaceInline(TEXT("(cpptext)"), TEXT("")); - } - - // Get rid of carriage return or tab characters, which mess up tooltips. - Result.ReplaceInline(TEXT( "\r" ), TEXT( "" ), ESearchCase::CaseSensitive); - - //wx widgets has a hard coded tab size of 8 - { - const int32 SpacesPerTab = 8; - Result.ConvertTabsToSpacesInline(SpacesPerTab); - } - - // get rid of uniform leading whitespace and all trailing whitespace, on each line - TArray Lines; - Result.ParseIntoArray(Lines, TEXT("\n"), false); - - for (FString& Line : Lines) - { - // Remove trailing whitespace - Line.TrimEndInline(); - - // 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.RightChopInline(Pos + 1, false); - } - } - } - - auto IsWhitespaceOrLineSeparator = [](const FString& Line) - { - int32 LineLength = Line.Len(); - int32 WhitespaceCount = 0; - while (WhitespaceCount < LineLength && FChar::IsWhitespace(Line[WhitespaceCount])) - { - ++WhitespaceCount; - } - - if (WhitespaceCount == LineLength) - { - return true; - } - - const TCHAR* Str = (*Line) + WhitespaceCount; - return IsAllSameChar(Str, TEXT('-')) || IsAllSameChar(Str, TEXT('=')) || IsAllSameChar(Str, TEXT('*')); - }; - - // Find first meaningful line - int32 FirstIndex = 0; - for (const FString& Line : Lines) - { - if (!IsWhitespaceOrLineSeparator(Line)) - { - break; - } - - ++FirstIndex; - } - - int32 LastIndex = Lines.Num(); - while (LastIndex != FirstIndex) - { - const FString& Line = Lines[LastIndex - 1]; - - if (!IsWhitespaceOrLineSeparator(Line)) - { - break; - } - - --LastIndex; - } - - Result.Reset(); - - if (FirstIndex != LastIndex) - { - FString& 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.RightChopInline(Pos, false); - } - - 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.RightChopInline(1, false); - } - - // Make sure it doesn't end with a dead newline - if (!Result.IsEmpty() && FChar::IsLinebreak(Result[Result.Len() - 1])) - { - Result.LeftInline(Result.Len() - 1, false); - } - - // Done. - return Result; -} - -TMap FHeaderParser::GetParameterToolTipsFromFunctionComment(const FString& Input) -{ - SCOPE_SECONDS_COUNTER_UHT(DocumentationPolicy); - - TMap Map; - if (Input.IsEmpty()) - { - return Map; - } - - TArray Params; - static const TCHAR ParamTag[] = TEXT("@param"); - static const TCHAR ReturnTag[] = TEXT("@return"); - static const TCHAR ReturnParamPrefix[] = TEXT("ReturnValue "); - - /** - * Search for @param / @return followed by a section until a line break. - * For example: "@param Test MyTest Variable" becomes "Test", "MyTest Variable" - * These pairs are then later split and stored as the parameter tooltips. - * Once we don't find either @param or @return we break from the loop. - */ - int32 Offset = 0; - while (Offset < Input.Len()) - { - const TCHAR* ParamPrefix = TEXT(""); - int32 ParamStart = Input.Find(ParamTag, ESearchCase::CaseSensitive, ESearchDir::FromStart, Offset); - if(ParamStart != INDEX_NONE) - { - ParamStart = ParamStart + UE_ARRAY_COUNT(ParamTag); - Offset = ParamStart; - } - else - { - ParamStart = Input.Find(ReturnTag, ESearchCase::CaseSensitive, ESearchDir::FromStart, Offset); - if (ParamStart != INDEX_NONE) - { - ParamStart = ParamStart + UE_ARRAY_COUNT(ReturnTag); - Offset = ParamStart; - ParamPrefix = ReturnParamPrefix; - } - else - { - // no @param, no @return? - break; - } - } - - int32 ParamEnd = Input.Find(TEXT("\n"), ESearchCase::CaseSensitive, ESearchDir::FromStart, ParamStart); - if (ParamEnd == INDEX_NONE) - { - ParamEnd = Input.Len(); - } - Offset = ParamEnd; - - Params.Add(ParamPrefix + Input.Mid(ParamStart, ParamEnd - ParamStart - 1)); - } - - for (FString& Param : Params) - { - Param.ConvertTabsToSpacesInline(4); - Param.TrimStartAndEndInline(); - - int32 FirstSpaceIndex = -1; - if (!Param.FindChar(' ', FirstSpaceIndex)) - { - continue; - } - - FString ParamToolTip = Param.Mid(FirstSpaceIndex + 1); - ParamToolTip.TrimStartInline(); - - Param.LeftInline(FirstSpaceIndex); - - Map.Add(*Param, MoveTemp(ParamToolTip)); - } - - return Map; -} - - -void FHeaderParser::AddFormattedPrevCommentAsTooltipMetaData(TMap& MetaData) -{ - // Don't add a tooltip if one already exists. - if (MetaData.Find(NAME_ToolTip)) - { - return; - } - - // Add the comment if it is not empty - if (!PrevComment.IsEmpty()) - { - MetaData.Add(NAME_Comment, *PrevComment); - } - - // 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(const FToken& Token) -{ - EAccessSpecifier ResultAccessSpecifier = ACCESS_NotAnAccessSpecifier; - - for (EAccessSpecifier Test = EAccessSpecifier(ACCESS_NotAnAccessSpecifier + 1); Test != ACCESS_Num; Test = EAccessSpecifier(Test + 1)) - { - if (Token.Matches(GetAccessSpecifierName(Test), ESearchCase::CaseSensitive)) - { - auto ErrorMessageGetter = [&Token]() { return FString::Printf(TEXT("after %s"), Token.Identifier); }; - - // Consume the colon after the specifier - RequireSymbol(TEXT(':'), ErrorMessageGetter); - return Test; - } - } - return ACCESS_NotAnAccessSpecifier; -} - - -/** - * Compile a struct definition. - */ -UScriptStruct* FHeaderParser::CompileStructDeclaration(FClasses& AllClasses) -{ - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - TSharedPtr Scope = CurrentSrcFile->GetScope(); - - // Make sure structs can be declared here. - CheckAllow( TEXT("'struct'"), ENestAllowFlags::TypeDecl ); - - FScriptLocation StructDeclaration; - - bool IsNative = false; - bool IsExport = false; - bool IsTransient = false; - uint32 StructFlags = STRUCT_Native; - TMap MetaData; - - // Get the struct specifier list - TArray SpecifiersFound; - ReadSpecifierSetInsideMacro(SpecifiersFound, TEXT("Struct"), MetaData); - - // Consume the struct keyword - RequireIdentifier(TEXT("struct"), ESearchCase::CaseSensitive, 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; - - // alignas() can come before or after the deprecation macro. - // We can't have both, but the compiler will catch that anyway. - SkipAlignasIfNecessary(*this); - SkipDeprecatedMacroIfNecessary(*this); - SkipAlignasIfNecessary(*this); - - // 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; - } - - StructNameStripped = GetClassNameWithPrefixRemoved(StructNameInScript); - - // Effective struct name - const FString EffectiveStructName = *StructNameStripped; - - // Verify that this struct name is not set to a reserved name - if (FHeaderParser::IsReservedTypeName(EffectiveStructName)) - { - FError::Throwf(TEXT("Struct '%s' uses a reserved type name ('%s')."), *StructNameInScript, *EffectiveStructName); - } - - // Process the list of specifiers - for (const FPropertySpecifier& Specifier : SpecifiersFound) - { - switch ((EStructSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GStructSpecifierStrings)) - { - default: - { - FError::Throwf(TEXT("Unknown struct specifier '%s'"), *Specifier.Key); - } - break; - - case EStructSpecifier::NoExport: - { - //UE_LOG_WARNING_UHT(TEXT("Struct named %s in %s is still marked noexport"), *EffectiveStructName, *(Class->GetName()));//@TODO: UCREMOVAL: Debug printing - StructFlags &= ~STRUCT_Native; - StructFlags |= STRUCT_NoExport; - } - break; - - case EStructSpecifier::Atomic: - { - StructFlags |= STRUCT_Atomic; - } - break; - - case EStructSpecifier::Immutable: - { - StructFlags |= STRUCT_Immutable | STRUCT_Atomic; - - if (!FPaths::IsSamePath(Filename, TypeDefinitionInfoMap[UObject::StaticClass()]->GetUnrealSourceFile().GetFilename())) - { - UE_LOG_ERROR_UHT(TEXT("Immutable is being phased out in favor of SerializeNative, and is only legal on the mirror structs declared in UObject")); - } - } - break; - } - } - - // Verify uniqueness (if declared within UClass). - { - UField* Existing = Scope->FindTypeByName(*EffectiveStructName); - if (Existing) - { - FError::Throwf(TEXT("struct: '%s' already defined here"), *EffectiveStructName); - } - - if (UStruct* FoundType = FindObject(ANY_PACKAGE, *EffectiveStructName)) - { - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - if (TTuple, int32>* FoundTypeInfo = GStructToSourceLine.Find(FoundType)) - { - FError::Throwf( - TEXT("struct: '%s' conflicts with another type of the same name defined at %s(%d)"), - *EffectiveStructName, - *FoundTypeInfo->Get<0>()->GetFilename(), - FoundTypeInfo->Get<1>() - ); - } - else - */ - { - FError::Throwf(TEXT("struct: '%s' conflicts with another type of the same name"), *EffectiveStructName); - } - - } - } - - // Get optional superstruct. - bool bExtendsBaseStruct = false; - - if (MatchSymbol(TEXT(':'))) - { - RequireIdentifier(TEXT("public"), ESearchCase::CaseSensitive, TEXT("struct inheritance")); - bExtendsBaseStruct = true; - } - - UScriptStruct* BaseStruct = NULL; - if (bExtendsBaseStruct) - { - FToken ParentScope, ParentName; - if (GetIdentifier( ParentScope )) - { - RedirectTypeIdentifier(ParentScope); - - TSharedPtr StructScope = Scope; - FString ParentStructNameInScript = FString(ParentScope.Identifier); - if (MatchSymbol(TEXT('.'))) - { - if (GetIdentifier(ParentName)) - { - RedirectTypeIdentifier(ParentName); - - ParentStructNameInScript = FString(ParentName.Identifier); - FString ParentNameStripped = GetClassNameWithPrefixRemoved(ParentScope.Identifier); - FClass* StructClass = AllClasses.FindClass(*ParentNameStripped); - if( !StructClass ) - { - // If we find the literal class name, the user didn't use a prefix - StructClass = AllClasses.FindClass(ParentScope.Identifier); - 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 ); - } - } - - StructScope = FScope::GetTypeScope(StructClass); - } - else - { - FError::Throwf( TEXT("'struct': Missing parent struct type after '%s.'"), ParentScope.Identifier ); - } - } - - FString ParentStructNameStripped; - const UField* Type = nullptr; - 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) - { - Type = StructScope->FindTypeByName(*ParentStructNameStripped); - } - - // If it wasn't found, try to find the literal name given - if (Type == nullptr) - { - Type = StructScope->FindTypeByName(*ParentStructNameInScript); - } - - // Resolve structs declared in another class //@TODO: UCREMOVAL: This seems extreme - if (Type == nullptr) - { - if (bOverrideParentStructName) - { - Type = FindObject(ANY_PACKAGE, *ParentStructNameStripped); - } - - if (Type == nullptr) - { - Type = FindObject(ANY_PACKAGE, *ParentStructNameInScript); - } - } - - // If the struct still wasn't found, throw an error - if (Type == NULL) - { - 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. - BaseStruct = ((UScriptStruct*)Type); - if( bOverrideParentStructName ) - { - const TCHAR* PrefixCPP = StructsWithTPrefix.Contains(ParentStructNameStripped) ? TEXT("T") : BaseStruct->GetPrefixCPP(); - if( ParentStructNameInScript != FString::Printf(TEXT("%s%s"), PrefixCPP, *ParentStructNameStripped) ) - { - BaseStruct = nullptr; - FError::Throwf(TEXT("Parent Struct '%s' is missing a valid Unreal prefix, expecting '%s'"), *ParentStructNameInScript, *FString::Printf(TEXT("%s%s"), PrefixCPP, *Type->GetName())); - } - } - } - } - 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); - } - // Create. - UScriptStruct* Struct = new(EC_InternalUseOnlyConstructor, CurrentSrcFile->GetPackage(), *EffectiveStructName, RF_Public) UScriptStruct(FObjectInitializer(), BaseStruct); - - Scope->AddType(Struct); - TypeDefinitionInfoMap.Add(Struct, MakeShared(*CurrentSrcFile, InputLine)); - FScope::AddTypeScope(Struct, &CurrentSrcFile->GetScope().Get()); - - AddModuleRelativePathToMetadata(Struct, MetaData); - - // 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. - FString DeclaredPrefix = GetClassPrefix( StructNameInScript ); - if( DeclaredPrefix == Struct->GetPrefixCPP() || DeclaredPrefix == TEXT("T") ) - { - // 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) - { - FError::Throwf(TEXT("Struct '%s' has an invalid Unreal prefix, expecting '%s'"), *StructNameInScript, *ExpectedStructName); - } - } - 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); - } - - Struct->StructFlags = EStructFlags(Struct->StructFlags | StructFlags); - - AddFormattedPrevCommentAsTooltipMetaData(MetaData); - - // Register the metadata - AddMetaDataToClassData(Struct, MoveTemp(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 HoldFromClass(CurrentAccessSpecifier, ACCESS_Public); - - { - FToken StructToken; - StructToken.Struct = Struct; - - // add this struct to the compiler's persistent tracking system - FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(StructToken.Struct, CurrentSrcFile); - } - - 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)) - { - CompileVariableDeclaration(AllClasses, Struct); - } - else if (Token.Matches(TEXT("UFUNCTION"), ESearchCase::CaseSensitive)) - { - FError::Throwf(TEXT("USTRUCTs cannot contain UFUNCTIONs.")); - } - else if (Token.Matches(TEXT("RIGVM_METHOD"), ESearchCase::CaseSensitive)) - { - CompileRigVMMethodDeclaration(AllClasses, Struct); - } - else if (Token.Matches(TEXT("GENERATED_USTRUCT_BODY"), ESearchCase::CaseSensitive) || Token.Matches(TEXT("GENERATED_BODY"), ESearchCase::CaseSensitive)) - { - // Match 'GENERATED_USTRUCT_BODY' '(' [StructName] ')' or 'GENERATED_BODY' '(' [StructName] ')' - if (CurrentAccessSpecifier != ACCESS_Public) - { - FError::Throwf(TEXT("%s must be in the public scope of '%s', not private or protected."), Token.Identifier, *StructNameInScript); - } - - if (Struct->StructMacroDeclaredLineNumber != INDEX_NONE) - { - FError::Throwf(TEXT("Multiple %s declarations found in '%s'"), Token.Identifier, *StructNameInScript); - } - - Struct->StructMacroDeclaredLineNumber = InputLine; - RequireSymbol(TEXT('('), TEXT("'struct'")); - - CompileVersionDeclaration(Struct); - - RequireSymbol(TEXT(')'), TEXT("'struct'")); - - // Eat a semicolon if present (not required) - SafeMatchSymbol(TEXT(';')); - } - else if ( Token.Matches(TEXT('#')) && MatchIdentifier(TEXT("ifdef"), ESearchCase::CaseSensitive) ) - { - PushCompilerDirective(ECompilerDirective::Insignificant); - } - else if ( Token.Matches(TEXT('#')) && MatchIdentifier(TEXT("ifndef"), ESearchCase::CaseSensitive) ) - { - PushCompilerDirective(ECompilerDirective::Insignificant); - } - else if (Token.Matches(TEXT('#')) && MatchIdentifier(TEXT("endif"), ESearchCase::CaseSensitive)) - { - 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"), ESearchCase::CaseSensitive) ) - { - //@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"), ESearchCase::CaseSensitive) ) - { - if (bInvertConditional) - { - FError::Throwf(TEXT("Cannot use !WITH_EDITORONLY_DATA")); - } - - PushCompilerDirective(ECompilerDirective::WithEditorOnlyData); - } - else if (MatchIdentifier(TEXT("WITH_EDITOR"), ESearchCase::CaseSensitive) ) - { - if (bInvertConditional) - { - FError::Throwf(TEXT("Cannot use !WITH_EDITOR")); - } - PushCompilerDirective(ECompilerDirective::WithEditor); - } - else if (MatchIdentifier(TEXT("CPP"), ESearchCase::CaseSensitive) || MatchConstInt(TEXT("0")) || MatchConstInt(TEXT("1")) || MatchIdentifier(TEXT("WITH_HOT_RELOAD"), ESearchCase::CaseSensitive) || MatchIdentifier(TEXT("WITH_HOT_RELOAD_CTORS"), ESearchCase::CaseSensitive)) - { - bConsumeAsCppText = !bInvertConditional; - PushCompilerDirective(ECompilerDirective::Insignificant); - } - 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()); - } - else if ( ch=='{' || (ch=='#' && (PeekIdentifier(TEXT("if"), ESearchCase::CaseSensitive) || PeekIdentifier(TEXT("ifdef"), ESearchCase::CaseSensitive))) ) - { - nest++; - } - else if ( ch=='}' || (ch=='#' && PeekIdentifier(TEXT("endif"), ESearchCase::CaseSensitive)) ) - { - nest--; - } - - if (nest==0) - { - RequireIdentifier(TEXT("endif"), ESearchCase::CaseSensitive, TEXT("'if'")); - } - } - } - } - else if (Token.Matches(TEXT('#')) && MatchIdentifier(TEXT("pragma"), ESearchCase::CaseSensitive)) - { - // skip it and skip over the text, it is not recorded or processed - TCHAR c; - while (!IsEOL(c = GetChar())) - { - } - } - else if (ProbablyAnUnknownObjectLikeMacro(*this, Token)) - { - // skip it - } - else - { - if (!Token.Matches( TEXT('}'))) - { - // Skip declaration will destroy data in Token, so cache off the identifier in case we need to provfide an error - TCHAR FirstTokenIdentifier[NAME_SIZE]; - FCString::Strncpy(FirstTokenIdentifier, Token.Identifier, NAME_SIZE); - - if (!SkipDeclaration(Token)) - { - FError::Throwf(TEXT("'struct': Unexpected '%s'"), FirstTokenIdentifier); - } - } - 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; - FError::Throwf(TEXT("Expected a GENERATED_BODY() at the start of struct")); - } - - // Validate sparse class data - CheckSparseClassData(Struct); - - // 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 ENestType::GlobalScope: - return TEXT("Global Scope"); - case ENestType::Class: - return TEXT("Class"); - case ENestType::NativeInterface: - case ENestType::Interface: - return TEXT("Interface"); - case ENestType::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(ENestAllowFlags AllowFlags) -{ - return (TopNest->Allow & AllowFlags) != ENestAllowFlags::None; -} - -// -// 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, ENestAllowFlags AllowFlags ) -{ - if (!IsAllowedInThisNesting(AllowFlags)) - { - if (TopNest->NestType == ENestType::GlobalScope) - { - FError::Throwf(TEXT("%s is not allowed before the Class definition"), Thing ); - } - else - { - FError::Throwf(TEXT("%s is not allowed here"), Thing ); - } - } -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -bool FHeaderParser::AllowReferenceToClass(UStruct* Scope, UClass* CheckClass) const -{ - check(CheckClass); - - return (Scope->GetOutermost() == CheckClass->GetOutermost()) - || ((CheckClass->ClassFlags&CLASS_Parsed) != 0) - || ((CheckClass->ClassFlags&CLASS_Intrinsic) != 0); -} -*/ - -/*----------------------------------------------------------------------------- - Nest management. ------------------------------------------------------------------------------*/ - -void FHeaderParser::PushNest(ENestType NestType, UStruct* InNode, FUnrealSourceFile* SourceFile) -{ - // Update pointer to top nesting level. - TopNest = &Nest[NestLevel++]; - TopNest->SetScope(NestType == ENestType::GlobalScope ? &SourceFile->GetScope().Get() : &FScope::GetTypeScope(InNode).Get()); - TopNest->NestType = NestType; - - // Prevent overnesting. - if (NestLevel >= MAX_NEST_LEVELS) - { - FError::Throwf(TEXT("Maximum nesting limit exceeded")); - } - - // Inherit info from stack node above us. - if (NestLevel > 1 && NestType == ENestType::GlobalScope) - { - // Use the existing stack node. - TopNest->SetScope(TopNest[-1].GetScope()); - } - - // NestType specific logic. - switch (NestType) - { - case ENestType::GlobalScope: - TopNest->Allow = ENestAllowFlags::Class | ENestAllowFlags::TypeDecl | ENestAllowFlags::ImplicitDelegateDecl; - break; - - case ENestType::Class: - TopNest->Allow = ENestAllowFlags::VarDecl | ENestAllowFlags::Function | ENestAllowFlags::ImplicitDelegateDecl; - break; - - case ENestType::NativeInterface: - case ENestType::Interface: - TopNest->Allow = ENestAllowFlags::Function; - break; - - case ENestType::FunctionDeclaration: - TopNest->Allow = ENestAllowFlags::VarDecl; - - break; - - default: - FError::Throwf(TEXT("Internal error in PushNest, type %i"), (uint8)NestType); - break; - } -} - -/** - * 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 - */ -void FHeaderParser::PopNest(ENestType NestType, const TCHAR* Descr) -{ - // Validate the nesting state. - if (NestLevel <= 0) - { - FError::Throwf(TEXT("Unexpected '%s' at global scope"), Descr, NestTypeName(NestType)); - } - else if (TopNest->NestType != NestType) - { - FError::Throwf(TEXT("Unexpected end of %s in '%s' block"), Descr, NestTypeName(TopNest->NestType)); - } - - if (NestType != ENestType::GlobalScope && NestType != ENestType::Class && NestType != ENestType::Interface && NestType != ENestType::NativeInterface && NestType != ENestType::FunctionDeclaration) - { - FError::Throwf(TEXT("Bad first pass NestType %i"), (uint8)NestType); - } - - bool bLinkProps = true; - if (NestType == ENestType::Class) - { - UClass* TopClass = GetCurrentClass(); - bLinkProps = !TopClass->HasAnyClassFlags(CLASS_Intrinsic); - } - - if (NestType != ENestType::GlobalScope) - { - GetCurrentClass()->StaticLink(bLinkProps); - } - - // Pop the nesting level. - NestType = TopNest->NestType; - NestLevel--; - if (NestLevel == 0) - { - TopNest = nullptr; - } - else - { - TopNest--; - check(TopNest >= Nest); - - } -} - -void FHeaderParser::FixupDelegateProperties( FClasses& AllClasses, UStruct* Struct, FScope& Scope, TMap& DelegateCache ) -{ - check(Struct); - - for ( FField* Field = Struct->ChildProperties; Field; Field = Field->Next ) - { - FProperty* Property = CastField(Field); - if ( Property != NULL ) - { - FDelegateProperty* DelegateProperty = CastField(Property); - FMulticastDelegateProperty* MulticastDelegateProperty = CastField(Property); - if ( DelegateProperty == NULL && MulticastDelegateProperty == NULL ) - { - // if this is an array property, see if the array's type is a delegate - FArrayProperty* ArrayProp = CastField(Property); - if ( ArrayProp != NULL ) - { - DelegateProperty = CastField(ArrayProp->Inner); - MulticastDelegateProperty = CastField(ArrayProp->Inner); - } - } - if (DelegateProperty != nullptr || MulticastDelegateProperty != nullptr) - { - // this FDelegateProperty corresponds to an actual delegate variable (i.e. delegate Foo); we need to lookup the token data for - // this property and verify that the delegate property's "type" is an actual delegate function - FClassMetaData* StructData = GScriptHelper.FindClassData(Struct); - check(StructData); - FTokenData* DelegatePropertyToken = StructData->FindTokenData(Property); - check(DelegatePropertyToken); - - // attempt to find the delegate function in the map of functions we've already found - UFunction* SourceDelegateFunction = DelegateCache.FindRef(DelegatePropertyToken->Token.DelegateName); - if (SourceDelegateFunction == nullptr) - { - 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 - SourceDelegateFunction = Cast(Scope.FindTypeByName(*NameOfDelegateFunction)); - if (SourceDelegateFunction == nullptr) - { - // Try to find in other packages. - UObject* DelegateSignatureOuter = DelegatePropertyToken->Token.DelegateSignatureOwnerClass - ? ((UObject*)DelegatePropertyToken->Token.DelegateSignatureOwnerClass) - : ((UObject*)ANY_PACKAGE); - SourceDelegateFunction = Cast(StaticFindObject(UFunction::StaticClass(), DelegateSignatureOuter, *NameOfDelegateFunction)); - - if (SourceDelegateFunction == nullptr) - { - // convert this into a fully qualified path name for the error message. - NameOfDelegateFunction = Scope.GetName().ToString() + TEXT(".") + NameOfDelegateFunction; - } - } - } - 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 - UClass* DelegateOwnerClass = AllClasses.FindScriptClassOrThrow(DelegateClassName); - if (FScope::GetTypeScope(DelegateOwnerClass)->FindTypeByName(*DelegateName) != nullptr) - { - FError::Throwf(TEXT("Inaccessible type: '%s'"), *DelegateOwnerClass->GetPathName()); - } - SourceDelegateFunction = Cast(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 PropIt(SourceDelegateFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt) - { - FProperty* FuncParam = *PropIt; - - if (!IsPropertySupportedByBlueprint(FuncParam, false)) - { - FString ExtendedCPPType; - FString CPPType = FuncParam->GetCPPType(&ExtendedCPPType); - UE_LOG_ERROR_UHT(TEXT("Type '%s%s' is not supported by blueprint. %s.%s"), *CPPType, *ExtendedCPPType, *SourceDelegateFunction->GetName(), *FuncParam->GetName()); - } - - if(FuncParam->HasAllPropertyFlags(CPF_OutParm) && !FuncParam->HasAllPropertyFlags(CPF_ConstParm) ) - { - const bool bClassGeneratedFromBP = FClass::IsDynamic(Struct); - const bool bAllowedArrayRefFromBP = bClassGeneratedFromBP && FuncParam->IsA(); - if (!bAllowedArrayRefFromBP) - { - UE_LOG_ERROR_UHT(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()); - } - } - } - } - } - - for (UField* Field = Struct->Children; Field; Field = Field->Next) - { - // if this is a state, function, or script struct, it might have its own delegate properties which need to be validated - UStruct* InternalStruct = Cast(Field); - if ( InternalStruct != NULL ) - { - FixupDelegateProperties(AllClasses, InternalStruct, Scope, DelegateCache); - } - } - - CheckDocumentationPolicyForStruct(Struct); - - ParseRigVMMethodParameters(Struct); -} - -void FHeaderParser::CheckSparseClassData(const UStruct* StructToCheck) -{ - // we're looking for classes that have sparse class data structures - const UClass* ClassToCheck = Cast(StructToCheck); - if (!ClassToCheck) - { - // make sure we don't try to have sparse class data inside of a struct instead of a class - if (StructToCheck->HasMetaData(FHeaderParserNames::NAME_SparseClassDataTypes)) - { - FError::Throwf(TEXT("%s contains sparse class data but is not a class."), *StructToCheck->GetName()); - } - return; - } - - if (!ClassToCheck->HasMetaData(FHeaderParserNames::NAME_SparseClassDataTypes)) - { - return; - } - - TArray SparseClassDataTypes; - ((FClass*)ClassToCheck)->GetSparseClassDataTypes(SparseClassDataTypes); - - // for now we only support one sparse class data structure per class - if (SparseClassDataTypes.Num() > 1) - { - FError::Throwf(TEXT("Class %s contains multiple sparse class data types."), *ClassToCheck->GetName()); - return; - } - if (SparseClassDataTypes.Num() == 0) - { - FError::Throwf(TEXT("Class %s has sparse class metadata but does not specify a type."), *ClassToCheck->GetName()); - return; - } - - for (const FString& SparseClassDataTypeName : SparseClassDataTypes) - { - UScriptStruct* SparseClassDataStruct = FindObjectSafe(ANY_PACKAGE, *SparseClassDataTypeName); - - // make sure the sparse class data struct actually exists - if (!SparseClassDataStruct) - { - FError::Throwf(TEXT("Unable to find sparse data type %s for class %s."), *SparseClassDataTypeName, *ClassToCheck->GetName()); - return; - } - - // check the data struct for invalid properties - for (TFieldIterator Property(SparseClassDataStruct); Property; ++Property) - { - if (Property->HasAnyPropertyFlags(CPF_BlueprintAssignable)) - { - FError::Throwf(TEXT("Sparse class data types can not contain blueprint assignable delegates. Type '%s' Delegate '%s'"), *SparseClassDataStruct->GetName(), *Property->GetName()); - } - - // all sparse properties should have EditDefaultsOnly - if (!Property->HasAllPropertyFlags(CPF_Edit | CPF_DisableEditOnInstance)) - { - FError::Throwf(TEXT("Sparse class data types must be EditDefaultsOnly. Type '%s' Property '%s'"), *SparseClassDataStruct->GetName(), *Property->GetName()); - } - - // no sparse properties should have BlueprintReadWrite - if (Property->HasAllPropertyFlags(CPF_BlueprintVisible) && !Property->HasAllPropertyFlags(CPF_BlueprintReadOnly)) - { - FError::Throwf(TEXT("Sparse class data types must not be BlueprintReadWrite. Type '%s' Property '%s'"), *SparseClassDataStruct->GetName(), *Property->GetName()); - } - } - - // if the class's parent has a sparse class data struct then the current class must also use the same struct or one that inherits from it - const UClass* ParentClass = ClassToCheck->GetSuperClass(); - TArray ParentSparseClassDataTypeNames; - ((FClass*)ParentClass)->GetSparseClassDataTypes(ParentSparseClassDataTypeNames); - for (FString& ParentSparseClassDataTypeName : ParentSparseClassDataTypeNames) - { - UScriptStruct* ParentSparseClassDataStruct = FindObjectSafe(ANY_PACKAGE, *ParentSparseClassDataTypeName); - if (ParentSparseClassDataStruct && !SparseClassDataStruct->IsChildOf(ParentSparseClassDataStruct)) - { - FError::Throwf(TEXT("Class %s is a child of %s but its sparse class data struct, %s, does not inherit from %s."), *ClassToCheck->GetName(), *ParentClass->GetName(), *SparseClassDataStruct->GetName(), *ParentSparseClassDataStruct->GetName()); - } - } - } -} - -void FHeaderParser::VerifyBlueprintPropertyGetter(FProperty* Prop, UFunction* TargetFunc) -{ - check(TargetFunc); - - FProperty* ReturnProp = TargetFunc->GetReturnProperty(); - if (TargetFunc->NumParms > 1 || (TargetFunc->NumParms == 1 && ReturnProp == nullptr)) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint Property getter function %s must not have parameters."), *TargetFunc->GetName()); - } - - if (ReturnProp == nullptr || !Prop->SameType(ReturnProp)) - { - FString ExtendedCPPType; - FString CPPType = Prop->GetCPPType(&ExtendedCPPType); - UE_LOG_ERROR_UHT(TEXT("Blueprint Property getter function %s must have return value of type %s%s."), *TargetFunc->GetName(), *CPPType, *ExtendedCPPType); - } - - if (TargetFunc->HasAnyFunctionFlags(FUNC_Event)) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint Property setter function cannot be a blueprint event.")); - } - else if (!TargetFunc->HasAnyFunctionFlags(FUNC_BlueprintPure)) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint Property getter function must be pure.")); - } -} - -void FHeaderParser::VerifyBlueprintPropertySetter(FProperty* Prop, UFunction* TargetFunc) -{ - check(TargetFunc); - FProperty* ReturnProp = TargetFunc->GetReturnProperty(); - - if (ReturnProp) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint Property setter function %s must not have a return value."), *TargetFunc->GetName()); - } - else - { - TFieldIterator Parm(TargetFunc); - if (TargetFunc->NumParms != 1 || !Prop->SameType(*Parm)) - { - FString ExtendedCPPType; - FString CPPType = Prop->GetCPPType(&ExtendedCPPType); - UE_LOG_ERROR_UHT(TEXT("Blueprint Property setter function %s must have exactly one parameter of type %s%s."), *TargetFunc->GetName(), *CPPType, *ExtendedCPPType); - } - } - - if (TargetFunc->HasAnyFunctionFlags(FUNC_Event)) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint Property setter function cannot be a blueprint event.")); - } - else if (!TargetFunc->HasAnyFunctionFlags(FUNC_BlueprintCallable)) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint Property setter function must be blueprint callable.")); - } - else if (TargetFunc->HasAnyFunctionFlags(FUNC_BlueprintPure)) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint Property setter function must not be pure.")); - } -} - -void FHeaderParser::VerifyRepNotifyCallback(FProperty* Prop, UFunction* TargetFunc) -{ - if( TargetFunc ) - { - if (TargetFunc->GetReturnProperty()) - { - UE_LOG_ERROR_UHT(TEXT("Replication notification function %s must not have return value."), *TargetFunc->GetName()); - } - - const bool bIsArrayProperty = ( Prop->ArrayDim > 1 || CastField(Prop) ); - const int32 MaxParms = bIsArrayProperty ? 2 : 1; - - if ( TargetFunc->NumParms > MaxParms) - { - UE_LOG_ERROR_UHT(TEXT("Replication notification function %s has too many parameters."), *TargetFunc->GetName()); - } - - TFieldIterator Parm(TargetFunc); - if ( TargetFunc->NumParms >= 1 && Parm) - { - // First parameter is always the old value: - if ( !Prop->SameType(*Parm) ) - { - FString ExtendedCPPType; - FString CPPType = Prop->GetCPPType(&ExtendedCPPType); - UE_LOG_ERROR_UHT(TEXT("Replication notification function %s has invalid parameter for property %s. First (optional) parameter must be of type %s%s."), *TargetFunc->GetName(), *Prop->GetName(), *CPPType, *ExtendedCPPType); - } - - ++Parm; - } - - if ( TargetFunc->NumParms >= 2 && Parm) - { - // A 2nd parameter for arrays can be specified as a const TArray&. This is a list of element indices that have changed - FArrayProperty *ArrayProp = CastField(*Parm); - if (!(ArrayProp && CastField(ArrayProp->Inner)) || !(Parm->GetPropertyFlags() & CPF_ConstParm) || !(Parm->GetPropertyFlags() & CPF_ReferenceParm)) - { - UE_LOG_ERROR_UHT(TEXT("Replication notification function %s (optional) second parameter must be of type 'const TArray&'"), *TargetFunc->GetName()); - } - } - } - else - { - // Couldn't find a valid function... - UE_LOG_ERROR_UHT(TEXT("Replication notification function %s not found"), *Prop->RepNotifyFunc.ToString() ); - } -} -void FHeaderParser::VerifyPropertyMarkups( UClass* TargetClass ) -{ - // Iterate over all properties, looking for those flagged as CPF_RepNotify - for ( FField* Field = TargetClass->ChildProperties; Field; Field = Field->Next ) - { - if (FProperty* Prop = CastField(Field)) - { - auto FindTargetFunction = [&](const FName FuncName) - { - // Search through this class and its superclasses looking for the specified callback - UFunction* TargetFunc = nullptr; - 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(TestField); - if (TestFunc && FNativeClassHeaderGenerator::GetOverriddenFName(TestFunc) == FuncName) - { - TargetFunc = TestFunc; - break; - } - } - SearchClass = SearchClass->GetSuperClass(); - } - - return TargetFunc; - }; - - FClassMetaData* TargetClassData = GScriptHelper.FindClassData(TargetClass); - check(TargetClassData); - FTokenData* PropertyToken = TargetClassData->FindTokenData(Prop); - check(PropertyToken); - - TGuardValue GuardedInputPos(InputPos, PropertyToken->Token.StartPos); - TGuardValue GuardedInputLine(InputLine, PropertyToken->Token.StartLine); - - if (Prop->HasAnyPropertyFlags(CPF_RepNotify)) - { - VerifyRepNotifyCallback(Prop, FindTargetFunction(Prop->RepNotifyFunc)); - } - - if (Prop->HasAnyPropertyFlags(CPF_BlueprintVisible)) - { - const FString& GetterFuncName = Prop->GetMetaData(NAME_BlueprintGetter); - if (!GetterFuncName.IsEmpty()) - { - if (UFunction* TargetFunc = FindTargetFunction(*GetterFuncName)) - { - VerifyBlueprintPropertyGetter(Prop, TargetFunc); - } - else - { - // Couldn't find a valid function... - UE_LOG_ERROR_UHT(TEXT("Blueprint Property getter function %s not found"), *GetterFuncName); - } - } - - if (!Prop->HasAnyPropertyFlags(CPF_BlueprintReadOnly)) - { - const FString& SetterFuncName = Prop->GetMetaData(NAME_BlueprintSetter); - if (!SetterFuncName.IsEmpty()) - { - if (UFunction* TargetFunc = FindTargetFunction(*SetterFuncName)) - { - VerifyBlueprintPropertySetter(Prop, TargetFunc); - } - else - { - // Couldn't find a valid function... - UE_LOG_ERROR_UHT(TEXT("Blueprint Property setter function %s not found"), *SetterFuncName); - } - } - } - } - } - } -} - - -/*----------------------------------------------------------------------------- - Compiler directives. ------------------------------------------------------------------------------*/ - -// -// Process a compiler directive. -// -void FHeaderParser::CompileDirective(FClasses& AllClasses) -{ - FUnrealSourceFile* CurrentSourceFilePtr = GetCurrentSourceFile(); - TSharedRef CurrentSrcFile = CurrentSourceFilePtr->AsShared(); - 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"), ESearchCase::CaseSensitive)) - { - FError::Throwf(TEXT("#error directive encountered") ); - } - else if (Directive.Matches(TEXT("pragma"), ESearchCase::CaseSensitive)) - { - // Ignore all pragmas - } - else if (Directive.Matches(TEXT("linenumber"), ESearchCase::CaseSensitive)) - { - FToken Number; - if (!GetToken(Number) || (Number.TokenType != TOKEN_Const) || (Number.Type != CPT_Int && Number.Type != CPT_Int64)) - { - FError::Throwf(TEXT("Missing line number in line number directive")); - } - - int32 newInputLine; - if ( Number.GetConstInt(newInputLine) ) - { - InputLine = newInputLine; - } - } - else if (Directive.Matches(TEXT("include"), ESearchCase::CaseSensitive)) - { - FToken IncludeName; - if (GetToken(IncludeName) && (IncludeName.TokenType == TOKEN_Const) && (IncludeName.Type == CPT_String)) - { - const FString& ExpectedHeaderName = CurrentSrcFile->GetGeneratedHeaderFilename(); - if (FCString::Stricmp(IncludeName.String, *ExpectedHeaderName) == 0) - { - bSpottedAutogeneratedHeaderInclude = true; - } - } - } - else if (Directive.Matches(TEXT("if"), ESearchCase::CaseSensitive)) - { - // Eat the ! if present - bool bNotDefined = MatchSymbol(TEXT('!')); - - int32 TempInt; - const bool bParsedInt = GetConstInt(TempInt); - if (bParsedInt && (TempInt == 0 || TempInt == 1)) - { - PushCompilerDirective(ECompilerDirective::Insignificant); - } - else - { - FToken Define; - if (!GetIdentifier(Define)) - { - FError::Throwf(TEXT("Missing define name '#if'") ); - } - - if ( Define.Matches(TEXT("WITH_EDITORONLY_DATA"), ESearchCase::CaseSensitive) ) - { - PushCompilerDirective(ECompilerDirective::WithEditorOnlyData); - } - else if ( Define.Matches(TEXT("WITH_EDITOR"), ESearchCase::CaseSensitive) ) - { - PushCompilerDirective(ECompilerDirective::WithEditor); - } - else if (Define.Matches(TEXT("WITH_HOT_RELOAD"), ESearchCase::CaseSensitive) || Define.Matches(TEXT("WITH_HOT_RELOAD_CTORS"), ESearchCase::CaseSensitive) || Define.Matches(TEXT('1'))) - { - PushCompilerDirective(ECompilerDirective::Insignificant); - } - else if ( Define.Matches(TEXT("CPP"), ESearchCase::CaseSensitive) && 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"), ESearchCase::CaseSensitive)) - { - if (CompilerDirectiveStack.Num() < 1) - { - FError::Throwf(TEXT("Unmatched '#endif' in class or global scope")); - } - CompilerDirectiveStack.Pop(); - } - else if (Directive.Matches(TEXT("define"), ESearchCase::CaseSensitive)) - { - // Ignore the define directive (can be multiline). - bDefineDirective = true; - } - else if (Directive.Matches(TEXT("ifdef"), ESearchCase::CaseSensitive) || Directive.Matches(TEXT("ifndef"), ESearchCase::CaseSensitive)) - { - PushCompilerDirective(ECompilerDirective::Insignificant); - } - else if (Directive.Matches(TEXT("undef"), ESearchCase::CaseSensitive) || Directive.Matches(TEXT("else"), ESearchCase::CaseSensitive)) - { - // 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. ------------------------------------------------------------------------------*/ - -void FHeaderParser::GetVarType( - FClasses& AllClasses, - FScope* Scope, - FPropertyBase& VarProperty, - EPropertyFlags Disallow, - const FToken* OuterPropertyType, - EPropertyDeclarationStyle::Type PropertyDeclarationStyle, - EVariableCategory::Type VariableCategory, - FIndexRange* ParsedVarIndexRange, - ELayoutMacroType* OutLayoutMacroType -) -{ - UStruct* OwnerStruct = Scope->IsFileScope() ? nullptr : ((FStructScope*)Scope)->GetStruct(); - FName RepCallbackName = FName(NAME_None); - - // Get flags. - EPropertyFlags Flags = CPF_None; - EPropertyFlags ImpliedFlags = CPF_None; - - // force members to be 'blueprint read only' if in a const class - if (VariableCategory == EVariableCategory::Member) - { - if (UClass* OwnerClass = Cast(OwnerStruct)) - { - if (OwnerClass->ClassFlags & CLASS_Const) - { - ImpliedFlags |= CPF_BlueprintReadOnly; - } - } - } - uint32 ExportFlags = PROPEXPORT_Public; - - // Build up a list of specifiers - TArray SpecifiersFound; - - TMap MetaDataFromNewStyle; - bool bNativeConst = false; - bool bNativeConstTemplateArg = false; - - const bool bIsParamList = (VariableCategory != EVariableCategory::Member) && MatchIdentifier(TEXT("UPARAM"), ESearchCase::CaseSensitive); - - // No specifiers are allowed inside a TArray - if ((OuterPropertyType == NULL) || !OuterPropertyType->Matches(TEXT("TArray"), ESearchCase::CaseSensitive)) - { - // New-style UPROPERTY() syntax - if (PropertyDeclarationStyle == EPropertyDeclarationStyle::UPROPERTY || bIsParamList) - { - ReadSpecifierSetInsideMacro(SpecifiersFound, TEXT("Variable"), MetaDataFromNewStyle); - } - } - - if (VariableCategory != EVariableCategory::Member) - { - // const before the variable type support (only for params) - if (MatchIdentifier(TEXT("const"), ESearchCase::CaseSensitive)) - { - Flags |= CPF_ConstParm; - bNativeConst = true; - } - } - - if (CompilerDirectiveStack.Num() > 0 && (CompilerDirectiveStack.Last()&ECompilerDirective::WithEditorOnlyData) != 0) - { - Flags |= CPF_EditorOnly; - } - - // Store the start and end positions of the parsed type - if (ParsedVarIndexRange) - { - ParsedVarIndexRange->StartIndex = InputPos; - } - - // Process the list of specifiers - bool bSeenEditSpecifier = false; - bool bSeenBlueprintWriteSpecifier = false; - bool bSeenBlueprintReadOnlySpecifier = false; - bool bSeenBlueprintGetterSpecifier = false; - for (const FPropertySpecifier& Specifier : SpecifiersFound) - { - EVariableSpecifier SpecID = (EVariableSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GVariableSpecifierStrings); - if (VariableCategory == EVariableCategory::Member) - { - switch (SpecID) - { - case EVariableSpecifier::EditAnywhere: - { - if (bSeenEditSpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Found more than one edit/visibility specifier (%s), only one is allowed"), *Specifier.Key); - } - Flags |= CPF_Edit; - bSeenEditSpecifier = true; - } - break; - - case EVariableSpecifier::EditInstanceOnly: - { - if (bSeenEditSpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Found more than one edit/visibility specifier (%s), only one is allowed"), *Specifier.Key); - } - Flags |= CPF_Edit | CPF_DisableEditOnTemplate; - bSeenEditSpecifier = true; - } - break; - - case EVariableSpecifier::EditDefaultsOnly: - { - if (bSeenEditSpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Found more than one edit/visibility specifier (%s), only one is allowed"), *Specifier.Key); - } - Flags |= CPF_Edit | CPF_DisableEditOnInstance; - bSeenEditSpecifier = true; - } - break; - - case EVariableSpecifier::VisibleAnywhere: - { - if (bSeenEditSpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Found more than one edit/visibility specifier (%s), only one is allowed"), *Specifier.Key); - } - Flags |= CPF_Edit | CPF_EditConst; - bSeenEditSpecifier = true; - } - break; - - case EVariableSpecifier::VisibleInstanceOnly: - { - if (bSeenEditSpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Found more than one edit/visibility specifier (%s), only one is allowed"), *Specifier.Key); - } - Flags |= CPF_Edit | CPF_EditConst | CPF_DisableEditOnTemplate; - bSeenEditSpecifier = true; - } - break; - - case EVariableSpecifier::VisibleDefaultsOnly: - { - if (bSeenEditSpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Found more than one edit/visibility specifier (%s), only one is allowed"), *Specifier.Key); - } - Flags |= CPF_Edit | CPF_EditConst | CPF_DisableEditOnInstance; - bSeenEditSpecifier = true; - } - break; - - case EVariableSpecifier::BlueprintReadWrite: - { - if (bSeenBlueprintReadOnlySpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Cannot specify a property as being both BlueprintReadOnly and BlueprintReadWrite.")); - } - - const FString* PrivateAccessMD = MetaDataFromNewStyle.Find(NAME_AllowPrivateAccess); // FBlueprintMetadata::MD_AllowPrivateAccess - const bool bAllowPrivateAccess = PrivateAccessMD ? (*PrivateAccessMD != TEXT("false")) : false; - if (CurrentAccessSpecifier == ACCESS_Private && !bAllowPrivateAccess) - { - UE_LOG_ERROR_UHT(TEXT("BlueprintReadWrite should not be used on private members")); - } - - if ((Flags & CPF_EditorOnly) != 0 && OwnerStruct->IsA()) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint exposed struct members cannot be editor only")); - } - - Flags |= CPF_BlueprintVisible; - bSeenBlueprintWriteSpecifier = true; - } - break; - - case EVariableSpecifier::BlueprintSetter: - { - if (bSeenBlueprintReadOnlySpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Cannot specify a property as being both BlueprintReadOnly and having a BlueprintSetter.")); - } - - if (OwnerStruct->IsA()) - { - UE_LOG_ERROR_UHT(TEXT("Cannot specify BlueprintSetter for a struct member.")) - } - - const FString BlueprintSetterFunction = RequireExactlyOneSpecifierValue(Specifier); - MetaDataFromNewStyle.Add(NAME_BlueprintSetter, BlueprintSetterFunction); - - Flags |= CPF_BlueprintVisible; - bSeenBlueprintWriteSpecifier = true; - } - break; - - case EVariableSpecifier::BlueprintReadOnly: - { - if (bSeenBlueprintWriteSpecifier) - { - UE_LOG_ERROR_UHT(TEXT("Cannot specify both BlueprintReadOnly and BlueprintReadWrite or BlueprintSetter."), *Specifier.Key); - } - - const FString* PrivateAccessMD = MetaDataFromNewStyle.Find(NAME_AllowPrivateAccess); // FBlueprintMetadata::MD_AllowPrivateAccess - const bool bAllowPrivateAccess = PrivateAccessMD ? (*PrivateAccessMD != TEXT("false")) : false; - if (CurrentAccessSpecifier == ACCESS_Private && !bAllowPrivateAccess) - { - UE_LOG_ERROR_UHT(TEXT("BlueprintReadOnly should not be used on private members")); - } - - if ((Flags & CPF_EditorOnly) != 0 && OwnerStruct->IsA()) - { - UE_LOG_ERROR_UHT(TEXT("Blueprint exposed struct members cannot be editor only")); - } - - Flags |= CPF_BlueprintVisible | CPF_BlueprintReadOnly; - ImpliedFlags &= ~CPF_BlueprintReadOnly; - bSeenBlueprintReadOnlySpecifier = true; - } - break; - - case EVariableSpecifier::BlueprintGetter: - { - if (OwnerStruct->IsA()) - { - UE_LOG_ERROR_UHT(TEXT("Cannot specify BlueprintGetter for a struct member.")) - } - - const FString BlueprintGetterFunction = RequireExactlyOneSpecifierValue(Specifier); - MetaDataFromNewStyle.Add(NAME_BlueprintGetter, BlueprintGetterFunction); - - Flags |= CPF_BlueprintVisible; - bSeenBlueprintGetterSpecifier = true; - } - break; - - case EVariableSpecifier::Config: - { - Flags |= CPF_Config; - } - break; - - case EVariableSpecifier::GlobalConfig: - { - Flags |= CPF_GlobalConfig | CPF_Config; - } - break; - - case EVariableSpecifier::Localized: - { - UE_LOG_ERROR_UHT(TEXT("The Localized specifier is deprecated")); - } - break; - - case EVariableSpecifier::Transient: - { - Flags |= CPF_Transient; - } - break; - - case EVariableSpecifier::DuplicateTransient: - { - Flags |= CPF_DuplicateTransient; - } - break; - - case EVariableSpecifier::TextExportTransient: - { - Flags |= CPF_TextExportTransient; - } - break; - - case EVariableSpecifier::NonPIETransient: - { - UE_LOG_WARNING_UHT(TEXT("NonPIETransient is deprecated - NonPIEDuplicateTransient should be used instead")); - Flags |= CPF_NonPIEDuplicateTransient; - } - break; - - case EVariableSpecifier::NonPIEDuplicateTransient: - { - Flags |= CPF_NonPIEDuplicateTransient; - } - break; - - case EVariableSpecifier::Export: - { - Flags |= CPF_ExportObject; - } - break; - - case EVariableSpecifier::EditInline: - { - UE_LOG_ERROR_UHT(TEXT("EditInline is deprecated. Remove it, or use Instanced instead.")); - } - break; - - case EVariableSpecifier::NoClear: - { - Flags |= CPF_NoClear; - } - break; - - case EVariableSpecifier::EditFixedSize: - { - Flags |= CPF_EditFixedSize; - } - break; - - case EVariableSpecifier::Replicated: - case EVariableSpecifier::ReplicatedUsing: - { - if (OwnerStruct->IsA()) - { - UE_LOG_ERROR_UHT(TEXT("Struct members cannot be replicated")); - } - - Flags |= CPF_Net; - - // See if we've specified a rep notification function - if (SpecID == EVariableSpecifier::ReplicatedUsing) - { - RepCallbackName = FName(*RequireExactlyOneSpecifierValue(Specifier)); - Flags |= CPF_RepNotify; - } - } - break; - - case EVariableSpecifier::NotReplicated: - { - if (!OwnerStruct->IsA()) - { - UE_LOG_ERROR_UHT(TEXT("Only Struct members can be marked NotReplicated")); - } - - Flags |= CPF_RepSkip; - } - break; - - case EVariableSpecifier::RepRetry: - { - UE_LOG_ERROR_UHT(TEXT("'RepRetry' is deprecated.")); - } - break; - - case EVariableSpecifier::Interp: - { - Flags |= CPF_Edit; - Flags |= CPF_BlueprintVisible; - Flags |= CPF_Interp; - } - break; - - case EVariableSpecifier::NonTransactional: - { - Flags |= CPF_NonTransactional; - } - break; - - case EVariableSpecifier::Instanced: - { - Flags |= CPF_PersistentInstance | CPF_ExportObject | CPF_InstancedReference; - AddEditInlineMetaData(MetaDataFromNewStyle); - } - break; - - case EVariableSpecifier::BlueprintAssignable: - { - Flags |= CPF_BlueprintAssignable; - } - break; - - case EVariableSpecifier::BlueprintCallable: - { - Flags |= CPF_BlueprintCallable; - } - break; - - case EVariableSpecifier::BlueprintAuthorityOnly: - { - Flags |= CPF_BlueprintAuthorityOnly; - } - break; - - case EVariableSpecifier::AssetRegistrySearchable: - { - Flags |= CPF_AssetRegistrySearchable; - } - break; - - case EVariableSpecifier::SimpleDisplay: - { - Flags |= CPF_SimpleDisplay; - } - break; - - case EVariableSpecifier::AdvancedDisplay: - { - Flags |= CPF_AdvancedDisplay; - } - break; - - case EVariableSpecifier::SaveGame: - { - Flags |= CPF_SaveGame; - } - break; - - case EVariableSpecifier::SkipSerialization: - { - Flags |= CPF_SkipSerialization; - } - break; - - default: - { - UE_LOG_ERROR_UHT(TEXT("Unknown variable specifier '%s'"), *Specifier.Key); - } - break; - } - } - else - { - switch (SpecID) - { - case EVariableSpecifier::Const: - { - Flags |= CPF_ConstParm; - } - break; - - case EVariableSpecifier::Ref: - { - Flags |= CPF_OutParm | CPF_ReferenceParm; - } - break; - - case EVariableSpecifier::NotReplicated: - { - if (VariableCategory == EVariableCategory::ReplicatedParameter) - { - VariableCategory = EVariableCategory::RegularParameter; - Flags |= CPF_RepSkip; - } - else - { - UE_LOG_ERROR_UHT(TEXT("Only parameters in service request functions can be marked NotReplicated")); - } - } - break; - - default: - { - UE_LOG_ERROR_UHT(TEXT("Unknown variable specifier '%s'"), *Specifier.Key); - } - break; - } - } - } - - // If we saw a BlueprintGetter but did not see BlueprintSetter or - // or BlueprintReadWrite then treat as BlueprintReadOnly - if (bSeenBlueprintGetterSpecifier && !bSeenBlueprintWriteSpecifier) - { - Flags |= CPF_BlueprintReadOnly; - ImpliedFlags &= ~CPF_BlueprintReadOnly; - } - - { - const FString* ExposeOnSpawnStr = MetaDataFromNewStyle.Find(NAME_ExposeOnSpawn); - const bool bExposeOnSpawn = (NULL != ExposeOnSpawnStr); - if (bExposeOnSpawn) - { - if (0 != (CPF_DisableEditOnInstance & Flags)) - { - UE_LOG_WARNING_UHT(TEXT("Property cannot have both 'DisableEditOnInstance' and 'ExposeOnSpawn' flags")); - } - if (0 == (CPF_BlueprintVisible & Flags)) - { - UE_LOG_WARNING_UHT(TEXT("Property cannot have 'ExposeOnSpawn' without 'BlueprintVisible' flag.")); - } - Flags |= CPF_ExposeOnSpawn; - } - } - - if (CurrentAccessSpecifier == ACCESS_Public || VariableCategory != EVariableCategory::Member) - { - Flags &= ~CPF_Protected; - ExportFlags |= PROPEXPORT_Public; - ExportFlags &= ~(PROPEXPORT_Private|PROPEXPORT_Protected); - - Flags &= ~CPF_NativeAccessSpecifiers; - Flags |= CPF_NativeAccessSpecifierPublic; - } - else if (CurrentAccessSpecifier == ACCESS_Protected) - { - Flags |= CPF_Protected; - ExportFlags |= PROPEXPORT_Protected; - ExportFlags &= ~(PROPEXPORT_Public|PROPEXPORT_Private); - - Flags &= ~CPF_NativeAccessSpecifiers; - Flags |= CPF_NativeAccessSpecifierProtected; - } - else if (CurrentAccessSpecifier == ACCESS_Private) - { - Flags &= ~CPF_Protected; - ExportFlags |= PROPEXPORT_Private; - ExportFlags &= ~(PROPEXPORT_Public|PROPEXPORT_Protected); - - Flags &= ~CPF_NativeAccessSpecifiers; - Flags |= CPF_NativeAccessSpecifierPrivate; - } - else - { - FError::Throwf(TEXT("Unknown access level")); - } - - // Swallow inline keywords - if (VariableCategory == EVariableCategory::Return) - { - FToken InlineToken; - if (!GetIdentifier(InlineToken, true)) - { - FError::Throwf(TEXT("%s: Missing variable type"), GetHintText(VariableCategory)); - } - - if (FCString::Strcmp(InlineToken.Identifier, TEXT("inline")) != 0 - && FCString::Strcmp(InlineToken.Identifier, TEXT("FORCENOINLINE")) != 0 - && FCString::Strncmp(InlineToken.Identifier, TEXT("FORCEINLINE"), 11) != 0) - { - UngetToken(InlineToken); - } - } - - // Get variable type. - bool bUnconsumedStructKeyword = false; - bool bUnconsumedClassKeyword = false; - bool bUnconsumedEnumKeyword = false; - bool bUnconsumedConstKeyword = false; - - // Handle MemoryLayout.h macros - ELayoutMacroType LayoutMacroType = ELayoutMacroType::None; - bool bHasWrapperBrackets = false; - ON_SCOPE_EXIT - { - if (OutLayoutMacroType) - { - *OutLayoutMacroType = LayoutMacroType; - if (bHasWrapperBrackets) - { - RequireSymbol(TEXT(')'), GLayoutMacroNames[(int32)LayoutMacroType]); - } - } - }; - - if (OutLayoutMacroType) - { - *OutLayoutMacroType = ELayoutMacroType::None; - - FToken LayoutToken; - if (GetToken(LayoutToken)) - { - if (LayoutToken.TokenType == TOKEN_Identifier) - { - LayoutMacroType = (ELayoutMacroType)Algo::FindSortedStringCaseInsensitive(LayoutToken.Identifier, GLayoutMacroNames, UE_ARRAY_COUNT(GLayoutMacroNames)); - if (LayoutMacroType != ELayoutMacroType::None) - { - RequireSymbol(TEXT('('), GLayoutMacroNames[(int32)LayoutMacroType]); - if (LayoutMacroType == ELayoutMacroType::ArrayEditorOnly || LayoutMacroType == ELayoutMacroType::FieldEditorOnly || LayoutMacroType == ELayoutMacroType::BitfieldEditorOnly) - { - Flags |= CPF_EditorOnly; - } - bHasWrapperBrackets = MatchSymbol(TEXT("(")); - } - else - { - UngetToken(LayoutToken); - } - } - } - } - - if (MatchIdentifier(TEXT("const"), ESearchCase::CaseSensitive)) - { - //@TODO: UCREMOVAL: Should use this to set the new (currently non-existent) CPF_Const flag appropriately! - bUnconsumedConstKeyword = true; - bNativeConst = true; - } - - if (MatchIdentifier(TEXT("mutable"), ESearchCase::CaseSensitive)) - { - //@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"), ESearchCase::CaseSensitive)) - { - bUnconsumedStructKeyword = true; - } - else if (MatchIdentifier(TEXT("class"), ESearchCase::CaseSensitive)) - { - bUnconsumedClassKeyword = true; - } - else if (MatchIdentifier(TEXT("enum"), ESearchCase::CaseSensitive)) - { - if (VariableCategory == EVariableCategory::Member) - { - FError::Throwf(TEXT("%s: Cannot declare enum at variable declaration"), GetHintText(VariableCategory)); - } - - bUnconsumedEnumKeyword = true; - } - - // - FToken VarType; - if ( !GetIdentifier(VarType,1) ) - { - FError::Throwf(TEXT("%s: Missing variable type"), GetHintText(VariableCategory)); - } - - RedirectTypeIdentifier(VarType); - - if ( VarType.Matches(TEXT("int8"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_Int8); - } - else if ( VarType.Matches(TEXT("int16"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_Int16); - } - else if ( VarType.Matches(TEXT("int32"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_Int); - } - else if ( VarType.Matches(TEXT("int64"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_Int64); - } - else if ( VarType.Matches(TEXT("uint64"), ESearchCase::CaseSensitive) && IsBitfieldProperty(LayoutMacroType) ) - { - // 64-bit bitfield (bool) type, treat it like 8 bit type - VarProperty = FPropertyBase(CPT_Bool8); - } - else if ( VarType.Matches(TEXT("uint32"), ESearchCase::CaseSensitive) && IsBitfieldProperty(LayoutMacroType) ) - { - // 32-bit bitfield (bool) type, treat it like 8 bit type - VarProperty = FPropertyBase(CPT_Bool8); - } - else if ( VarType.Matches(TEXT("uint16"), ESearchCase::CaseSensitive) && IsBitfieldProperty(LayoutMacroType) ) - { - // 16-bit bitfield (bool) type, treat it like 8 bit type. - VarProperty = FPropertyBase(CPT_Bool8); - } - else if ( VarType.Matches(TEXT("uint8"), ESearchCase::CaseSensitive) && IsBitfieldProperty(LayoutMacroType) ) - { - // 8-bit bitfield (bool) type - VarProperty = FPropertyBase(CPT_Bool8); - } - else if ( VarType.Matches(TEXT("int"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_Int, EIntType::Unsized); - } - else if ( VarType.Matches(TEXT("signed"), ESearchCase::CaseSensitive) ) - { - MatchIdentifier(TEXT("int"), ESearchCase::CaseSensitive); - VarProperty = FPropertyBase(CPT_Int, EIntType::Unsized); - } - else if (VarType.Matches(TEXT("unsigned"), ESearchCase::CaseSensitive)) - { - MatchIdentifier(TEXT("int"), ESearchCase::CaseSensitive); - VarProperty = FPropertyBase(CPT_UInt32, EIntType::Unsized); - } - else if ( VarType.Matches(TEXT("bool"), ESearchCase::CaseSensitive) ) - { - if (IsBitfieldProperty(LayoutMacroType)) - { - UE_LOG_ERROR_UHT(TEXT("bool bitfields are not supported.")); - } - // C++ bool type - VarProperty = FPropertyBase(CPT_Bool); - } - else if ( VarType.Matches(TEXT("uint8"), ESearchCase::CaseSensitive) ) - { - // Intrinsic Byte type. - VarProperty = FPropertyBase(CPT_Byte); - } - else if ( VarType.Matches(TEXT("uint16"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_UInt16); - } - else if ( VarType.Matches(TEXT("uint32"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_UInt32); - } - else if ( VarType.Matches(TEXT("uint64"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_UInt64); - } - else if ( VarType.Matches(TEXT("float"), ESearchCase::CaseSensitive) ) - { - // Intrinsic single precision floating point type. - VarProperty = FPropertyBase(CPT_Float); - } - else if ( VarType.Matches(TEXT("double"), ESearchCase::CaseSensitive) ) - { - // Intrinsic double precision floating point type type. - VarProperty = FPropertyBase(CPT_Double); - } - else if ( VarType.Matches(TEXT("FName"), ESearchCase::CaseSensitive) ) - { - // Intrinsic Name type. - VarProperty = FPropertyBase(CPT_Name); - } - else if ( VarType.Matches(TEXT("TArray"), ESearchCase::CaseSensitive) ) - { - RequireSymbol( TEXT('<'), TEXT("'tarray'") ); - - VarType.PropertyFlags = Flags; - - GetVarType(AllClasses, Scope, VarProperty, Disallow, &VarType, EPropertyDeclarationStyle::None, VariableCategory); - if (VarProperty.IsContainer()) - { - FError::Throwf(TEXT("Nested containers are not supported.") ); - } - // TODO: Prevent sparse delegate types from being used in a container - - if (VarProperty.MetaData.Find(NAME_NativeConst)) - { - bNativeConstTemplateArg = true; - } - - VarType.PropertyFlags = VarProperty.PropertyFlags & (CPF_ContainsInstancedReference | CPF_InstancedReference); // propagate these to the array, we will fix them later - VarProperty.ArrayType = EArrayType::Dynamic; - - FToken CloseTemplateToken; - if (!GetToken(CloseTemplateToken, /*bNoConsts=*/ true, ESymbolParseOption::CloseTemplateBracket)) - { - FError::Throwf(TEXT("Missing token while parsing TArray.")); - } - - if (CloseTemplateToken.TokenType != TOKEN_Symbol || !CloseTemplateToken.Matches(TEXT('>'))) - { - // If we didn't find a comma, report it - if (!CloseTemplateToken.Matches(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("Unexpected end of file when parsing TArray allocator.")); - } - - if (AllocatorToken.TokenType != TOKEN_Identifier) - { - FError::Throwf(TEXT("Found '%s' - expected a '>' or ','."), AllocatorToken.Identifier); - } - - if (FCString::Strcmp(AllocatorToken.Identifier, TEXT("FMemoryImageAllocator")) == 0) - { - if (EnumHasAnyFlags(Flags, CPF_Net)) - { - FError::Throwf(TEXT("Replicated arrays with MemoryImageAllocators are not yet supported")); - } - - RequireSymbol(TEXT('>'), TEXT("TArray template arguments"), ESymbolParseOption::CloseTemplateBracket); - - VarProperty.AllocatorType = EAllocatorType::MemoryImage; - } - else if (FCString::Strcmp(AllocatorToken.Identifier, TEXT("TMemoryImageAllocator")) == 0) - { - if (EnumHasAnyFlags(Flags, CPF_Net)) - { - FError::Throwf(TEXT("Replicated arrays with MemoryImageAllocators are not yet supported")); - } - - RequireSymbol(TEXT('<'), TEXT("TMemoryImageAllocator template arguments")); - - FToken SkipToken; - for (;;) - { - if (!GetToken(SkipToken, /*bNoConsts=*/ false, ESymbolParseOption::CloseTemplateBracket)) - { - FError::Throwf(TEXT("Unexpected end of file when parsing TMemoryImageAllocator template arguments.")); - } - - if (SkipToken.TokenType == TOKEN_Symbol && FCString::Strcmp(SkipToken.Identifier, TEXT(">")) == 0) - { - RequireSymbol(TEXT('>'), TEXT("TArray template arguments"), ESymbolParseOption::CloseTemplateBracket); - VarProperty.AllocatorType = EAllocatorType::MemoryImage; - break; - } - } - } - else - { - FError::Throwf(TEXT("Found '%s' - explicit allocators are not supported in TArray properties."), AllocatorToken.Identifier); - } - } - } - else if ( VarType.Matches(TEXT("TMap"), ESearchCase::CaseSensitive) ) - { - RequireSymbol( TEXT('<'), TEXT("'tmap'") ); - - VarType.PropertyFlags = Flags; - - FToken MapKeyType; - GetVarType(AllClasses, Scope, MapKeyType, Disallow, &VarType, EPropertyDeclarationStyle::None, VariableCategory); - if (MapKeyType.IsContainer()) - { - FError::Throwf(TEXT("Nested containers are not supported.") ); - } - // TODO: Prevent sparse delegate types from being used in a container - - if (MapKeyType.Type == CPT_Interface) - { - FError::Throwf(TEXT("UINTERFACEs are not currently supported as key types.")); - } - - if (MapKeyType.Type == CPT_Text) - { - FError::Throwf(TEXT("FText is not currently supported as a key type.")); - } - - FToken CommaToken; - if (!GetToken(CommaToken, /*bNoConsts=*/ true) || CommaToken.TokenType != TOKEN_Symbol || !CommaToken.Matches(TEXT(','))) - { - FError::Throwf(TEXT("Missing value type while parsing TMap.")); - } - - GetVarType(AllClasses, Scope, VarProperty, Disallow, &VarType, EPropertyDeclarationStyle::None, VariableCategory); - if (VarProperty.IsContainer()) - { - FError::Throwf(TEXT("Nested containers are not supported.") ); - } - // TODO: Prevent sparse delegate types from being used in a container - - EPropertyFlags InnerFlags = (MapKeyType.PropertyFlags | VarProperty.PropertyFlags) & (CPF_ContainsInstancedReference | CPF_InstancedReference); // propagate these to the map value, we will fix them later - VarType.PropertyFlags = InnerFlags; - VarProperty.MapKeyProp = MakeShared(MoveTemp(MapKeyType)); - VarProperty.MapKeyProp->PropertyFlags = InnerFlags | (VarProperty.MapKeyProp->PropertyFlags & CPF_UObjectWrapper); // Make sure the 'UObjectWrapper' flag is maintained so that 'TMap, ...>' works - - FToken CloseTemplateToken; - if (!GetToken(CloseTemplateToken, /*bNoConsts=*/ true, ESymbolParseOption::CloseTemplateBracket)) - { - FError::Throwf(TEXT("Missing token while parsing TMap.")); - } - - if (CloseTemplateToken.TokenType != TOKEN_Symbol || !CloseTemplateToken.Matches(TEXT('>'))) - { - // If we didn't find a comma, report it - if (!CloseTemplateToken.Matches(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("Unexpected end of file when parsing TArray allocator.")); - } - - if (AllocatorToken.TokenType != TOKEN_Identifier) - { - FError::Throwf(TEXT("Found '%s' - expected a '>' or ','."), AllocatorToken.Identifier); - } - - if (FCString::Strcmp(AllocatorToken.Identifier, TEXT("FMemoryImageSetAllocator")) == 0) - { - if (EnumHasAnyFlags(Flags, CPF_Net)) - { - FError::Throwf(TEXT("Replicated maps with MemoryImageSetAllocators are not yet supported")); - } - - RequireSymbol(TEXT('>'), TEXT("TMap template arguments"), ESymbolParseOption::CloseTemplateBracket); - - VarProperty.AllocatorType = EAllocatorType::MemoryImage; - } - else - { - FError::Throwf(TEXT("Found '%s' - explicit allocators are not supported in TMap properties."), AllocatorToken.Identifier); - } - } - } - else if ( VarType.Matches(TEXT("TSet"), ESearchCase::CaseSensitive) ) - { - RequireSymbol( TEXT('<'), TEXT("'tset'") ); - - VarType.PropertyFlags = Flags; - - GetVarType(AllClasses, Scope, VarProperty, Disallow, &VarType, EPropertyDeclarationStyle::None, VariableCategory); - if (VarProperty.IsContainer()) - { - FError::Throwf(TEXT("Nested containers are not supported.") ); - } - // TODO: Prevent sparse delegate types from being used in a container - - if (VarProperty.Type == CPT_Interface) - { - FError::Throwf(TEXT("UINTERFACEs are not currently supported as element types.")); - } - - if (VarProperty.Type == CPT_Text) - { - FError::Throwf(TEXT("FText is not currently supported as an element type.")); - } - - VarType.PropertyFlags = VarProperty.PropertyFlags & (CPF_ContainsInstancedReference | CPF_InstancedReference); // propagate these to the set, we will fix them later - VarProperty.ArrayType = EArrayType::Set; - - FToken CloseTemplateToken; - if (!GetToken(CloseTemplateToken, /*bNoConsts=*/ true, ESymbolParseOption::CloseTemplateBracket)) - { - FError::Throwf(TEXT("Missing token while parsing TArray.")); - } - - if (CloseTemplateToken.TokenType != TOKEN_Symbol || !CloseTemplateToken.Matches(TEXT('>'))) - { - // If we didn't find a comma, report it - if (!CloseTemplateToken.Matches(TEXT(','))) - { - FError::Throwf(TEXT("Expected '>' but found '%s'"), CloseTemplateToken.Identifier); - } - - // If we found a comma, read the next thing, assume it's a keyfuncs, 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 KeyFuncs are not supported in TSet properties."), AllocatorToken.Identifier); - } - } - else if ( VarType.Matches(TEXT("FString"), ESearchCase::CaseSensitive) || VarType.Matches(TEXT("FMemoryImageString"), ESearchCase::CaseSensitive)) - { - 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"), ESearchCase::IgnoreCase) ) - { - FError::Throwf(TEXT("%s' is missing a prefix, expecting 'FText'"), VarType.Identifier); - } - else if ( VarType.Matches(TEXT("FText"), ESearchCase::CaseSensitive) ) - { - VarProperty = FPropertyBase(CPT_Text); - } - else if (VarType.Matches(TEXT("TEnumAsByte"), ESearchCase::CaseSensitive)) - { - RequireSymbol(TEXT('<'), VarType.Identifier); - - // Eat the forward declaration enum text if present - MatchIdentifier(TEXT("enum"), ESearchCase::CaseSensitive); - - bool bFoundEnum = false; - - FToken InnerEnumType; - if (GetIdentifier(InnerEnumType, true)) - { - if (UEnum* Enum = FindObject(ANY_PACKAGE, InnerEnumType.Identifier)) - { - // In-scope enumeration. - VarProperty = FPropertyBase(Enum, CPT_Byte); - bFoundEnum = true; - } - } - - // 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.") ); - } - } - - if (!bFoundEnum) - { - FError::Throwf(TEXT("Expected the name of a previously defined enum")); - } - - RequireSymbol(TEXT('>'), VarType.Identifier, ESymbolParseOption::CloseTemplateBracket); - } - else if (VarType.Matches(TEXT("TFieldPath"), ESearchCase::CaseSensitive )) - { - RequireSymbol( TEXT('<'), TEXT("'TFieldPath'") ); - - FFieldClass* PropertyClass = nullptr; - FToken PropertyTypeToken; - if (!GetToken(PropertyTypeToken, /* bNoConsts = */ true)) - { - FError::Throwf(TEXT("Expected the property type")); - } - else - { - FFieldClass** PropertyClassPtr = FFieldClass::GetNameToFieldClassMap().Find(PropertyTypeToken.Identifier + 1); - if (PropertyClassPtr) - { - PropertyClass = *PropertyClassPtr; - } - else - { - FError::Throwf(TEXT("Undefined property type: %s"), PropertyTypeToken.Identifier); - } - } - - RequireSymbol(TEXT('>'), VarType.Identifier, ESymbolParseOption::CloseTemplateBracket); - - VarProperty = FPropertyBase(PropertyClass, CPT_FieldPath); - } - else if (UEnum* Enum = FindObject(ANY_PACKAGE, VarType.Identifier)) - { - EPropertyType UnderlyingType = CPT_Byte; - - if (VariableCategory == EVariableCategory::Member) - { - // UHTLite - FRWScopeLock Lock(EnumUnderlyingTypesLock, SLT_ReadOnly); - - EUnderlyingEnumType* EnumUnderlyingType = EnumUnderlyingTypes.Find(Enum); - if (!EnumUnderlyingType) - { - 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."), *Enum->CppType); - } - } - - // 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. - VarProperty = FPropertyBase(Enum, UnderlyingType); - bUnconsumedEnumKeyword = false; - } - else - { - // Check for structs/classes - bool bHandledType = false; - FString IdentifierStripped = GetClassNameWithPrefixRemoved(VarType.Identifier); - bool bStripped = false; - UScriptStruct* Struct = FindObject( ANY_PACKAGE, VarType.Identifier ); - if (!Struct) - { - Struct = FindObject( ANY_PACKAGE, *IdentifierStripped ); - bStripped = true; - } - - auto SetDelegateType = [&](UFunction* InFunction, const FString& InIdentifierStripped) - { - bHandledType = true; - - VarProperty = FPropertyBase(InFunction->HasAnyFunctionFlags(FUNC_MulticastDelegate) ? CPT_MulticastDelegate : CPT_Delegate); - VarProperty.DelegateName = *InIdentifierStripped; - VarProperty.Function = InFunction; - - if (!(Disallow & CPF_InstancedReference)) - { - Flags |= CPF_InstancedReference; - } - }; - - if (!Struct && MatchSymbol(TEXT("::"))) - { - FToken DelegateName; - if (GetIdentifier(DelegateName)) - { - UClass* LocalOwnerClass = AllClasses.FindClass(*IdentifierStripped); - if (LocalOwnerClass) - { - TSharedRef LocScope = FScope::GetTypeScope(LocalOwnerClass); - const FString DelegateIdentifierStripped = GetClassNameWithPrefixRemoved(DelegateName.Identifier); - if (UFunction* DelegateFunc = Cast(LocScope->FindTypeByName(*(DelegateIdentifierStripped + HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX)))) - { - SetDelegateType(DelegateFunc, DelegateIdentifierStripped); - VarProperty.DelegateSignatureOwnerClass = LocalOwnerClass; - } - } - else - { - FError::Throwf(TEXT("Cannot find class '%s', to resolve delegate '%s'"), *IdentifierStripped, DelegateName.Identifier); - } - } - } - - if (bHandledType) - { - } - else 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; - } - else if ( FindObject( ANY_PACKAGE, *IdentifierStripped ) != nullptr) - { - bHandledType = true; - - // Struct keyword in front of a struct is legal, we 'consume' it - bUnconsumedStructKeyword = false; - } - else if (UFunction* DelegateFunc = Cast(Scope->FindTypeByName(*(IdentifierStripped + HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX)))) - { - SetDelegateType(DelegateFunc, IdentifierStripped); - } - else - { - // An object reference of some type (maybe a restricted class?) - UClass* TempClass = NULL; - - const bool bIsLazyPtrTemplate = VarType.Matches(TEXT("TLazyObjectPtr"), ESearchCase::CaseSensitive); - const bool bIsSoftObjectPtrTemplate = VarType.Matches(TEXT("TSoftObjectPtr"), ESearchCase::CaseSensitive); - const bool bIsSoftClassPtrTemplate = VarType.Matches(TEXT("TSoftClassPtr"), ESearchCase::CaseSensitive); - const bool bIsWeakPtrTemplate = VarType.Matches(TEXT("TWeakObjectPtr"), ESearchCase::CaseSensitive); - const bool bIsAutoweakPtrTemplate = VarType.Matches(TEXT("TAutoWeakObjectPtr"), ESearchCase::CaseSensitive); - const bool bIsScriptInterfaceWrapper = VarType.Matches(TEXT("TScriptInterface"), ESearchCase::CaseSensitive); - - bool bIsWeak = false; - bool bIsLazy = false; - bool bIsSoft = false; - bool bWeakIsAuto = false; - - if (VarType.Matches(TEXT("TSubclassOf"), ESearchCase::CaseSensitive)) - { - TempClass = UClass::StaticClass(); - } - else if (VarType.Matches(TEXT("FScriptInterface"), ESearchCase::CaseSensitive)) - { - TempClass = UInterface::StaticClass(); - Flags |= CPF_UObjectWrapper; - } - else if (bIsSoftClassPtrTemplate) - { - TempClass = UClass::StaticClass(); - bIsSoft = true; - } - else if (bIsLazyPtrTemplate || bIsWeakPtrTemplate || bIsAutoweakPtrTemplate || bIsScriptInterfaceWrapper || bIsSoftObjectPtrTemplate) - { - RequireSymbol(TEXT('<'), VarType.Identifier); - - // Consume a forward class declaration 'class' if present - MatchIdentifier(TEXT("class"), ESearchCase::CaseSensitive); - - // Also consume const - bNativeConstTemplateArg |= MatchIdentifier(TEXT("const"), ESearchCase::CaseSensitive); - - // Find the lazy/weak class - FToken InnerClass; - if (GetIdentifier(InnerClass)) - { - RedirectTypeIdentifier(InnerClass); - - TempClass = AllClasses.FindScriptClass(InnerClass.Identifier); - if (TempClass == nullptr) - { - FError::Throwf(TEXT("Unrecognized type '%s' (in expression %s<%s>) - type must be a UCLASS"), InnerClass.Identifier, VarType.Identifier, InnerClass.Identifier); - } - - if (bIsAutoweakPtrTemplate) - { - bIsWeak = true; - bWeakIsAuto = true; - } - else if (bIsLazyPtrTemplate) - { - bIsLazy = true; - } - else if (bIsWeakPtrTemplate) - { - bIsWeak = true; - } - else if (bIsSoftObjectPtrTemplate) - { - bIsSoft = true; - } - - Flags |= CPF_UObjectWrapper; - } - else - { - FError::Throwf(TEXT("%s: Missing template type"), VarType.Identifier); - } - - RequireSymbol(TEXT('>'), VarType.Identifier, ESymbolParseOption::CloseTemplateBracket); - } - else - { - TempClass = AllClasses.FindScriptClass(VarType.Identifier); - } - - 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 - VarProperty = FPropertyBase(TempClass, bAllowWeak && bIsWeak, bWeakIsAuto, bIsLazy, bIsSoft); - if (TempClass->IsChildOf(UClass::StaticClass())) - { - if ( MatchSymbol(TEXT('<')) ) - { - Flags |= CPF_UObjectWrapper; - - // Consume a forward class declaration 'class' if present - MatchIdentifier(TEXT("class"), ESearchCase::CaseSensitive); - - // Get the actual class type to restrict this to - FToken Limitor; - if( !GetIdentifier(Limitor) ) - { - FError::Throwf(TEXT("'class': Missing class limitor")); - } - - RedirectTypeIdentifier(Limitor); - - VarProperty.MetaClass = AllClasses.FindScriptClassOrThrow(Limitor.Identifier); - - RequireSymbol( TEXT('>'), TEXT("'class limitor'"), ESymbolParseOption::CloseTemplateBracket ); - } - 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 (bIsSoftObjectPtrTemplate) - { - FError::Throwf(TEXT("Class variables cannot be stored in TSoftObjectPtr, use TSoftClassPtr 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 - if (!(Flags & CPF_UObjectWrapper)) - { - // Const after variable type but before pointer symbol - bNativeConst |= MatchIdentifier(TEXT("const"), ESearchCase::CaseSensitive); - - RequireSymbol(TEXT('*'), TEXT("Expected a pointer type")); - - // Swallow trailing 'const' after pointer properties - if (VariableCategory == EVariableCategory::Member) - { - MatchIdentifier(TEXT("const"), ESearchCase::CaseSensitive); - } - - 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) - { - if (UFunction* DelegateFunc = (UFunction*)StaticFindObject(UFunction::StaticClass(), ANY_PACKAGE, *(IdentifierStripped + HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX))) - { - SetDelegateType(DelegateFunc, IdentifierStripped); - } - - if (!bHandledType) - { - FError::Throwf(TEXT("Unrecognized type '%s' - type must be a UCLASS, USTRUCT or UENUM"), VarType.Identifier ); - } - } - } - - if (VariableCategory != EVariableCategory::Member) - { - // const after the variable type support (only for params) - if (MatchIdentifier(TEXT("const"), ESearchCase::CaseSensitive)) - { - Flags |= CPF_ConstParm; - bNativeConst = true; - } - } - - if (bUnconsumedConstKeyword) - { - 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); - } - } - - 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('*'))) - { - FError::Throwf(TEXT("Inappropriate '*' on variable of type '%s', cannot have an exposed pointer to this type."), VarType.Identifier ); - } - - //@TODO: UCREMOVAL: 'const' member variables that will get written post-construction by defaultproperties - if (VariableCategory == EVariableCategory::Member && OwnerStruct->IsA() && ((UClass*)OwnerStruct)->HasAnyClassFlags(CLASS_Const)) - { - // Eat a 'not quite truthful' const after the type; autogenerated for member variables of const classes. - bNativeConst |= MatchIdentifier(TEXT("const"), ESearchCase::CaseSensitive); - } - - // 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)) - { - FError::Throwf(TEXT("Replicated %s parameters cannot be passed by non-const reference"), VarType.Identifier); - } - - Flags |= CPF_ReferenceParm; - } - 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 | ImpliedFlags; - VarProperty.ImpliedPropertyFlags |= ImpliedFlags; - - // 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 - if (VarProperty.PropertyFlags & CPF_PersistentInstance) - { - if (VarProperty.Type == CPT_ObjectReference) - { - if (VarProperty.PropertyClass->IsChildOf()) - { - FError::Throwf(TEXT("'Instanced' cannot be applied to class properties (UClass* or TSubclassOf<>)")); - } - } - else - { - FError::Throwf(TEXT("'Instanced' is only allowed on an object property, an array of objects, a set of objects, or a map with an object value type.")); - } - } - - if ( VarProperty.IsObject() && VarProperty.Type != CPT_SoftObjectReference && VarProperty.MetaClass == nullptr && (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) - { - FError::Throwf(TEXT("'BlueprintCallable' is only allowed on a property when it is a multicast delegate")); - } - - if ((VarProperty.PropertyFlags & CPF_BlueprintAuthorityOnly) && VarProperty.Type != CPT_MulticastDelegate) - { - FError::Throwf(TEXT("'BlueprintAuthorityOnly' is only allowed on a property when it is a multicast delegate")); - } - - 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 - EPropertyFlags Transients = VarProperty.PropertyFlags & (CPF_DuplicateTransient | CPF_TextExportTransient | CPF_NonPIEDuplicateTransient); - if (Transients && !Cast(OwnerStruct)) - { - TArray 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") ); - } - - // For now, copy the flags that a TMap value has to the key - if (FPropertyBase* KeyProp = VarProperty.MapKeyProp.Get()) - { - // Make sure the 'UObjectWrapper' flag is maintained so that both 'TMap, ...>' and 'TMap>' works correctly - KeyProp->PropertyFlags = (VarProperty.PropertyFlags & ~CPF_UObjectWrapper) | (KeyProp->PropertyFlags & CPF_UObjectWrapper); - } - - VarProperty.MetaData = MetaDataFromNewStyle; - if (bNativeConst) - { - VarProperty.MetaData.Add(NAME_NativeConst, FString()); - } - if (bNativeConstTemplateArg) - { - VarProperty.MetaData.Add(NAME_NativeConstTemplateArg, FString()); - } - - if (ParsedVarIndexRange) - { - ParsedVarIndexRange->Count = InputPos - ParsedVarIndexRange->StartIndex; - } -} - -/** - * 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 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(MoveTemp(UpperPropertyStr),1); - FName CurrentText(PropertyName,FNAME_Find); // keep generating this FName in case it has been affecting the case of future FNames. - return FNAME_Replace_Not_Safe_For_Threading; -} - -FProperty* FHeaderParser::GetVarNameAndDim -( - UStruct* Scope, - FToken& VarProperty, - EVariableCategory::Type VariableCategory, - ELayoutMacroType LayoutMacroType -) -{ - check(Scope); - - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - EObjectFlags ObjectFlags = RF_Public; - if (VariableCategory == EVariableCategory::Member && CurrentAccessSpecifier == ACCESS_Private) - { - ObjectFlags = RF_NoFlags; - } - - const TCHAR* HintText = GetHintText(VariableCategory); - - AddModuleRelativePathToMetadata(Scope, VarProperty.MetaData); - - // Get variable name. - if (VariableCategory == EVariableCategory::Return) - { - // Hard-coded variable name, such as with return value. - VarProperty.TokenType = TOKEN_Identifier; - FCString::Strcpy( VarProperty.Identifier, TEXT("ReturnValue") ); - } - else - { - FToken VarToken; - if (!GetIdentifier(VarToken)) - { - FError::Throwf(TEXT("Missing variable name") ); - } - - switch (LayoutMacroType) - { - case ELayoutMacroType::Array: - case ELayoutMacroType::ArrayEditorOnly: - case ELayoutMacroType::Bitfield: - case ELayoutMacroType::BitfieldEditorOnly: - case ELayoutMacroType::FieldInitialized: - RequireSymbol(TEXT(','), GLayoutMacroNames[(int32)LayoutMacroType]); - break; - - default: - break; - } - - 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); - - const int32 DeprecatedIndex = VarName.Find(TEXT("_DEPRECATED")); - const int32 NativizedPropertyPostfixIndex = VarName.Find(TEXT("__pf")); //TODO: check OverrideNativeName in Meta Data, to be sure it's not a random occurrence of the "__pf" string. - bool bIgnoreDeprecatedWord = (NativizedPropertyPostfixIndex != INDEX_NONE) && (NativizedPropertyPostfixIndex > DeprecatedIndex); - if ((DeprecatedIndex != INDEX_NONE) && !bIgnoreDeprecatedWord) - { - if (DeprecatedIndex != VarName.Len() - 11) - { - FError::Throwf(TEXT("Deprecated variables must end with _DEPRECATED")); - } - - // We allow deprecated properties in blueprints that have getters and setters assigned as they may be part of a backwards compatibility path - const bool bBlueprintVisible = (VarProperty.PropertyFlags & CPF_BlueprintVisible) > 0; - const bool bWarnOnGetter = bBlueprintVisible && !VarProperty.MetaData.Contains(NAME_BlueprintGetter); - const bool bWarnOnSetter = bBlueprintVisible && !(VarProperty.PropertyFlags & CPF_BlueprintReadOnly) && !VarProperty.MetaData.Contains(NAME_BlueprintSetter); - - if (bWarnOnGetter) - { - UE_LOG_WARNING_UHT(TEXT("%s: Deprecated property '%s' should not be marked as blueprint visible without having a BlueprintGetter"), HintText, *VarName); - } - - if (bWarnOnSetter) - { - UE_LOG_WARNING_UHT(TEXT("%s: Deprecated property '%s' should not be marked as blueprint writeable without having a BlueprintSetter"), HintText, *VarName); - } - - - // Warn if a deprecated property is visible - if (VarProperty.PropertyFlags & (CPF_Edit | CPF_EditConst) || // Property is marked as editable - (!bBlueprintVisible && (VarProperty.PropertyFlags & CPF_BlueprintReadOnly) && !(VarProperty.ImpliedPropertyFlags & CPF_BlueprintReadOnly)) ) // Is BPRO, but not via Implied Flags and not caught by Getter/Setter path above - { - UE_LOG_WARNING_UHT(TEXT("%s: Deprecated property '%s' should not be marked as visible or editable"), HintText, *VarName); - } - - VarProperty.PropertyFlags |= CPF_Deprecated; - VarName.MidInline(0, DeprecatedIndex, false); - - FCString::Strcpy(VarProperty.Identifier, *VarName); - } - } - - // Make sure it doesn't conflict. - int32 OuterContextCount = 0; - UField* ExistingField = FindField(Scope, VarProperty.Identifier, true, UField::StaticClass(), NULL); - FField* ExistingProperty = FindProperty(Scope, VarProperty.Identifier, true, FField::StaticClass(), NULL); - - if (ExistingField != nullptr || ExistingProperty != nullptr) - { - bool bErrorDueToShadowing = true; - - if (ExistingField && ExistingField->IsA(UFunction::StaticClass()) && (VariableCategory != EVariableCategory::Member)) - { - // A function parameter with the same name as a method is allowed - bErrorDueToShadowing = false; - } - - //@TODO: This exception does not seem sound either, but there is enough existing code that it will need to be - // fixed up first before the exception it is removed. - if (ExistingProperty) - { - FProperty* ExistingProp = CastField(ExistingProperty); - const bool bExistingPropDeprecated = (ExistingProp != nullptr) && ExistingProp->HasAnyPropertyFlags(CPF_Deprecated); - const bool bNewPropDeprecated = (VariableCategory == EVariableCategory::Member) && ((VarProperty.PropertyFlags & CPF_Deprecated) != 0); - if (bNewPropDeprecated || bExistingPropDeprecated) - { - // if this is a property and one of them is deprecated, ignore it since it will be removed soon - bErrorDueToShadowing = false; - } - } - - if (bErrorDueToShadowing) - { - FError::Throwf(TEXT("%s: '%s' cannot be defined in '%s' as it is already defined in scope '%s' (shadowing is not allowed)"), - HintText, - VarProperty.Identifier, - *Scope->GetName(), - ExistingField ? *ExistingField->GetOuter()->GetName() : *ExistingProperty->GetOwnerVariant().GetFullName()); - } - } - - // Get optional dimension immediately after name. - FToken Dimensions; - if ((LayoutMacroType == ELayoutMacroType::None && MatchSymbol(TEXT('['))) || LayoutMacroType == ELayoutMacroType::Array || LayoutMacroType == ELayoutMacroType::ArrayEditorOnly) - { - switch (VariableCategory) - { - case EVariableCategory::Return: - { - FError::Throwf(TEXT("Arrays aren't allowed as return types")); - } - - case EVariableCategory::RegularParameter: - case EVariableCategory::ReplicatedParameter: - { - FError::Throwf(TEXT("Arrays aren't allowed as function parameters")); - } - } - - if (VarProperty.IsContainer()) - { - FError::Throwf(TEXT("Static arrays of containers are not allowed")); - } - - if (VarProperty.IsBool()) - { - FError::Throwf(TEXT("Bool arrays are not allowed") ); - } - - if (LayoutMacroType == ELayoutMacroType::None) - { - // 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); - } - } - else - { - // 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 = nullptr; - - if (*Dimensions.String) - { - FString Temp = Dimensions.String; - - bool bAgain; - do - { - bAgain = false; - - // Remove any casts - static const TCHAR* Casts[] = { - TEXT("(uint32)"), - TEXT("(int32)"), - TEXT("(uint16)"), - TEXT("(int16)"), - TEXT("(uint8)"), - TEXT("(int8)"), - TEXT("(int)"), - TEXT("(unsigned)"), - TEXT("(signed)"), - TEXT("(unsigned int)"), - TEXT("(signed int)") - }; - - // Remove any brackets - if (Temp[0] == TEXT('(')) - { - int32 TempLen = Temp.Len(); - int32 ClosingParen = FindMatchingClosingParenthesis(Temp); - if (ClosingParen == TempLen - 1) - { - Temp.MidInline(1, TempLen - 2, false); - bAgain = true; - } - } - - for (const TCHAR* Cast : Casts) - { - if (Temp.StartsWith(Cast, ESearchCase::CaseSensitive)) - { - Temp.RightChopInline(FCString::Strlen(Cast), false); - bAgain = true; - } - } - } - while (bAgain); - - UEnum::LookupEnumNameSlow(*Temp, &Enum); - } - - if (!Enum) - { - // If the enum wasn't declared in this scope, then try to find it anywhere we can - Enum = FindObject(ANY_PACKAGE, Dimensions.String); - } - - if (Enum) - { - // set the ArraySizeEnum if applicable - VarProperty.MetaData.Add(NAME_ArraySizeEnum, Enum->GetPathName()); - } - - if (LayoutMacroType == ELayoutMacroType::None) - { - MatchSymbol(TEXT(']')); - } - } - - // Try gathering metadata for member fields - if (VariableCategory == EVariableCategory::Member) - { - ParseFieldMetaData(VarProperty.MetaData, VarProperty.Identifier); - AddFormattedPrevCommentAsTooltipMetaData(VarProperty.MetaData); - } - // validate UFunction parameters - else - { - // 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) - { - FError::Throwf(TEXT("UFunctions cannot take a lazy pointer as a parameter.")); - } - } - - // 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); - - FProperty* Prev = nullptr; - for (TFieldIterator It(Scope, EFieldIteratorFlags::ExcludeSuper); It; ++It) - { - Prev = *It; - } - - auto PropagateFlagsFromInnerAndHandlePersistentInstanceMetadata = [](EPropertyFlags& DestFlags, const TMap& InMetaData, FProperty* Inner) { - // Copy some of the property flags to the container property. - if (Inner->PropertyFlags & (CPF_ContainsInstancedReference | CPF_InstancedReference)) - { - DestFlags |= CPF_ContainsInstancedReference; - DestFlags &= ~(CPF_InstancedReference | CPF_PersistentInstance); //this was propagated to the inner - - if (Inner->PropertyFlags & CPF_PersistentInstance) - { - AddMetaDataToClassData(Inner, TMap(InMetaData)); - } - } - }; - - FProperty* Result = nullptr; - if (VarProperty.ArrayType == EArrayType::Dynamic) - { - FArrayProperty* Array = new FArrayProperty(Scope, PropertyName, ObjectFlags); - FProperty* InnerProp = CreateVariableProperty(VarProperty, Array, PropertyName, RF_Public, VariableCategory, CurrentSrcFile); - - Array->Inner = InnerProp; - Array->PropertyFlags = VarProperty.PropertyFlags; - - // Propagate flags - InnerProp->PropertyFlags |= Array->PropertyFlags & CPF_PropagateToArrayInner; - - PropagateFlagsFromInnerAndHandlePersistentInstanceMetadata(Array->PropertyFlags, VarProperty.MetaData, InnerProp); - - Result = Array; - - if (VarProperty.AllocatorType == EAllocatorType::MemoryImage) - { - PropertyUsesMemoryImageAllocator.Add(Array); - } - } - else if (VarProperty.ArrayType == EArrayType::Set) - { - FSetProperty* Set = new FSetProperty(Scope, PropertyName, ObjectFlags); - FProperty* InnerProp = CreateVariableProperty(VarProperty, Set, PropertyName, RF_Public, VariableCategory, CurrentSrcFile); - - Set->ElementProp = InnerProp; - Set->PropertyFlags = VarProperty.PropertyFlags; - - // Propagate flags - InnerProp->PropertyFlags |= Set->PropertyFlags & CPF_PropagateToSetElement; - - PropagateFlagsFromInnerAndHandlePersistentInstanceMetadata(Set->PropertyFlags, VarProperty.MetaData, InnerProp); - - Result = Set; - } - else if (VarProperty.MapKeyProp.IsValid()) - { - FMapProperty* Map = new FMapProperty(Scope, PropertyName, ObjectFlags); - FProperty* KeyProp = CreateVariableProperty(*VarProperty.MapKeyProp, Map, *(PropertyName.ToString() + TEXT("_Key")), RF_Public, VariableCategory, CurrentSrcFile); - FProperty* ValueProp = CreateVariableProperty(VarProperty, Map, PropertyName, RF_Public, VariableCategory, CurrentSrcFile); - - Map->KeyProp = KeyProp; - Map->ValueProp = ValueProp; - Map->PropertyFlags = VarProperty.PropertyFlags; - - // Propagate flags - KeyProp ->PropertyFlags |= VarProperty.MapKeyProp->PropertyFlags & CPF_PropagateToMapKey; - ValueProp->PropertyFlags |= Map->PropertyFlags & CPF_PropagateToMapValue; - - PropagateFlagsFromInnerAndHandlePersistentInstanceMetadata(Map->PropertyFlags, VarProperty.MapKeyProp->MetaData, KeyProp); - PropagateFlagsFromInnerAndHandlePersistentInstanceMetadata(Map->PropertyFlags, VarProperty.MetaData, ValueProp); - - Result = Map; - - if (VarProperty.AllocatorType == EAllocatorType::MemoryImage) - { - PropertyUsesMemoryImageAllocator.Add(Map); - } - } - else - { - Result = CreateVariableProperty(VarProperty, Scope, PropertyName, ObjectFlags, VariableCategory, CurrentSrcFile); - - if (VarProperty.ArrayType == EArrayType::Static) - { - Result->ArrayDim = 2; // 2 = static array - ArrayDimensions.Add(Result, Dimensions.String); - } - - Result->PropertyFlags = VarProperty.PropertyFlags; - } - - if (Prev != nullptr) - { - Result->Next = Prev->Next; - Prev->Next = Result; - } - else - { - Result->Next = Scope->ChildProperties; - Scope->ChildProperties = Result; - } - - VarProperty.TokenProperty = Result; - VarProperty.StartLine = InputLine; - VarProperty.StartPos = InputPos; - FClassMetaData* ScopeData = GScriptHelper.FindClassData(Scope); - check(ScopeData); - ScopeData->AddProperty(FToken(VarProperty), CurrentSrcFile); - - // if we had any metadata, add it to the class - AddMetaDataToClassData(VarProperty.TokenProperty, TMap(VarProperty.MetaData)); - - return Result; -} - -/*----------------------------------------------------------------------------- - Statement compiler. ------------------------------------------------------------------------------*/ - -// -// Compile a declaration in Token. Returns 1 if compiled, 0 if not. -// -bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, TArray& DelegatesToFixup, FToken& Token) -{ - EAccessSpecifier AccessSpecifier = ParseAccessProtectionSpecifier(Token); - if (AccessSpecifier) - { - if (!IsAllowedInThisNesting(ENestAllowFlags::VarDecl) && !IsAllowedInThisNesting(ENestAllowFlags::Function)) - { - FError::Throwf(TEXT("Access specifier %s not allowed here."), Token.Identifier); - } - check(TopNest->NestType == ENestType::Class || TopNest->NestType == ENestType::Interface || TopNest->NestType == ENestType::NativeInterface); - CurrentAccessSpecifier = AccessSpecifier; - return true; - } - - if (Token.Matches(TEXT("class"), ESearchCase::CaseSensitive) && (TopNest->NestType == ENestType::GlobalScope)) - { - // Make sure the previous class ended with valid nesting. - if (bEncounteredNewStyleClass_UnmatchedBrackets) - { - FError::Throwf(TEXT("Missing } at end of class")); - } - - // Start parsing the second class - bEncounteredNewStyleClass_UnmatchedBrackets = true; - CurrentAccessSpecifier = ACCESS_Private; - - if (!TryParseIInterfaceClass(AllClasses)) - { - bEncounteredNewStyleClass_UnmatchedBrackets = false; - UngetToken(Token); - return SkipDeclaration(Token); - } - return true; - } - - if (Token.Matches(TEXT("GENERATED_IINTERFACE_BODY"), ESearchCase::CaseSensitive) || (Token.Matches(TEXT("GENERATED_BODY"), ESearchCase::CaseSensitive) && TopNest->NestType == ENestType::NativeInterface)) - { - if (TopNest->NestType != ENestType::NativeInterface) - { - FError::Throwf(TEXT("%s must occur inside the native interface definition"), Token.Identifier); - } - RequireSymbol(TEXT('('), Token.Identifier); - CompileVersionDeclaration(GetCurrentClass()); - RequireSymbol(TEXT(')'), Token.Identifier); - - FClassMetaData* ClassData = GetCurrentClassData(); - if (!ClassData || ClassData->ParsedInterface == EParsedInterface::NotAnInterface) - { - FString CurrentClassName = GetCurrentClass()->GetName(); - FError::Throwf(TEXT("Could not find the associated 'U%s' class while parsing 'I%s' - it could be missing or malformed"), *CurrentClassName, *CurrentClassName); - } - - if (ClassData->ParsedInterface == EParsedInterface::ParsedIInterface) - { - FString CurrentClassName = GetCurrentClass()->GetName(); - FError::Throwf(TEXT("Duplicate IInterface definition found while parsing 'I%s'"), *CurrentClassName); - } - - check(ClassData->ParsedInterface == EParsedInterface::ParsedUInterface); - - ClassData->ParsedInterface = EParsedInterface::ParsedIInterface; - ClassData->GeneratedBodyMacroAccessSpecifier = CurrentAccessSpecifier; - ClassData->SetInterfaceGeneratedBodyLine(InputLine); - - bClassHasGeneratedIInterfaceBody = true; - - if (Token.Matches(TEXT("GENERATED_IINTERFACE_BODY"), ESearchCase::CaseSensitive)) - { - CurrentAccessSpecifier = ACCESS_Public; - } - - if (Token.Matches(TEXT("GENERATED_BODY"), ESearchCase::CaseSensitive)) - { - ClassDefinitionRanges[GetCurrentClass()].bHasGeneratedBody = true; - } - return true; - } - - if (Token.Matches(TEXT("GENERATED_UINTERFACE_BODY"), ESearchCase::CaseSensitive) || (Token.Matches(TEXT("GENERATED_BODY"), ESearchCase::CaseSensitive) && TopNest->NestType == ENestType::Interface)) - { - if (TopNest->NestType != ENestType::Interface) - { - FError::Throwf(TEXT("%s must occur inside the interface definition"), Token.Identifier); - } - RequireSymbol(TEXT('('), Token.Identifier); - CompileVersionDeclaration(GetCurrentClass()); - RequireSymbol(TEXT(')'), Token.Identifier); - - FClassMetaData* ClassData = GetCurrentClassData(); - - ClassData->GeneratedBodyMacroAccessSpecifier = CurrentAccessSpecifier; - ClassData->SetGeneratedBodyLine(InputLine); - - bClassHasGeneratedUInterfaceBody = true; - - if (Token.Matches(TEXT("GENERATED_UINTERFACE_BODY"), ESearchCase::CaseSensitive)) - { - CurrentAccessSpecifier = ACCESS_Public; - } - return true; - } - - if (Token.Matches(TEXT("GENERATED_UCLASS_BODY"), ESearchCase::CaseSensitive) || (Token.Matches(TEXT("GENERATED_BODY"), ESearchCase::CaseSensitive) && TopNest->NestType == ENestType::Class)) - { - if (TopNest->NestType != ENestType::Class) - { - FError::Throwf(TEXT("%s must occur inside the class definition"), Token.Identifier); - } - - FClassMetaData* ClassData = GetCurrentClassData(); - - if (Token.Matches(TEXT("GENERATED_BODY"), ESearchCase::CaseSensitive)) - { - if (!ClassDefinitionRanges.Contains(GetCurrentClass())) - { - ClassDefinitionRanges.Add(GetCurrentClass(), ClassDefinitionRange()); - } - - ClassDefinitionRanges[GetCurrentClass()].bHasGeneratedBody = true; - - ClassData->GeneratedBodyMacroAccessSpecifier = CurrentAccessSpecifier; - } - else - { - CurrentAccessSpecifier = ACCESS_Public; - } - - RequireSymbol(TEXT('('), Token.Identifier); - CompileVersionDeclaration(GetCurrentClass()); - RequireSymbol(TEXT(')'), Token.Identifier); - - ClassData->SetGeneratedBodyLine(InputLine); - - bClassHasGeneratedBody = true; - return true; - } - - if (Token.Matches(TEXT("UCLASS"), ESearchCase::CaseSensitive)) - { - bHaveSeenUClass = true; - bEncounteredNewStyleClass_UnmatchedBrackets = true; - UClass* Class = CompileClassDeclaration(AllClasses); - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - GStructToSourceLine.Add(Class, MakeTuple(GetCurrentSourceFile()->AsShared(), Token.StartLine)); - */ - - return true; - } - - if (Token.Matches(TEXT("UINTERFACE"), ESearchCase::CaseSensitive)) - { - bHaveSeenUClass = true; - bEncounteredNewStyleClass_UnmatchedBrackets = true; - CompileInterfaceDeclaration(AllClasses); - return true; - } - - if (Token.Matches(TEXT("UFUNCTION"), ESearchCase::CaseSensitive)) - { - CompileFunctionDeclaration(AllClasses); - return true; - } - - if (Token.Matches(TEXT("UDELEGATE"), ESearchCase::CaseSensitive)) - { - UDelegateFunction* Delegate = CompileDelegateDeclaration(AllClasses, Token.Identifier, EDelegateSpecifierAction::Parse); - DelegatesToFixup.Add(Delegate); - return true; - } - - if (IsValidDelegateDeclaration(Token)) // Legacy delegate parsing - it didn't need a UDELEGATE - { - UDelegateFunction* Delegate = CompileDelegateDeclaration(AllClasses, Token.Identifier); - DelegatesToFixup.Add(Delegate); - return true; - } - - if (Token.Matches(TEXT("UPROPERTY"), ESearchCase::CaseSensitive)) - { - CheckAllow(TEXT("'Member variable declaration'"), ENestAllowFlags::VarDecl); - check(TopNest->NestType == ENestType::Class); - - CompileVariableDeclaration(AllClasses, GetCurrentClass()); - return true; - } - - if (Token.Matches(TEXT("UENUM"), ESearchCase::CaseSensitive)) - { - // Enumeration definition. - CompileEnum(); - return true; - } - - if (Token.Matches(TEXT("USTRUCT"), ESearchCase::CaseSensitive)) - { - // Struct definition. - UScriptStruct* Struct = CompileStructDeclaration(AllClasses); - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - GStructToSourceLine.Add(Struct, MakeTuple(GetCurrentSourceFile()->AsShared(), Token.StartLine)); - */ - - return true; - } - - if (Token.Matches(TEXT('#'))) - { - // Compiler directive. - CompileDirective(AllClasses); - return true; - } - - if (bEncounteredNewStyleClass_UnmatchedBrackets && Token.Matches(TEXT('}'))) - { - if (ClassDefinitionRanges.Contains(GetCurrentClass())) - { - ClassDefinitionRanges[GetCurrentClass()].End = &Input[InputPos]; - } - 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; - - UClass* CurrentClass = GetCurrentClass(); - - // Pop nesting here to allow other non UClass declarations in the header file. - if (CurrentClass->ClassFlags & CLASS_Interface) - { - checkf(TopNest->NestType == ENestType::Interface || TopNest->NestType == ENestType::NativeInterface, TEXT("Unexpected end of interface block.")); - PopNest(TopNest->NestType, TEXT("'Interface'")); - PostPopNestInterface(AllClasses, CurrentClass); - - // Ensure the UINTERFACE classes have a GENERATED_BODY declaration - if (bHaveSeenUClass && !bClassHasGeneratedUInterfaceBody) - { - FError::Throwf(TEXT("Expected a GENERATED_BODY() at the start of class")); - } - - // Ensure the non-UINTERFACE interface classes have a GENERATED_BODY declaration - if (!bHaveSeenUClass && !bClassHasGeneratedIInterfaceBody) - { - FError::Throwf(TEXT("Expected a GENERATED_BODY() at the start of class")); - } - } - else - { - PopNest(ENestType::Class, TEXT("'Class'")); - PostPopNestClass(CurrentClass); - - // Ensure classes have a GENERATED_BODY declaration - if (bHaveSeenUClass && !bClassHasGeneratedBody) - { - FError::Throwf(TEXT("Expected a GENERATED_BODY() at the start of class")); - } - } - - bHaveSeenUClass = false; - bClassHasGeneratedBody = false; - bClassHasGeneratedUInterfaceBody = false; - bClassHasGeneratedIInterfaceBody = false; - - GetCurrentScope()->AddType(CurrentClass); - return true; - } - - if (Token.Matches(TEXT(';'))) - { - if (GetToken(Token)) - { - FError::Throwf(TEXT("Extra ';' before '%s'"), Token.Identifier); - } - else - { - FError::Throwf(TEXT("Extra ';' before end of file")); - } - } - - if (bEncounteredNewStyleClass_UnmatchedBrackets && IsInAClass()) - { - if (UClass* Class = GetCurrentClass()) - { - FToken ConstructorToken = Token; - - // Allow explicit constructors - bool bFoundExplicit = ConstructorToken.Matches(TEXT("explicit"), ESearchCase::CaseSensitive); - if (bFoundExplicit) - { - GetToken(ConstructorToken); - } - - bool bSkippedAPIToken = false; - if (FString(ConstructorToken.Identifier).EndsWith(TEXT("_API"), ESearchCase::CaseSensitive)) - { - if (!bFoundExplicit) - { - // Explicit can come before or after an _API - MatchIdentifier(TEXT("explicit"), ESearchCase::CaseSensitive); - } - - GetToken(ConstructorToken); - bSkippedAPIToken = true; - } - - if (ConstructorToken.Matches(*FNameLookupCPP::GetNameCPP(Class), ESearchCase::IgnoreCase)) - { - if (TryToMatchConstructorParameterList(ConstructorToken)) - { - return true; - } - } - else if (bSkippedAPIToken) - { - // We skipped over an _API token, but this wasn't a constructor so we need to unget so that subsequent code and still process it - UngetToken(ConstructorToken); - } - } - } - - // Skip anything that looks like a macro followed by no bracket that we don't know about - if (ProbablyAnUnknownObjectLikeMacro(*this, Token)) - { - return true; - } - - // Determine if this statement is a serialize function declaration - if (bEncounteredNewStyleClass_UnmatchedBrackets && IsInAClass() && TopNest->NestType == ENestType::Class) - { - while (Token.Matches(TEXT("virtual"), ESearchCase::CaseSensitive) || FString(Token.Identifier).EndsWith(TEXT("_API"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - } - - if (Token.Matches(TEXT("void"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - if (Token.Matches(TEXT("Serialize"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - if (Token.Matches(TEXT('('))) - { - GetToken(Token); - - ESerializerArchiveType ArchiveType = ESerializerArchiveType::None; - if (Token.Matches(TEXT("FArchive"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - if (Token.Matches(TEXT('&'))) - { - GetToken(Token); - - // Allow the declaration to not define a name for the archive parameter - if (!Token.Matches(TEXT(')'))) - { - GetToken(Token); - } - - if (Token.Matches(TEXT(')'))) - { - ArchiveType = ESerializerArchiveType::Archive; - } - } - } - else if (Token.Matches(TEXT("FStructuredArchive"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - if (Token.Matches(TEXT("::"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - - if (Token.Matches(TEXT("FRecord"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - - // Allow the declaration to not define a name for the slot parameter - if (!Token.Matches(TEXT(')'))) - { - GetToken(Token); - } - - if (Token.Matches(TEXT(')'))) - { - ArchiveType = ESerializerArchiveType::StructuredArchiveRecord; - } - } - } - } - else if (Token.Matches(TEXT("FStructuredArchiveRecord"), ESearchCase::CaseSensitive)) - { - GetToken(Token); - - // Allow the declaration to not define a name for the slot parameter - if (!Token.Matches(TEXT(')'))) - { - GetToken(Token); - } - - if (Token.Matches(TEXT(')'))) - { - ArchiveType = ESerializerArchiveType::StructuredArchiveRecord; - } - } - - if (ArchiveType != ESerializerArchiveType::None) - { - // Found what we want! - if (CompilerDirectiveStack.Num() == 0 || (CompilerDirectiveStack.Num() == 1 && CompilerDirectiveStack[0] == ECompilerDirective::WithEditorOnlyData)) - { - FString EnclosingDefine = CompilerDirectiveStack.Num() > 0 ? TEXT("WITH_EDITORONLY_DATA") : TEXT(""); - - UClass* CurrentClass = GetCurrentClass(); - - { - // UHTLite - FRWScopeLock Lock(ClassSerializerMapLock, SLT_Write); - - ClassSerializerMap.Add(CurrentClass, { ArchiveType, MoveTemp(EnclosingDefine) }); - } - } - else - { - FError::Throwf(TEXT("Serialize functions must not be inside preprocessor blocks, except for WITH_EDITORONLY_DATA")); - } - } - } - } - } - } - - // Ignore C++ declaration / function definition. - return SkipDeclaration(Token); -} - -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. - int32 NestedScopes = 0; - // 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"), ESearchCase::CaseSensitive) || Token.Matches(TEXT("struct"), ESearchCase::CaseSensitive); - // (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(TEXT("DECLARE_FUNCTION"), ESearchCase::CaseSensitive); - bool bEndOfDeclarationFound = false; - bool bDefinitionFound = false; - TCHAR OpeningBracket = bMacroDeclaration ? TEXT('(') : TEXT('{'); - 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(); - if (bPossiblyClassDeclaration && NestedScopes == 0 && Token.Matches(TEXT('('))) - { - bPossiblyClassDeclaration = false; - } - - bRetestCurrentToken = false; - if (Token.Matches(TEXT(';')) && NestedScopes == 0) - { - bEndOfDeclarationFound = true; - break; - } - - if (!bMacroDeclaration && Token.Matches(TEXT("PURE_VIRTUAL"), ESearchCase::CaseSensitive) && NestedScopes == 0) - { - OpeningBracket = TEXT('('); - ClosingBracket = TEXT(')'); - } - - if (Token.Matches(OpeningBracket)) - { - // This is a function definition or class declaration. - bDefinitionFound = true; - NestedScopes++; - } - else if (Token.Matches(ClosingBracket)) - { - NestedScopes--; - if (NestedScopes == 0) - { - // Could be a class declaration in all capitals, and not a macro - bool bReallyEndDeclaration = true; - if (bMacroDeclaration) - { - FToken PossibleBracketToken; - GetToken(PossibleBracketToken); - UngetToken(Token); - GetToken(Token); - - // If Strcmp returns 0, it is probably a class, else a macro. - bReallyEndDeclaration = !PossibleBracketToken.Matches(TEXT('{')); - } - - if (bReallyEndDeclaration) - { - bEndOfDeclarationFound = true; - break; - } - } - - if (NestedScopes < 0) - { - FError::Throwf(TEXT("Unexpected '}'. Did you miss a semi-colon?")); - } - } - else if (bMacroDeclaration && NestedScopes == 0) - { - 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); - } - else if (!SafeMatchSymbol(TEXT(';'))) - { - FError::Throwf(*FString::Printf(TEXT("Unexpected '%s'. Did you miss a semi-colon?"), VariableName.Identifier)); - } - } - - // 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. - return NestedScopes == 0 && bEndOfDeclarationFound; -} - -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 && Token.Identifier[0] == Match && Token.Identifier[1] == 0) - { - return true; - } - - UngetToken(Token); - } - // Return to the stored position. - ReturnToLocation(LocationBeforeNextSymbol); - - return false; -} - -FClass* FHeaderParser::ParseClassNameDeclaration(FClasses& AllClasses, FString& DeclaredClassName, FString& RequiredAPIMacroIfPresent) -{ - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - ParseNameWithPotentialAPIMacroPrefix(/*out*/ DeclaredClassName, /*out*/ RequiredAPIMacroIfPresent, TEXT("class")); - - FClass* FoundClass = AllClasses.FindClass(*GetClassNameWithPrefixRemoved(*DeclaredClassName)); - check(FoundClass); - - FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(FoundClass, CurrentSrcFile); - - // Get parent class. - bool bSpecifiesParentClass = false; - - // Skip optional final keyword - MatchIdentifier(TEXT("final"), ESearchCase::CaseSensitive); - - if (MatchSymbol(TEXT(':'))) - { - RequireIdentifier(TEXT("public"), ESearchCase::CaseSensitive, TEXT("class inheritance")); - bSpecifiesParentClass = true; - } - - // Add class cast flag - FoundClass->ClassCastFlags |= ClassCastFlagMap::Get().GetCastFlag(DeclaredClassName); - - if (bSpecifiesParentClass) - { - // Set the base class. - UClass* TempClass = GetQualifiedClass(AllClasses, TEXT("'extends'")); - check(TempClass); - // a class cannot 'extends' an interface, use 'implements' - if (TempClass->ClassFlags & CLASS_Interface) - { - FError::Throwf(TEXT("Class '%s' cannot extend interface '%s', use 'implements'"), *FoundClass->GetName(), *TempClass->GetName()); - } - - UClass* SuperClass = FoundClass->GetSuperClass(); - if( SuperClass == NULL ) - { - FoundClass->SetSuperStruct(TempClass); - } - else if( SuperClass != TempClass ) - { - FError::Throwf(TEXT("%s's superclass must be %s, not %s"), *FoundClass->GetPathName(), *SuperClass->GetPathName(), *TempClass->GetPathName()); - } - - FoundClass->ClassCastFlags |= FoundClass->GetSuperClass()->ClassCastFlags; - - // Handle additional inherited interface classes - while (MatchSymbol(TEXT(','))) - { - RequireIdentifier(TEXT("public"), ESearchCase::CaseSensitive, TEXT("Interface inheritance must be public")); - - FString InterfaceName; - - FToken Token; - for (;;) - { - if (!GetIdentifier(Token, true)) - { - FError::Throwf(TEXT("Failed to get interface class identifier")); - } - - InterfaceName += Token.Identifier; - - // Handle templated native classes - if (MatchSymbol(TEXT('<'))) - { - InterfaceName += TEXT('<'); - - int32 NestedScopes = 1; - while (NestedScopes) - { - if (!GetToken(Token)) - { - FError::Throwf(TEXT("Unexpected end of file")); - } - - if (Token.TokenType == TOKEN_Symbol) - { - if (Token.Matches(TEXT('<'))) - { - ++NestedScopes; - } - else if (Token.Matches(TEXT('>'))) - { - --NestedScopes; - } - } - - InterfaceName += Token.Identifier; - } - } - - // Handle scoped native classes - if (MatchSymbol(TEXT("::"))) - { - InterfaceName += TEXT("::"); - - // Keep reading nested identifiers - continue; - } - - break; - } - - HandleOneInheritedClass(AllClasses, FoundClass, MoveTemp(InterfaceName)); - } - } - else if (FoundClass->GetSuperClass()) - { - FError::Throwf(TEXT("class: missing 'Extends %s'"), *FoundClass->GetSuperClass()->GetName()); - } - - return FoundClass; -} - -void FHeaderParser::HandleOneInheritedClass(FClasses& AllClasses, UClass* Class, FString&& InterfaceName) -{ - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - // Check for UInterface derived interface inheritance - if (UClass* Interface = AllClasses.FindScriptClass(InterfaceName)) - { - // 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)) - { - FClassMetaData* ClassData = GScriptHelper.FindClassData(Class); - check(ClassData); - ClassData->AddInheritanceParent(Interface, CurrentSrcFile); - } - } - else - { - // Non-UObject inheritance - FClassMetaData* ClassData = GScriptHelper.FindClassData(Class); - check(ClassData); - ClassData->AddInheritanceParent(MoveTemp(InterfaceName), CurrentSrcFile); - } -} - -/** - * 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 - auto HasAllOptimizationClassFlags = [Class]() - { - return (Class->HasAllClassFlags(CLASS_Config | CLASS_HasInstancedReference)); - }; - - if (!HasAllOptimizationClassFlags()) - { - for (TFieldIterator It(Class, EFieldIteratorFlags::ExcludeSuper); It; ++It) - { - if ((It->PropertyFlags & CPF_Config) != 0) - { - Class->ClassFlags |= CLASS_Config; - if (HasAllOptimizationClassFlags()) break; - } - - if (It->ContainsInstancedObjectProperty()) - { - Class->ClassFlags |= CLASS_HasInstancedReference; - if (HasAllOptimizationClassFlags()) break; - } - } - } - - // 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; - } - } -} - -/** - * Compiles a class declaration. - */ -UClass* FHeaderParser::CompileClassDeclaration(FClasses& AllClasses) -{ - // Start of a class block. - CheckAllow(TEXT("'class'"), ENestAllowFlags::Class); - - // New-style UCLASS() syntax - TMap MetaData; - - TArray SpecifiersFound; - ReadSpecifierSetInsideMacro(SpecifiersFound, TEXT("Class"), MetaData); - - const int32 PrologFinishLine = InputLine; - - // Members of classes have a default private access level in c++ - // Setting this directly should be ok as we don't support nested classes, so the outer scope access should not need restoring - CurrentAccessSpecifier = ACCESS_Private; - - AddFormattedPrevCommentAsTooltipMetaData(MetaData); - - // New style files have the class name / extends afterwards - RequireIdentifier(TEXT("class"), ESearchCase::CaseSensitive, TEXT("Class declaration")); - - // alignas() can come before or after the deprecation macro. - // We can't have both, but the compiler will catch that anyway. - SkipAlignasIfNecessary(*this); - SkipDeprecatedMacroIfNecessary(*this); - SkipAlignasIfNecessary(*this); - - FString DeclaredClassName; - FString RequiredAPIMacroIfPresent; - - FClass* Class = ParseClassNameDeclaration(AllClasses, /*out*/ DeclaredClassName, /*out*/ RequiredAPIMacroIfPresent); - check(Class); - FClassDeclarationMetaData* ClassDeclarationData = &ClassDeclarations.FindChecked(Class->GetFName()); - - ClassDefinitionRanges.Add(Class, ClassDefinitionRange(&Input[InputPos], nullptr)); - - check(Class->ClassFlags == 0 || (Class->ClassFlags & ClassDeclarationData->ClassFlags) != 0); - - Class->ClassFlags |= CLASS_Parsed; - - PushNest(ENestType::Class, Class); - - const uint32 PrevClassFlags = Class->ClassFlags; - ResetClassData(); - - // Verify class variables haven't been filled in - check(Class->Children == NULL); - check(Class->Next == NULL); - check(Class->NetFields.Num() == 0); - check(Class->FirstOwnedClassRep == 0); - - // Make sure our parent classes is parsed. - for (UClass* Temp = Class->GetSuperClass(); Temp; Temp = Temp->GetSuperClass()) - { - bool bIsParsed = !!(Temp->ClassFlags & CLASS_Parsed); - bool bIsIntrinsic = !!(Temp->ClassFlags & CLASS_Intrinsic); - if (!(bIsParsed || bIsIntrinsic)) - { - FError::Throwf(TEXT("'%s' can't be compiled: Parent class '%s' has errors"), *Class->GetName(), *Temp->GetName()); - } - } - - // Merge with categories inherited from the parent. - ClassDeclarationData->MergeClassCategories(Class); - - // Class attributes. - FClassMetaData* ClassData = GScriptHelper.FindClassData(Class); - check(ClassData); - ClassData->SetPrologLine(PrologFinishLine); - - ClassDeclarationData->MergeAndValidateClassFlags(DeclaredClassName, PrevClassFlags, Class, AllClasses); - Class->SetInternalFlags(EInternalObjectFlags::Native); - - // Class metadata - MetaData.Append(ClassDeclarationData->MetaData); - if (ClassDeclarationData->ClassGroupNames.Num()) { MetaData.Add(NAME_ClassGroupNames, FString::Join(ClassDeclarationData->ClassGroupNames, TEXT(" "))); } - if (ClassDeclarationData->AutoCollapseCategories.Num()) { MetaData.Add(NAME_AutoCollapseCategories, FString::Join(ClassDeclarationData->AutoCollapseCategories, TEXT(" "))); } - if (ClassDeclarationData->HideCategories.Num()) { MetaData.Add(FHeaderParserNames::NAME_HideCategories, FString::Join(ClassDeclarationData->HideCategories, TEXT(" "))); } - if (ClassDeclarationData->ShowSubCatgories.Num()) { MetaData.Add(FHeaderParserNames::NAME_ShowCategories, FString::Join(ClassDeclarationData->ShowSubCatgories, TEXT(" "))); } - if (ClassDeclarationData->SparseClassDataTypes.Num()) { MetaData.Add(FHeaderParserNames::NAME_SparseClassDataTypes, FString::Join(ClassDeclarationData->SparseClassDataTypes, TEXT(" "))); } - if (ClassDeclarationData->HideFunctions.Num()) { MetaData.Add(NAME_HideFunctions, FString::Join(ClassDeclarationData->HideFunctions, TEXT(" "))); } - if (ClassDeclarationData->AutoExpandCategories.Num()) { MetaData.Add(NAME_AutoExpandCategories, FString::Join(ClassDeclarationData->AutoExpandCategories, TEXT(" "))); } - - AddIncludePathToMetadata(Class, MetaData); - AddModuleRelativePathToMetadata(Class, MetaData); - - // Register the metadata - AddMetaDataToClassData(Class, MoveTemp(MetaData)); - - // Handle the start of the rest of the class - RequireSymbol( TEXT('{'), TEXT("'Class'") ); - - // Make visible outside the package. - Class->ClearFlags(RF_Transient); - check(Class->HasAnyFlags(RF_Public)); - check(Class->HasAnyFlags(RF_Standalone)); - - // Copy properties from parent class. - if (Class->GetSuperClass()) - { - Class->SetPropertiesSize(Class->GetSuperClass()->GetPropertiesSize()); - } - - // auto-create properties for all of the VFTables needed for the multiple inheritances - // get the inheritance parents - const TArray& 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 FProperty in the class's Interfaces map now... - if (UClass* InheritedInterface = InheritanceParents[ParentIndex]->InterfaceClass) - { - FImplementedInterface* Found = Class->Interfaces.FindByPredicate([=](const FImplementedInterface& Impl) { return Impl.Class == InheritedInterface; }); - if (Found) - { - Found->PointerOffset = 1; - } - else - { - Class->Interfaces.Add(FImplementedInterface(InheritedInterface, 1, false)); - } - } - } - - // Validate sparse class data - CheckSparseClassData(Class); - - return Class; -} - -FClass* FHeaderParser::ParseInterfaceNameDeclaration(FClasses& AllClasses, FString& DeclaredInterfaceName, FString& RequiredAPIMacroIfPresent) -{ - ParseNameWithPotentialAPIMacroPrefix(/*out*/ DeclaredInterfaceName, /*out*/ RequiredAPIMacroIfPresent, TEXT("interface")); - - FClass* FoundClass = AllClasses.FindClass(*GetClassNameWithPrefixRemoved(*DeclaredInterfaceName)); - if (FoundClass == nullptr) - { - return nullptr; - } - - // Get super interface - bool bSpecifiesParentClass = MatchSymbol(TEXT(':')); - if (!bSpecifiesParentClass) - { - return FoundClass; - } - - RequireIdentifier(TEXT("public"), ESearchCase::CaseSensitive, 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 - UClass* TempClass = GetQualifiedClass(AllClasses, TEXT("'extends'")); - check(TempClass); - 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() ); - } - - UClass* SuperClass = FoundClass->GetSuperClass(); - if (SuperClass == NULL) - { - FoundClass->SetSuperStruct(TempClass); - } - else if (SuperClass != TempClass) - { - FError::Throwf(TEXT("%s's superclass must be %s, not %s"), *FoundClass->GetPathName(), *SuperClass->GetPathName(), *TempClass->GetPathName()); - } - - return FoundClass; -} - -bool FHeaderParser::TryParseIInterfaceClass(FClasses& AllClasses) -{ - // 'class' was already matched by the caller - - // Get a class name - FString DeclaredInterfaceName; - FString RequiredAPIMacroIfPresent; - if (ParseInterfaceNameDeclaration(AllClasses, /*out*/ DeclaredInterfaceName, /*out*/ RequiredAPIMacroIfPresent) == nullptr) - { - 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; - } - - // Continue parsing the second class as if it were a part of the first (for reflection data purposes, it is) - RequireSymbol(TEXT('{'), TEXT("C++ interface mix-in class declaration")); - - // Push the interface class nesting again. - PushNest(ENestType::NativeInterface, FoundClass); - - return true; -} - -/** - * compiles Java or C# style interface declaration - */ -void FHeaderParser::CompileInterfaceDeclaration(FClasses& AllClasses) -{ - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - // 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'"), ENestAllowFlags::Class ); - - FString DeclaredInterfaceName; - FString RequiredAPIMacroIfPresent; - TMap MetaData; - - // Build up a list of interface specifiers - TArray SpecifiersFound; - - // New-style UINTERFACE() syntax - ReadSpecifierSetInsideMacro(SpecifiersFound, TEXT("Interface"), MetaData); - - int32 PrologFinishLine = InputLine; - - // New style files have the interface name / extends afterwards - RequireIdentifier(TEXT("class"), ESearchCase::CaseSensitive, TEXT("Interface declaration")); - FClass* InterfaceClass = ParseInterfaceNameDeclaration(AllClasses, /*out*/ DeclaredInterfaceName, /*out*/ RequiredAPIMacroIfPresent); - ClassDefinitionRanges.Add(InterfaceClass, ClassDefinitionRange(&Input[InputPos], nullptr)); - - // Record that this interface is RequiredAPI if the CORE_API style macro was present - if (!RequiredAPIMacroIfPresent.IsEmpty()) - { - InterfaceClass->ClassFlags |= CLASS_RequiredAPI; - } - - // Set the appropriate interface class flags - InterfaceClass->ClassFlags |= CLASS_Interface | CLASS_Abstract; - if (InterfaceClass->GetSuperClass() != NULL) - { - InterfaceClass->ClassCastFlags |= InterfaceClass->GetSuperClass()->ClassCastFlags; - } - - // All classes that are parsed are expected to be native - if (InterfaceClass->GetSuperClass() && !InterfaceClass->GetSuperClass()->HasAnyClassFlags(CLASS_Native)) - { - FError::Throwf(TEXT("Native classes cannot extend non-native classes") ); - } - - InterfaceClass->SetInternalFlags(EInternalObjectFlags::Native); - InterfaceClass->ClassFlags |= CLASS_Native; - - // Process all of the interface specifiers - for (const FPropertySpecifier& Specifier : SpecifiersFound) - { - switch ((EInterfaceSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GInterfaceSpecifierStrings)) - { - default: - { - FError::Throwf(TEXT("Unknown interface specifier '%s'"), *Specifier.Key); - } - break; - - case EInterfaceSpecifier::DependsOn: - { - FError::Throwf(TEXT("The dependsOn specifier is deprecated. Please use #include \"ClassHeaderFilename.h\" instead.")); - } - break; - - case EInterfaceSpecifier::MinimalAPI: - { - InterfaceClass->ClassFlags |= CLASS_MinimalAPI; - } - break; - - case EInterfaceSpecifier::ConversionRoot: - { - MetaData.Add(FHeaderParserNames::NAME_IsConversionRoot, TEXT("true")); - } - break; - } - } - - // All classes must start with a valid Unreal prefix - const FString ExpectedInterfaceName = InterfaceClass->GetNameWithPrefix(EEnforceInterfacePrefix::U); - 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 - FClassMetaData* ClassData = GScriptHelper.AddInterfaceClassData(InterfaceClass, CurrentSrcFile); - check(ClassData); - - ClassData->SetPrologLine(PrologFinishLine); - - // Register the metadata - AddModuleRelativePathToMetadata(InterfaceClass, MetaData); - AddMetaDataToClassData(InterfaceClass, MoveTemp(MetaData)); - - // Handle the start of the rest of the interface - RequireSymbol( TEXT('{'), TEXT("'Class'") ); - - // Make visible outside the package. - InterfaceClass->ClearFlags(RF_Transient); - check(InterfaceClass->HasAnyFlags(RF_Public)); - check(InterfaceClass->HasAnyFlags(RF_Standalone)); - - // Push the interface class nesting. - // we need a more specific set of allow flags for ENestType::Interface, only function declaration is allowed, no other stuff are allowed - PushNest(ENestType::Interface, InterfaceClass); -} - -void FHeaderParser::CompileRigVMMethodDeclaration(FClasses& AllClasses, UStruct* Struct) -{ - if (!MatchSymbol(TEXT("("))) - { - FError::Throwf(TEXT("Bad RIGVM_METHOD definition")); - } - - // find the next close brace - while (!MatchSymbol(TEXT(")"))) - { - FToken Token; - if (!GetToken(Token)) - { - break; - } - } - - FToken PrefixToken, ReturnTypeToken, NameToken, PostfixToken; - if (!GetToken(PrefixToken)) - { - return; - } - - if (FString(PrefixToken.Identifier).Equals(TEXT("virtual"))) - { - if (!GetToken(ReturnTypeToken)) - { - return; - } - } - else - { - ReturnTypeToken = PrefixToken; - } - - if (!GetToken(NameToken)) - { - return; - } - - if (!MatchSymbol(TEXT("("))) - { - FError::Throwf(TEXT("Bad RIGVM_METHOD definition")); - } - - TArray ParamsContent; - while (!MatchSymbol(TEXT(")"))) - { - FToken Token; - if (!GetToken(Token)) - { - break; - } - ParamsContent.Add(FString(Token.Identifier)); - } - - while (!FString(PostfixToken.Identifier).Equals(TEXT(";"))) - { - if (!GetToken(PostfixToken)) - { - return; - } - } - - FRigVMMethodInfo MethodInfo; - MethodInfo.ReturnType = ReturnTypeToken.Identifier; - MethodInfo.Name = NameToken.Identifier; - - FString ParamString = FString::Join(ParamsContent, TEXT(" ")); - if (!ParamString.IsEmpty()) - { - FString ParamPrev, ParamLeft, ParamRight; - ParamPrev = ParamString; - while (ParamPrev.Contains(TEXT(","))) - { - ParamPrev.Split(TEXT(","), &ParamLeft, &ParamRight); - FRigVMParameter Parameter; - Parameter.Name = ParamLeft.TrimStartAndEnd(); - MethodInfo.Parameters.Add(Parameter); - ParamPrev = ParamRight; - } - - ParamPrev = ParamPrev.TrimStartAndEnd(); - if (!ParamPrev.IsEmpty()) - { - FRigVMParameter Parameter; - Parameter.Name = ParamPrev.TrimStartAndEnd(); - MethodInfo.Parameters.Add(Parameter); - } - } - - for (FRigVMParameter& Parameter : MethodInfo.Parameters) - { - FString FullParameter = Parameter.Name; - - int32 LastEqual = INDEX_NONE; - if (FullParameter.FindLastChar(TCHAR('='), LastEqual)) - { - FullParameter = FullParameter.Mid(0, LastEqual); - } - - FullParameter.TrimStartAndEndInline(); - - FString ParameterType = FullParameter; - FString ParameterName = FullParameter; - - int32 LastSpace = INDEX_NONE; - if (FullParameter.FindLastChar(TCHAR(' '), LastSpace)) - { - Parameter.Type = FullParameter.Mid(0, LastSpace); - Parameter.Name = FullParameter.Mid(LastSpace + 1); - Parameter.Type.TrimStartAndEndInline(); - Parameter.Name.TrimStartAndEndInline(); - } - } - - FRigVMStructInfo& StructRigVMInfo = StructRigVMMap.FindOrAdd(Struct); - StructRigVMInfo.Name = Struct->GetName(); - StructRigVMInfo.Methods.Add(MethodInfo); -} - -static const FName NAME_InputText(TEXT("Input")); -static const FName NAME_OutputText(TEXT("Output")); -static const FName NAME_ConstantText(TEXT("Constant")); -static const FName NAME_MaxArraySizeText(TEXT("MaxArraySize")); - -static const TCHAR* TArrayText = TEXT("TArray"); -static const TCHAR* TArrayViewText = TEXT("TArrayView"); -static const TCHAR* GetRefText = TEXT("GetRef"); -static const TCHAR* GetArrayText = TEXT("GetArray"); - -void FHeaderParser::ParseRigVMMethodParameters(UStruct* Struct) -{ - FRigVMStructInfo* StructRigVMInfo = StructRigVMMap.Find(Struct); - if (StructRigVMInfo == nullptr) - { - return; - } - - // validate the property types for this struct - for (TFieldIterator It(Struct); It; ++It) - { - FProperty const* const Prop = *It; - FString MemberCPPType; - FString ExtendedCPPType; - MemberCPPType = Prop->GetCPPType(&ExtendedCPPType); - - FRigVMParameter Parameter; - Parameter.Name = Prop->GetName(); - Parameter.Type = MemberCPPType + ExtendedCPPType; - Parameter.bConstant = Prop->HasMetaData(NAME_ConstantText); - Parameter.bInput = Prop->HasMetaData(NAME_InputText); - Parameter.bOutput = Prop->HasMetaData(NAME_OutputText); - Parameter.MaxArraySize = Prop->GetMetaData(NAME_MaxArraySizeText); - Parameter.Getter = GetRefText; - Parameter.bEditorOnly = Prop->IsEditorOnlyProperty(); - - if (Parameter.bEditorOnly) - { - UE_LOG_ERROR_UHT(TEXT("RigVM Struct '%s' - Member '%s' is editor only - WITH_EDITORONLY_DATA not allowed on structs with RIGVM_METHOD."), *Struct->GetName(), *Parameter.Name, *MemberCPPType); - } - - if (!ExtendedCPPType.IsEmpty()) - { - // we only support arrays - no maps or similar data structures - if (MemberCPPType != TArrayText) - { - UE_LOG_ERROR_UHT(TEXT("RigVM Struct '%s' - Member '%s' type '%s' not supported by RigVM."), *Struct->GetName(), *Parameter.Name, *MemberCPPType); - continue; - } - - if (!Parameter.IsConst() && Parameter.MaxArraySize.IsEmpty()) - { - UE_LOG_ERROR_UHT(TEXT("RigVM Struct '%s' - Member '%s' requires the 'MaxArraySize' meta tag."), *Struct->GetName(), *Parameter.Name); - continue; - } - } - - if (MemberCPPType.StartsWith(TArrayText, ESearchCase::CaseSensitive)) - { - if (Parameter.IsConst() || !Parameter.MaxArraySize.IsEmpty()) - { - Parameter.CastName = FString::Printf(TEXT("%s_%d_View"), *Parameter.Name, StructRigVMInfo->Members.Num()); - Parameter.CastType = FString::Printf(TEXT("%s%s"), TArrayViewText, *ExtendedCPPType); - Parameter.Getter = GetArrayText; - } - } - - StructRigVMInfo->Members.Add(MoveTemp(Parameter)); - } - - if (StructRigVMInfo->Members.Num() == 0) - { - UE_LOG_ERROR_UHT(TEXT("RigVM Struct '%s' - has zero members - invalid RIGVM_METHOD."), *Struct->GetName()); - } - - if (StructRigVMInfo->Members.Num() > 64) - { - UE_LOG_ERROR_UHT(TEXT("RigVM Struct '%s' - has %d members (64 is the limit)."), *Struct->GetName(), StructRigVMInfo->Members.Num()); - } -} - -// Returns true if the token is a dynamic delegate declaration -bool FHeaderParser::IsValidDelegateDeclaration(const FToken& Token) const -{ - return (Token.TokenType == TOKEN_Identifier) && !FCString::Strncmp(Token.Identifier, TEXT("DECLARE_DYNAMIC_"), 16); -} - -// Modify token to fix redirected types if needed -void FHeaderParser::RedirectTypeIdentifier(FToken& Token) const -{ - check(Token.TokenType == TOKEN_Identifier); - - const FString* FoundRedirect = TypeRedirectMap.Find(Token.Identifier); - if (FoundRedirect) - { - Token.SetIdentifier(**FoundRedirect); - } -} - -// Parse the parameter list of a function or delegate declaration -void FHeaderParser::ParseParameterList(FClasses& AllClasses, UFunction* Function, bool bExpectCommaBeforeName, TMap* MetaData) -{ - // Get parameter list. - if (MatchSymbol(TEXT(')'))) - { - return; - } - - FAdvancedDisplayParameterHandler AdvancedDisplay(MetaData); - do - { - // Get parameter type. - FToken Property(CPT_None); - EVariableCategory::Type VariableCategory = (Function->FunctionFlags & FUNC_Net) ? EVariableCategory::ReplicatedParameter : EVariableCategory::RegularParameter; - GetVarType(AllClasses, GetCurrentScope(), Property, ~(CPF_ParmFlags | CPF_AutoWeak | CPF_RepSkip | CPF_UObjectWrapper | CPF_NativeAccessSpecifiers), NULL, EPropertyDeclarationStyle::None, VariableCategory); - Property.PropertyFlags |= CPF_Parm; - - if (bExpectCommaBeforeName) - { - RequireSymbol(TEXT(','), TEXT("Delegate definitions require a , between the parameter type and parameter name")); - } - - FProperty* Prop = GetVarNameAndDim(Function, Property, VariableCategory); - - 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) - { - UE_LOG_ERROR_UHT(TEXT("Replicated functions cannot contain out parameters")); - } - - if (Property.PropertyFlags & CPF_RepSkip) - { - UE_LOG_ERROR_UHT(TEXT("Only service request functions cannot contain NoReplication parameters")); - } - - if ((Prop->GetCastFlags() & CASTCLASS_FDelegateProperty) != 0) - { - UE_LOG_ERROR_UHT(TEXT("Replicated functions cannot contain delegate parameters (this would be insecure)")); - } - - if (Property.Type == CPT_String && Property.RefQualifier != ERefQualifier::ConstRef && Prop->ArrayDim == 1) - { - UE_LOG_ERROR_UHT(TEXT("Replicated FString parameters must be passed by const reference")); - } - - if (Property.ArrayType == EArrayType::Dynamic && Property.RefQualifier != ERefQualifier::ConstRef && Prop->ArrayDim == 1) - { - UE_LOG_ERROR_UHT(TEXT("Replicated TArray parameters must be passed by const reference")); - } - } - else - { - if (!(Property.PropertyFlags & CPF_RepSkip) && (Property.PropertyFlags & CPF_OutParm)) - { - UE_LOG_ERROR_UHT(TEXT("Service request functions cannot contain out parameters, unless marked NotReplicated")); - } - - if (!(Property.PropertyFlags & CPF_RepSkip) && (Prop->GetCastFlags() & CASTCLASS_FDelegateProperty) != 0) - { - UE_LOG_ERROR_UHT(TEXT("Service request functions cannot contain delegate parameters, unless marked NotReplicated")); - } - } - } - if ((Function->FunctionFlags & (FUNC_BlueprintEvent|FUNC_BlueprintCallable)) != 0) - { - if (Property.Type == CPT_Byte) - { - if (FEnumProperty* EnumProperty = CastField(Prop)) - { - FProperty* InnerType = EnumProperty->GetUnderlyingProperty(); - if (InnerType && !InnerType->IsA()) - { - FError::Throwf(TEXT("Invalid enum param for Blueprints - currently only uint8 supported")); - } - } - } - - // Check that the parameter name is valid and does not conflict with pre-defined types - { - const static TArray InvalidParamNames = - { - TEXT("self"), - }; - - for (const FString& InvalidName : InvalidParamNames) - { - if (Property.Matches(*InvalidName, ESearchCase::IgnoreCase)) - { - UE_LOG_ERROR_UHT(TEXT("Paramater name '%s' in function is invalid, '%s' is a reserved name."), *InvalidName, *InvalidName); - } - } - } - } - - // 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); - } - - 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") ); -} - -UDelegateFunction* FHeaderParser::CompileDelegateDeclaration(FClasses& AllClasses, const TCHAR* DelegateIdentifier, EDelegateSpecifierAction::Type SpecifierAction) -{ - const TCHAR* CurrentScopeName = TEXT("Delegate Declaration"); - - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - TMap MetaData; - AddModuleRelativePathToMetadata(*CurrentSrcFile, MetaData); - - FFuncInfo FuncInfo; - - // If this is a UDELEGATE, parse the specifiers first - FString DelegateMacro; - if (SpecifierAction == EDelegateSpecifierAction::Parse) - { - TArray SpecifiersFound; - ReadSpecifierSetInsideMacro(SpecifiersFound, TEXT("Delegate"), MetaData); - - ProcessFunctionSpecifiers(FuncInfo, SpecifiersFound, MetaData); - - // 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; - - //Workaround for UE-28897 - const FStructScope* CurrentStructScope = TopNest->GetScope() ? TopNest->GetScope()->AsStructScope() : nullptr; - const bool bDynamicClassScope = CurrentStructScope && CurrentStructScope->GetStruct() && FClass::IsDynamic(CurrentStructScope->GetStruct()); - CheckAllow(CurrentScopeName, bDynamicClassScope ? ENestAllowFlags::ImplicitDelegateDecl : ENestAllowFlags::TypeDecl); - } - else - { - DelegateMacro = DelegateIdentifier; - CheckAllow(CurrentScopeName, ENestAllowFlags::ImplicitDelegateDecl); - } - - // Break the delegate declaration macro down into parts - const bool bHasReturnValue = DelegateMacro.Contains(TEXT("_RetVal"), ESearchCase::CaseSensitive); - const bool bDeclaredConst = DelegateMacro.Contains(TEXT("_Const"), ESearchCase::CaseSensitive); - const bool bIsMulticast = DelegateMacro.Contains(TEXT("_MULTICAST"), ESearchCase::CaseSensitive); - const bool bIsSparse = DelegateMacro.Contains(TEXT("_SPARSE"), ESearchCase::CaseSensitive); - - // 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%s_DELEGATE%s%s%s"), - bIsMulticast ? TEXT("_MULTICAST") : TEXT(""), - bIsSparse ? TEXT("_SPARSE") : 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) - { - UE_LOG_ERROR_UHT(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) - { - GetVarType(AllClasses, GetCurrentScope(), ReturnType, CPF_None, nullptr, EPropertyDeclarationStyle::None, EVariableCategory::Return); - RequireSymbol(TEXT(','), CurrentScopeName); - } - - // Skip whitespaces to get InputPos exactly on beginning of function name. - while (FChar::IsWhitespace(PeekChar())) { GetChar(); } - - FuncInfo.InputPos = InputPos; - - // 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"), ESearchCase::CaseSensitive)) - { - FError::Throwf(TEXT("Delegate type declarations must start with F")); - } - - Name.RightChopInline(1, false); - - // Append the signature goo - Name += HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX; - - // Replace the name - FCString::Strcpy( FuncInfo.Function.Identifier, *Name ); - } - - UDelegateFunction* DelegateSignatureFunction = (bIsSparse ? CreateDelegateFunction(FuncInfo) : CreateDelegateFunction(FuncInfo)); - - FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(DelegateSignatureFunction, CurrentSrcFile); - - DelegateSignatureFunction->FunctionFlags |= FuncInfo.FunctionFlags; - - FuncInfo.FunctionReference = DelegateSignatureFunction; - FuncInfo.SetFunctionNames(); - if (FuncInfo.FunctionReference->HasAnyFunctionFlags(FUNC_Delegate) && !GetCurrentScope()->IsFileScope()) - { - GetCurrentClassData()->MarkContainsDelegate(); - } - - GetCurrentScope()->AddType(DelegateSignatureFunction); - - // determine whether this function should be 'const' - if (bDeclaredConst) - { - DelegateSignatureFunction->FunctionFlags |= FUNC_Const; - } - - if (bIsSparse) - { - FToken OwningClass; - - RequireSymbol(TEXT(','), TEXT("Delegate Declaration")); - - if (!GetIdentifier(OwningClass)) - { - FError::Throwf(TEXT("Missing OwningClass specifier.")); - } - RequireSymbol(TEXT(','), TEXT("Delegate Declaration")); - - FToken DelegateName; - if (!GetIdentifier(DelegateName)) - { - FError::Throwf(TEXT("Missing Delegate Name.")); - } - - USparseDelegateFunction* SDF = CastChecked(DelegateSignatureFunction); - SDF->OwningClassName = *GetClassNameWithoutPrefix(FString(OwningClass.Identifier)); - SDF->DelegateName = DelegateName.Identifier; - } - - // Get parameter list. - if (FoundParamCount) - { - RequireSymbol(TEXT(','), CurrentScopeName); - - ParseParameterList(AllClasses, DelegateSignatureFunction, /*bExpectCommaBeforeName=*/ true); - - // Check the expected versus actual number of parameters - int32 ParamCount = UE_PTRDIFF_TO_INT32(FoundParamCount - DelegateParameterCountStrings.GetData()) + 1; - 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")); - } - - // Save off function identifier in case we need it to error later, since we are moving the FuncInfo - TCHAR FuncInfoFunctionIdentifier[NAME_SIZE]; - FCString::Strncpy(FuncInfoFunctionIdentifier, FuncInfo.Function.Identifier, NAME_SIZE); - - FuncInfo.MacroLine = InputLine; - FFunctionData::Add(MoveTemp(FuncInfo)); - - // Create the return value property - if (bHasReturnValue) - { - ReturnType.PropertyFlags |= CPF_Parm | CPF_OutParm | CPF_ReturnParm; - FProperty* ReturnProp = GetVarNameAndDim(DelegateSignatureFunction, ReturnType, EVariableCategory::Return); - - DelegateSignatureFunction->NumParms++; - } - - // Try parsing metadata for the function - ParseFieldMetaData(MetaData, *(DelegateSignatureFunction->GetName())); - - AddFormattedPrevCommentAsTooltipMetaData(MetaData); - - AddMetaDataToClassData(DelegateSignatureFunction, MoveTemp(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 - PostPopFunctionDeclaration(AllClasses, DelegateSignatureFunction); - - // Don't allow delegate signatures to be redefined. - auto FunctionIterator = GetCurrentScope()->GetTypeIterator(); - while (FunctionIterator.MoveNext()) - { - UFunction* TestFunc = *FunctionIterator; - if ((TestFunc->GetFName() == DelegateSignatureFunction->GetFName()) && (TestFunc != DelegateSignatureFunction)) - { - FError::Throwf(TEXT("Can't override delegate signature function '%s'"), FuncInfoFunctionIdentifier); - } - } - - return DelegateSignatureFunction; -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -// Compares the properties of two functions to see if they have the same signature. -bool AreFunctionSignaturesEqual(const UFunction* Lhs, const UFunction* Rhs) -{ - auto LhsPropIter = TFieldIterator(Lhs); - auto RhsPropIter = TFieldIterator(Rhs); - - for (;;) - { - bool bEndOfLhsFunction = !LhsPropIter; - bool bEndOfRhsFunction = !RhsPropIter; - - if (bEndOfLhsFunction != bEndOfRhsFunction) - { - // The functions have different numbers of parameters - return false; - } - - if (bEndOfLhsFunction) - { - // We've compared all the parameters - return true; - } - - const FProperty* LhsProp = *LhsPropIter; - const FProperty* RhsProp = *RhsPropIter; - - FFieldClass* LhsClass = LhsProp->GetClass(); - FFieldClass* RhsClass = RhsProp->GetClass(); - - if (LhsClass != RhsClass) - { - // The properties have different types - return false; - } - - if (LhsClass == FArrayProperty::StaticClass()) - { - const FArrayProperty* LhsArrayProp = (const FArrayProperty*)LhsProp; - const FArrayProperty* RhsArrayProp = (const FArrayProperty*)RhsProp; - - if (LhsArrayProp->Inner->GetClass() != RhsArrayProp->Inner->GetClass()) - { - // The properties are arrays of different types - return false; - } - } - else if (LhsClass == FMapProperty::StaticClass()) - { - const FMapProperty* LhsMapProp = (const FMapProperty*)LhsProp; - const FMapProperty* RhsMapProp = (const FMapProperty*)RhsProp; - - if (LhsMapProp->KeyProp->GetClass() != RhsMapProp->KeyProp->GetClass() || LhsMapProp->ValueProp->GetClass() != RhsMapProp->ValueProp->GetClass()) - { - // The properties are maps of different types - return false; - } - } - else if (LhsClass == FSetProperty::StaticClass()) - { - const FSetProperty* LhsSetProp = (const FSetProperty*)LhsProp; - const FSetProperty* RhsSetProp = (const FSetProperty*)RhsProp; - - if (LhsSetProp->ElementProp->GetClass() != RhsSetProp->ElementProp->GetClass()) - { - // The properties are sets of different types - return false; - } - } - - ++LhsPropIter; - ++RhsPropIter; - } -} -*/ - -/** - * Parses and compiles a function declaration - */ -void FHeaderParser::CompileFunctionDeclaration(FClasses& AllClasses) -{ - CheckAllow(TEXT("'Function'"), ENestAllowFlags::Function); - - FUnrealSourceFile* CurrentSrcFile = GetCurrentSourceFile(); - TMap MetaData; - AddModuleRelativePathToMetadata(*CurrentSrcFile, MetaData); - - // New-style UFUNCTION() syntax - TArray SpecifiersFound; - ReadSpecifierSetInsideMacro(SpecifiersFound, TEXT("Function"), MetaData); - - FScriptLocation FuncNameRetry; - InitScriptLocation(FuncNameRetry); - - if (!GetCurrentClass()->HasAnyClassFlags(CLASS_Native)) - { - FError::Throwf(TEXT("Should only be here for native classes!")); - } - - // Process all specifiers. - const TCHAR* TypeOfFunction = TEXT("function"); - - bool bAutomaticallyFinal = true; - - FFuncInfo FuncInfo; - FuncInfo.MacroLine = InputLine; - FuncInfo.FunctionFlags = FUNC_Native; - - // 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 - if (GetCurrentClass()->HasAnyClassFlags(CLASS_Const)) - { - FuncInfo.FunctionFlags |= FUNC_Const; - } - - if (MatchIdentifier(TEXT("static"), ESearchCase::CaseSensitive)) - { - FuncInfo.FunctionFlags |= FUNC_Static; - FuncInfo.FunctionExportFlags |= FUNCEXPORT_CppStatic; - } - - if (MetaData.Contains(NAME_CppFromBpEvent)) - { - FuncInfo.FunctionFlags |= FUNC_Event; - } - - if (CompilerDirectiveStack.Num() > 0 && (CompilerDirectiveStack.Last()&ECompilerDirective::WithEditor) != 0) - { - FuncInfo.FunctionFlags |= FUNC_EditorOnly; - } - - ProcessFunctionSpecifiers(FuncInfo, SpecifiersFound, MetaData); - - const bool bClassGeneratedFromBP = FClass::IsDynamic(GetCurrentClass()); - - if ((0 != (FuncInfo.FunctionExportFlags & FUNCEXPORT_CustomThunk)) && !MetaData.Contains(NAME_CustomThunk)) - { - MetaData.Add(NAME_CustomThunk, TEXT("true")); - } - - if ((FuncInfo.FunctionFlags & FUNC_BlueprintPure) && GetCurrentClass()->HasAnyClassFlags(CLASS_Interface)) - { - // Until pure interface casts are supported, we don't allow pures in interfaces - UE_LOG_ERROR_UHT(TEXT("BlueprintPure specifier is not allowed for interface functions")); - } - - if (FuncInfo.FunctionFlags & FUNC_Net) - { - // Network replicated functions are always events, and are only final if sealed - TypeOfFunction = TEXT("event"); - bAutomaticallyFinal = false; - } - - if (FuncInfo.FunctionFlags & FUNC_BlueprintEvent) - { - TypeOfFunction = (FuncInfo.FunctionFlags & FUNC_Native) ? TEXT("BlueprintNativeEvent") : TEXT("BlueprintImplementableEvent"); - bAutomaticallyFinal = false; - } - - bool bSawVirtual = false; - - if (MatchIdentifier(TEXT("virtual"), ESearchCase::CaseSensitive)) - { - bSawVirtual = true; - } - - FString* InternalPtr = MetaData.Find(NAME_BlueprintInternalUseOnly); // FBlueprintMetadata::MD_BlueprintInternalUseOnly - const bool bInternalOnly = InternalPtr && *InternalPtr == TEXT("true"); - - // If this function is blueprint callable or blueprint pure, require a category - if ((FuncInfo.FunctionFlags & (FUNC_BlueprintCallable | FUNC_BlueprintPure)) != 0) - { - const bool bDeprecated = MetaData.Contains(NAME_DeprecatedFunction); // FBlueprintMetadata::MD_DeprecatedFunction - const bool bBlueprintAccessor = MetaData.Contains(NAME_BlueprintSetter) || MetaData.Contains(NAME_BlueprintGetter); // FBlueprintMetadata::MD_BlueprintSetter, // FBlueprintMetadata::MD_BlueprintGetter - const bool bHasMenuCategory = MetaData.Contains(NAME_Category); // FBlueprintMetadata::MD_FunctionCategory - - if (!bHasMenuCategory && !bInternalOnly && !bDeprecated && !bBlueprintAccessor) - { - // To allow for quick iteration, don't enforce the requirement that game functions have to be categorized - if (bIsCurrentModulePartOfEngine) - { - UE_LOG_ERROR_UHT(TEXT("An explicit Category specifier is required for Blueprint accessible functions in an Engine module.")); - } - } - } - - // Verify interfaces with respect to their blueprint accessible functions - if (GetCurrentClass()->HasAnyClassFlags(CLASS_Interface)) - { - if((FuncInfo.FunctionFlags & FUNC_BlueprintEvent) != 0 && !bInternalOnly) - { - const bool bCanImplementInBlueprints = !GetCurrentClass()->HasMetaData(NAME_CannotImplementInterfaceInBlueprint); //FBlueprintMetadata::MD_CannotImplementInterfaceInBlueprint - - // Ensure that blueprint events are only allowed in implementable interfaces. Internal only functions allowed - if (!bCanImplementInBlueprints) - { - UE_LOG_ERROR_UHT(TEXT("Interfaces that are not implementable in blueprints cannot have BlueprintImplementableEvent members.")); - } - } - - if (((FuncInfo.FunctionFlags & FUNC_BlueprintCallable) != 0) && (((~FuncInfo.FunctionFlags) & FUNC_BlueprintEvent) != 0)) - { - const bool bCanImplementInBlueprints = !GetCurrentClass()->HasMetaData(NAME_CannotImplementInterfaceInBlueprint); //FBlueprintMetadata::MD_CannotImplementInterfaceInBlueprint - - // Ensure that if this interface contains blueprint callable functions that are not blueprint defined, that it must be implemented natively - if (bCanImplementInBlueprints) - { - UE_LOG_ERROR_UHT(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 - FString APIMacroIfPresent; - { - FToken Token; - if (GetToken(Token, true)) - { - bool bThrowTokenBack = true; - if (Token.TokenType == TOKEN_Identifier) - { - FString RequiredAPIMacroIfPresent(Token.Identifier); - if (RequiredAPIMacroIfPresent.EndsWith(TEXT("_API"), ESearchCase::CaseSensitive)) - { - //@TODO: Validate the module name for RequiredAPIMacroIfPresent - bThrowTokenBack = false; - - if (GetCurrentClass()->HasAnyClassFlags(CLASS_RequiredAPI)) - { - FError::Throwf(TEXT("'%s' must not be used on methods of a class that is marked '%s' itself."), *RequiredAPIMacroIfPresent, *RequiredAPIMacroIfPresent); - } - FuncInfo.FunctionFlags |= FUNC_RequiredAPI; - FuncInfo.FunctionExportFlags |= FUNCEXPORT_RequiredAPI; - - APIMacroIfPresent = RequiredAPIMacroIfPresent; - } - } - - if (bThrowTokenBack) - { - UngetToken(Token); - } - } - } - - // Look for static again, in case there was an ENGINE_API token first - if (!APIMacroIfPresent.IsEmpty() && MatchIdentifier(TEXT("static"), ESearchCase::CaseSensitive)) - { - FError::Throwf(TEXT("Unexpected API macro '%s'. Did you mean to put '%s' after the static keyword?"), *APIMacroIfPresent, *APIMacroIfPresent); - } - - // Look for virtual again, in case there was an ENGINE_API token first - if (MatchIdentifier(TEXT("virtual"), ESearchCase::CaseSensitive)) - { - bSawVirtual = true; - } - - // Process the virtualness - if (bSawVirtual) - { - // Remove the implicit final, the user can still specifying an explicit final at the end of the declaration - bAutomaticallyFinal = false; - - // if this is a BlueprintNativeEvent or BlueprintImplementableEvent in an interface, make sure it's not "virtual" - if (FuncInfo.FunctionFlags & FUNC_BlueprintEvent) - { - if (GetCurrentClass()->HasAnyClassFlags(CLASS_Interface)) - { - FError::Throwf(TEXT("BlueprintImplementableEvents in Interfaces must not be declared 'virtual'")); - } - - // if this is a BlueprintNativeEvent, make sure it's not "virtual" - else if (FuncInfo.FunctionFlags & FUNC_Native) - { - UE_LOG_ERROR_UHT(TEXT("BlueprintNativeEvent functions must be non-virtual.")); - } - - else - { - UE_LOG_WARNING_UHT(TEXT("BlueprintImplementableEvents should not be virtual. Use BlueprintNativeEvent instead.")); - } - } - } - else - { - // if this is a function in an Interface, it must be marked 'virtual' unless it's an event - if (GetCurrentClass()->HasAnyClassFlags(CLASS_Interface) && !(FuncInfo.FunctionFlags & FUNC_BlueprintEvent)) - { - FError::Throwf(TEXT("Interface functions that are not BlueprintImplementableEvents must be declared 'virtual'")); - } - } - - // Handle the initial implicit/explicit final - // A user can still specify an explicit final after the parameter list as well. - if (bAutomaticallyFinal || FuncInfo.bSealedEvent) - { - FuncInfo.FunctionFlags |= FUNC_Final; - FuncInfo.FunctionExportFlags |= FUNCEXPORT_Final; - - if (GetCurrentClass()->HasAnyClassFlags(CLASS_Interface)) - { - UE_LOG_ERROR_UHT(TEXT("Interface functions cannot be declared 'final'")); - } - } - - // Get return type. - FToken ReturnType( CPT_None ); - - // C++ style functions always have a return value type, even if it's void - bool bHasReturnValue = !MatchIdentifier(TEXT("void"), ESearchCase::CaseSensitive); - if (bHasReturnValue) - { - GetVarType(AllClasses, GetCurrentScope(), ReturnType, CPF_None, nullptr, EPropertyDeclarationStyle::None, EVariableCategory::Return); - } - - // Skip whitespaces to get InputPos exactly on beginning of function name. - while (FChar::IsWhitespace(PeekChar())) { GetChar(); } - - FuncInfo.InputPos = InputPos; - - // 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 && FuncInfo.EndpointName != TEXT("JSBridge")) - { - // 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); - } - } - } - - UFunction* TopFunction = CreateFunction(FuncInfo); - - FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(TopFunction, CurrentSrcFile); - - TopFunction->FunctionFlags |= FuncInfo.FunctionFlags; - - FuncInfo.FunctionReference = TopFunction; - FuncInfo.SetFunctionNames(); - - GetCurrentScope()->AddType(TopFunction); - - FFunctionData* StoredFuncData = FFunctionData::Add(FFuncInfo(FuncInfo)); - if (FuncInfo.FunctionReference->HasAnyFunctionFlags(FUNC_Delegate)) - { - GetCurrentClassData()->MarkContainsDelegate(); - } - - // Get parameter list. - ParseParameterList(AllClasses, TopFunction, false, &MetaData); - - // Get return type, if any. - if (bHasReturnValue) - { - ReturnType.PropertyFlags |= CPF_Parm | CPF_OutParm | CPF_ReturnParm; - FProperty* ReturnProp = GetVarNameAndDim(TopFunction, ReturnType, EVariableCategory::Return); - - TopFunction->NumParms++; - } - - // determine if there are any outputs for this function - bool bHasAnyOutputs = bHasReturnValue; - if (!bHasAnyOutputs) - { - for (TFieldIterator It(TopFunction); It; ++It) - { - FProperty const* const Param = *It; - if (!(Param->PropertyFlags & CPF_ReturnParm) && (Param->PropertyFlags & CPF_OutParm)) - { - bHasAnyOutputs = true; - break; - } - } - } - - // Check to see if there is a function in the super class with the same name - UStruct* SuperStruct = GetCurrentClass(); - if (SuperStruct) - { - SuperStruct = SuperStruct->GetSuperStruct(); - } - if (SuperStruct) - { - if (UFunction* OverriddenFunction = ::FindUField(SuperStruct, FuncInfo.Function.Identifier)) - { - // 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) - UE_LOG_ERROR_UHT(TEXT("%s: Override of UFUNCTION in parent class (%s) cannot have a UFUNCTION() declaration above it; it will use the same parameters as the original declaration."), FuncInfo.Function.Identifier, *OverriddenFunction->GetOuter()->GetName()); - } - } - - if (!bHasAnyOutputs && (FuncInfo.FunctionFlags & (FUNC_BlueprintPure))) - { - // This bad behavior would be treated as a warning in the Blueprint editor, so when converted assets generates these bad functions - // we don't want to prevent compilation: - if (!bClassGeneratedFromBP) - { - UE_LOG_ERROR_UHT(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"), ESearchCase::CaseSensitive) ) - { - if( (TopFunction->FunctionFlags & (FUNC_Native)) == 0 ) - { - // @TODO: UCREMOVAL Reconsider? - //FError::Throwf(TEXT("'const' may only be used for native functions")); - } - - FuncInfo.FunctionFlags |= FUNC_Const; - - // @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: - - // If its a const BlueprintCallable function with some sort of output and is not being marked as an BlueprintPure=false function, mark it as BlueprintPure as well - if ( bHasAnyOutputs && ((FuncInfo.FunctionFlags & FUNC_BlueprintCallable) != 0) && !FuncInfo.bForceBlueprintImpure) - { - FuncInfo.FunctionFlags |= FUNC_BlueprintPure; - } - } - - // Try parsing metadata for the function - ParseFieldMetaData(MetaData, *(TopFunction->GetName())); - - AddFormattedPrevCommentAsTooltipMetaData(MetaData); - - AddMetaDataToClassData(TopFunction, MoveTemp(MetaData)); - - // 'final' and 'override' can appear in any order before an optional '= 0' pure virtual specifier - bool bFoundFinal = MatchIdentifier(TEXT("final"), ESearchCase::CaseSensitive); - bool bFoundOverride = MatchIdentifier(TEXT("override"), ESearchCase::CaseSensitive); - if (!bFoundFinal && bFoundOverride) - { - bFoundFinal = MatchIdentifier(TEXT("final"), ESearchCase::CaseSensitive); - } - - // Handle C++ style functions being declared as abstract - if (MatchSymbol(TEXT('='))) - { - int32 ZeroValue = 1; - bool bGotZero = GetConstInt(/*out*/ZeroValue); - bGotZero = bGotZero && (ZeroValue == 0); - - if (!bGotZero) - { - FError::Throwf(TEXT("Expected 0 to indicate function is abstract")); - } - } - - // Look for the final keyword to indicate this function is sealed - if (bFoundFinal) - { - // This is a final (prebinding, non-overridable) function - FuncInfo.FunctionFlags |= FUNC_Final; - FuncInfo.FunctionExportFlags |= FUNCEXPORT_Final; - if (GetCurrentClass()->HasAnyClassFlags(CLASS_Interface)) - { - FError::Throwf(TEXT("Interface functions cannot be declared 'final'")); - } - else if (FuncInfo.FunctionFlags & FUNC_BlueprintEvent) - { - FError::Throwf(TEXT("Blueprint events cannot be declared 'final'")); - } - } - - // 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); - - // 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(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()); - } - - if (TopFunction->FunctionFlags & (FUNC_BlueprintCallable | FUNC_BlueprintEvent)) - { - for (TFieldIterator It(TopFunction); It; ++It) - { - FProperty const* const Param = *It; - if (Param->ArrayDim > 1) - { - FError::Throwf(TEXT("Static array cannot be exposed to blueprint. Function: %s Parameter %s\n"), *TopFunction->GetName(), *Param->GetName()); - } - - if (!IsPropertySupportedByBlueprint(Param, false)) - { - FString ExtendedCPPType; - FString CPPType = Param->GetCPPType(&ExtendedCPPType); - UE_LOG_ERROR_UHT(TEXT("Type '%s%s' is not supported by blueprint. %s.%s"), *CPPType, *ExtendedCPPType, *TopFunction->GetName(), *Param->GetName()); - } - } - } - - // Just declaring a function, so end the nesting. - PostPopFunctionDeclaration(AllClasses, TopFunction); - - // See what's coming next - FToken Token; - if (!GetToken(Token)) - { - FError::Throwf(TEXT("Unexpected end of file")); - } - - // Optionally consume a semicolon - // This is optional to allow inline function definitions - if (Token.TokenType == TOKEN_Symbol && Token.Matches(TEXT(';'))) - { - // Do nothing (consume it) - } - else if (Token.TokenType == TOKEN_Symbol && Token.Matches(TEXT('{'))) - { - // Skip inline function bodies - UngetToken(Token); - SkipDeclaration(Token); - } - else - { - // Put the token back so we can continue parsing as normal - UngetToken(Token); - } - - // perform documentation policy tests - CheckDocumentationPolicyForFunc(GetCurrentClass(), FuncInfo.FunctionReference); -} - -/** Parses optional metadata text. */ -void FHeaderParser::ParseFieldMetaData(TMap& MetaData, const TCHAR* FieldName) -{ - FToken PropertyMetaData; - bool bMetadataPresent = false; - if (MatchIdentifier(TEXT("UMETA"), ESearchCase::CaseSensitive)) - { - auto ErrorMessageGetter = [FieldName]() { return FString::Printf(TEXT("' %s metadata'"), FieldName); }; - - bMetadataPresent = true; - RequireSymbol( TEXT('('), ErrorMessageGetter ); - if (!GetRawTokenRespectingQuotes(PropertyMetaData, TCHAR(')'))) - { - FError::Throwf(TEXT("'%s': No metadata specified"), FieldName); - } - RequireSymbol( TEXT(')'), ErrorMessageGetter); - } - - if (bMetadataPresent) - { - // parse apart the string - TArray 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(MoveTemp(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 = MoveTemp(Pairs[PairIndex]); - FString Key; - // by default, not value, just a key (allowed) - FString Value; - - // look for a value after an = - const int32 Equals = Token.Find(TEXT("="), ESearchCase::CaseSensitive); - // if we have an =, break up the string - if (Equals != INDEX_NONE) - { - Key = Token.Left(Equals); - Value = MoveTemp(Token); - Value.RightInline((Value.Len() - Equals) - 1, false); - } - else - { - Key = MoveTemp(Token); - } - - InsertMetaDataPair(MetaData, MoveTemp(Key), MoveTemp(Value)); - } - } -} - -bool FHeaderParser::IsBitfieldProperty(ELayoutMacroType LayoutMacroType) -{ - if (LayoutMacroType == ELayoutMacroType::Bitfield || LayoutMacroType == ELayoutMacroType::BitfieldEditorOnly) - { - return true; - } - - 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 && Token.Matches(TEXT(':'))) - { - bIsBitfield = true; - } - UngetToken(Token); - } - UngetToken(TokenVarName); - } - - return bIsBitfield; -} - -void FHeaderParser::ValidatePropertyIsDeprecatedIfNecessary(const FPropertyBase& VarProperty, const FToken* OuterPropertyType) -{ - // check to see if we have a FClassProperty using a deprecated class - if ( VarProperty.MetaClass != NULL && VarProperty.MetaClass->HasAnyClassFlags(CLASS_Deprecated) && !(VarProperty.PropertyFlags & CPF_Deprecated) && - (OuterPropertyType == NULL || !(OuterPropertyType->PropertyFlags & CPF_Deprecated)) ) - { - UE_LOG_ERROR_UHT(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 FObjectProperty 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_SoftObjectReference) && 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 - { - UE_LOG_ERROR_UHT(TEXT("Property is using a deprecated class: %s. Property should be marked deprecated as well."), *VarProperty.PropertyClass->GetPathName()); - } -} - -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_Int64: - case CPT_Byte: - case CPT_Float: - case CPT_Bool: - case CPT_Bool8: - case CPT_ObjectReference: - case CPT_String: - case CPT_Text: - case CPT_Name: - case CPT_Interface: - case CPT_SoftObjectReference: - ProperNativeType = true; - } - - if (!ProperNativeType && (CPT_Struct == Property.Type) && Property.Struct) - { - ProperNativeType |= Property.Struct->GetBoolMetaData(NAME_BlueprintType); - } - - return ProperNativeType; - } -}; - -void FHeaderParser::CompileVariableDeclaration(FClasses& AllClasses, UStruct* Struct) -{ - EPropertyFlags DisallowFlags = CPF_ParmFlags; - EPropertyFlags EdFlags = CPF_None; - - // Get variable type. - FPropertyBase OriginalProperty(CPT_None); - FIndexRange TypeRange; - ELayoutMacroType LayoutMacroType = ELayoutMacroType::None; - GetVarType( AllClasses, &FScope::GetTypeScope(Struct).Get(), OriginalProperty, DisallowFlags, /*OuterPropertyType=*/ NULL, EPropertyDeclarationStyle::UPROPERTY, EVariableCategory::Member, &TypeRange, &LayoutMacroType); - OriginalProperty.PropertyFlags |= EdFlags; - - FString* Category = OriginalProperty.MetaData.Find(NAME_Category); - - // 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))) - { - if ((Struct->GetOutermost() != nullptr) && !bIsCurrentModulePartOfEngine) - { - Category = &OriginalProperty.MetaData.Add(NAME_Category, Struct->GetName()); - } - else - { - UE_LOG_ERROR_UHT(TEXT("An explicit Category specifier is required for any property exposed to the editor or Blueprints in an Engine module.")); - } - } - - // 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).TrimStartAndEnd().LeftChop(1).TrimEnd(); - FError::Throwf(TEXT("UPROPERTY pointers cannot be interfaces - did you mean TScriptInterface<%s>?"), *TypeName); - } - - // If the category was specified explicitly, it wins - if (Category && !(OriginalProperty.PropertyFlags & (CPF_Edit|CPF_BlueprintVisible|CPF_BlueprintAssignable|CPF_BlueprintCallable))) - { - UE_LOG_WARNING_UHT(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) - { - UE_LOG_ERROR_UHT(TEXT("Property cannot have 'DisableEditOnInstance' without being editable")); - } - - if (OriginalProperty.PropertyFlags & CPF_DisableEditOnTemplate) - { - UE_LOG_ERROR_UHT(TEXT("Property cannot have 'DisableEditOnTemplate' without being editable")); - } - } - - // Validate. - if (OriginalProperty.PropertyFlags & CPF_ParmFlags) - { - FError::Throwf(TEXT("Illegal type modifiers in member variable declaration") ); - } - - if (FString* ExposeOnSpawnValue = OriginalProperty.MetaData.Find(NAME_ExposeOnSpawn)) - { - if ((*ExposeOnSpawnValue == TEXT("true")) && !FExposeOnSpawnValidator::IsSupported(OriginalProperty)) - { - UE_LOG_ERROR_UHT(TEXT("ExposeOnSpawn - Property cannot be exposed")); - } - } - - if (LayoutMacroType != ELayoutMacroType::None) - { - RequireSymbol(TEXT(','), GLayoutMacroNames[(int32)LayoutMacroType]); - } - - // Process all variables of this type. - TArray NewProperties; - for (;;) - { - FToken Property = OriginalProperty; - FProperty* NewProperty = GetVarNameAndDim(Struct, Property, EVariableCategory::Member, LayoutMacroType); - - // Optionally consume the :1 at the end of a bitfield boolean declaration - if (Property.IsBool()) - { - if (LayoutMacroType == ELayoutMacroType::Bitfield || LayoutMacroType == ELayoutMacroType::BitfieldEditorOnly || 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 != ENestType::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(Struct)) - { - if (NewProperty->ContainsInstancedObjectProperty()) - { - StructBeingBuilt->StructFlags = EStructFlags(StructBeingBuilt->StructFlags | STRUCT_HasInstancedReference); - } - } - - if (NewProperty->HasAnyPropertyFlags(CPF_BlueprintVisible)) - { - if (Struct->IsA() && !Struct->GetBoolMetaDataHierarchical(NAME_BlueprintType)) - { - UE_LOG_ERROR_UHT(TEXT("Cannot expose property to blueprints in a struct that is not a BlueprintType. %s.%s"), *Struct->GetName(), *NewProperty->GetName()); - } - - if (NewProperty->ArrayDim > 1) - { - UE_LOG_ERROR_UHT(TEXT("Static array cannot be exposed to blueprint %s.%s"), *Struct->GetName(), *NewProperty->GetName()); - } - - if (!IsPropertySupportedByBlueprint(NewProperty, true)) - { - FString ExtendedCPPType; - FString CPPType = NewProperty->GetCPPType(&ExtendedCPPType); - UE_LOG_ERROR_UHT(TEXT("Type '%s%s' is not supported by blueprint. %s.%s"), *CPPType, *ExtendedCPPType, *Struct->GetName(), *NewProperty->GetName()); - } - } - - if (LayoutMacroType != ELayoutMacroType::None || !MatchSymbol(TEXT(','))) - { - break; - } - } - - // Optional member initializer. - if (LayoutMacroType == ELayoutMacroType::FieldInitialized) - { - // Skip past the specified member initializer; we make no attempt to parse it - FToken SkipToken; - int Nesting = 1; - while (GetToken(SkipToken)) - { - if (SkipToken.Matches(TEXT('('))) - { - ++Nesting; - } - else if (SkipToken.Matches(TEXT(')'))) - { - --Nesting; - if (Nesting == 0) - { - UngetToken(SkipToken); - break; - } - } - } - } - else 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; - } - } - } - // Using Brace Initialization - else if (MatchSymbol(TEXT('{'))) - { - FToken SkipToken; - int BraceLevel = 1; - while (GetToken(SkipToken)) - { - if (SkipToken.Matches(TEXT('{'))) - { - ++BraceLevel; - } - else if (SkipToken.Matches(TEXT('}'))) - { - --BraceLevel; - if (BraceLevel == 0) - { - break; - } - } - } - } - - if (LayoutMacroType == ELayoutMacroType::None) - { - // Expect a semicolon. - RequireSymbol(TEXT(';'), TEXT("'variable declaration'")); - } - else - { - // Expect a close bracket. - RequireSymbol(TEXT(')'), GLayoutMacroNames[(int32)LayoutMacroType]); - } - - // Skip redundant semi-colons - for (;;) - { - int32 CurrInputPos = InputPos; - int32 CurrInputLine = InputLine; - - FToken Token; - if (!GetToken(Token, /*bNoConsts=*/ true)) - { - break; - } - - if (Token.TokenType != TOKEN_Symbol || !Token.Matches(TEXT(';'))) - { - InputPos = CurrInputPos; - InputLine = CurrInputLine; - break; - } - } -} - -// -// Compile a statement: Either a declaration or a command. -// Returns 1 if success, 0 if end of file. -// -bool FHeaderParser::CompileStatement(FClasses& AllClasses, TArray& DelegatesToFixup) -{ - // Get a token and compile it. - FToken Token; - if( !GetToken(Token, true) ) - { - // End of file. - return false; - } - else if (!CompileDeclaration(AllClasses, DelegatesToFixup, Token)) - { - 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 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 It(ThisFunction); It; ++It) - { - FProperty* Param = *It; - if (FStructProperty* StructProp = CastField(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 It(ThisFunction); It; ++It) - { - FProperty* Param = *It; - - if (!(Param->PropertyFlags & CPF_ReturnParm) && (Param->PropertyFlags & CPF_OutParm)) - { - ThisFunction->FunctionFlags |= FUNC_HasOutParms; - } - - if (FStructProperty* StructProp = CastField(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 - */ -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -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 ChildIt(Class, EFieldIteratorFlags::ExcludeSuper); ChildIt; ++ChildIt) - { - UStruct* ChildStruct = *ChildIt; - - if (UFunction* Function = Cast(ChildStruct)) - { - // Add this function to the function map of its parent class - Class->AddFunctionToFunctionMap(Function, Function->GetFName()); - } - else if (ChildStruct->IsA(UScriptStruct::StaticClass())) - { - // Ignore embedded structs - } - else - { - UE_LOG_WARNING_UHT(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. -// Returns result enumeration. -// -ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrealSourceFile* SourceFile) -{ - SetCurrentSourceFile(SourceFile); - FUnrealSourceFile* CurrentSrcFile = SourceFile; - if (CurrentSrcFile->IsParsed()) - { - return ECompilationResult::Succeeded; - } - - CurrentSrcFile->MarkAsParsed(); - - // Early-out if this class has previously failed some aspect of parsing - if (FailedFilesAnnotation.Get(CurrentSrcFile)) - { - return ECompilationResult::OtherCompilationError; - } - - // Reset the parser to begin a new class - bEncounteredNewStyleClass_UnmatchedBrackets = false; - bSpottedAutogeneratedHeaderInclude = false; - bHaveSeenUClass = false; - bClassHasGeneratedBody = false; - bClassHasGeneratedUInterfaceBody = false; - bClassHasGeneratedIInterfaceBody = false; - - ECompilationResult::Type Result = ECompilationResult::OtherCompilationError; - - // Message. - UE_LOG(LogCompile, Verbose, TEXT("Parsing %s"), *CurrentSrcFile->GetFilename()); - - // Init compiler variables. - ResetParser(*CurrentSrcFile->GetContent()); - - // Init nesting. - NestLevel = 0; - TopNest = NULL; - PushNest(ENestType::GlobalScope, nullptr, CurrentSrcFile); - - // C++ classes default to private access level - CurrentAccessSpecifier = ACCESS_Private; - - // Try to compile it, and catch any errors. - bool bEmptyFile = true; - - // Tells if this header defines no-export classes only. - bool bNoExportClassesOnly = true; - -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - // Parse entire program. - TArray DelegatesToFixup; - while (CompileStatement(AllClasses, DelegatesToFixup)) - { - bEmptyFile = false; - - // Clear out the previous comment in anticipation of the next statement. - ClearComment(); - StatementsParsed++; - } - - PopNest(ENestType::GlobalScope, TEXT("Global scope")); - - auto ScopeTypeIterator = CurrentSrcFile->GetScope()->GetTypeIterator(); - while (ScopeTypeIterator.MoveNext()) - { - UField* Type = *ScopeTypeIterator; - - if (!Type->IsA() && !Type->IsA()) - { - continue; - } - - UStruct* Struct = Cast(Type); - - // now validate all delegate variables declared in the class - TMap DelegateCache; - FixupDelegateProperties(AllClasses, Struct, FScope::GetTypeScope(Struct).Get(), DelegateCache); - } - - // Fix up any delegates themselves, if they refer to other delegates - { - TMap DelegateCache; - for (UDelegateFunction* Delegate : DelegatesToFixup) - { - FixupDelegateProperties(AllClasses, Delegate, CurrentSrcFile->GetScope().Get(), DelegateCache); - } - } - - // Precompute info for runtime optimization. - LinesParsed += InputLine; - - if (RPCsNeedingHookup.Num() > 0) - { - FString ErrorMsg(TEXT("Request functions missing response pairs:\r\n")); - for (TMap::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") ); - } - - if (NestLevel == 1) - { - FError::Throwf(TEXT("Internal nest inconsistency") ); - } - else if (NestLevel > 2) - { - FError::Throwf(TEXT("Unexpected end of script in '%s' block"), NestTypeName(TopNest->NestType) ); - } - - // First-pass success. - Result = ECompilationResult::Succeeded; - - for (const TPair& ClassDataPair : CurrentSrcFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - PostParsingClassSetup(Class); - - // Clean up and exit. - Class->Bind(); - - // Finalize functions - FinalizeScriptExposedFunctions(Class); - - bNoExportClassesOnly = bNoExportClassesOnly && Class->HasAnyClassFlags(CLASS_NoExport); - } - - check(CurrentSrcFile->IsParsed()); - - if (!bSpottedAutogeneratedHeaderInclude && !bEmptyFile && !bNoExportClassesOnly) - { - const FString& ExpectedHeaderName = CurrentSrcFile->GetGeneratedHeaderFilename(); - FError::Throwf(TEXT("Expected an include at the top of the header: '#include \"%s\"'"), *ExpectedHeaderName); - } - - GScriptHelper.CheckForNoIInterfaces(); - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch( TCHAR* ErrorMsg ) - { - if (NestLevel == 0) - { - // Pushing nest so there is a file context for this error. - PushNest(ENestType::GlobalScope, nullptr, CurrentSrcFile); - } - - // Handle compiler error. - { - TGuardValue DisableLogTimes(GPrintLogTimes, ELogTimes::None); - FString FormattedErrorMessageWithContext = FString::Printf(TEXT("%s: Error: %s"), *GetContext(), ErrorMsg); - - UE_LOG(LogCompile, Log, TEXT("%s"), *FormattedErrorMessageWithContext ); - Warn->Log(ELogVerbosity::Error, *FString::Printf(TEXT("Error: %s"), ErrorMsg)); - } - - FailedFilesAnnotation.Set(CurrentSrcFile); - Result = GCompilationResult; - } -#endif - - return Result; //@TODO: UCREMOVAL: This function is always returning succeeded even on a compiler error; should this continue? -} - -/*----------------------------------------------------------------------------- - Global functions. ------------------------------------------------------------------------------*/ - -ECompilationResult::Type FHeaderParser::ParseRestOfModulesSourceFiles(FClasses& AllClasses, UPackage* ModulePackage) -{ - if (const TArray* SourceFiles = UnrealSourceFilesMap.FindFilesForPackage(ModulePackage)) - { - for (FUnrealSourceFile* SourceFile : *SourceFiles) - { - if (!SourceFile->IsParsed() || SourceFile->GetDefinedClassesCount() == 0) - { - ECompilationResult::Type Result; - if ((Result = ParseHeaders(AllClasses, SourceFile)) != ECompilationResult::Succeeded) - { - return Result; - } - } - } - } - - return ECompilationResult::Succeeded; -} - -// Parse Class's annotated headers and optionally its child classes. -static const FString ObjectHeader(TEXT("NoExportTypes.h")); - -ECompilationResult::Type FHeaderParser::ParseHeaders(FClasses& AllClasses, FUnrealSourceFile* SourceFile) -{ - ECompilationResult::Type Result = ECompilationResult::Succeeded; - - if (SourceFile->AreDependenciesResolved()) - { - return Result; - } - - SourceFile->MarkDependenciesResolved(); - - TArray SourceFilesRequired; - - for (FHeaderProvider& Include : SourceFile->GetIncludes()) - { - if (Include.GetId() == ObjectHeader) - { - continue; - } - - if (FUnrealSourceFile* DepFile = Include.Resolve(UnrealSourceFilesMap, TypeDefinitionInfoMap)) - { - SourceFilesRequired.Add(DepFile); - } - } - - for (const TPair& ClassDataPair : SourceFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - for (UClass* ParentClass = Class->GetSuperClass(); ParentClass && !ParentClass->HasAnyClassFlags(CLASS_Parsed | CLASS_Intrinsic); ParentClass = ParentClass->GetSuperClass()) - { - SourceFilesRequired.Add(&TypeDefinitionInfoMap[ParentClass]->GetUnrealSourceFile()); - } - } - - for (FUnrealSourceFile* RequiredFile : SourceFilesRequired) - { - SourceFile->GetScope()->IncludeScope(&RequiredFile->GetScope().Get()); - - ECompilationResult::Type ParseResult = ParseHeaders(AllClasses, RequiredFile); - - if (ParseResult != ECompilationResult::Succeeded) - { - return ParseResult; - } - } - - // Parse the file - { - ECompilationResult::Type OneFileResult = ParseHeader(AllClasses, SourceFile); - - for (const TPair& ClassDataPair : SourceFile->GetDefinedClassesWithParsingInfo()) - { - UClass* Class = ClassDataPair.Key; - Class->ClassFlags |= CLASS_Parsed; - } - - if (OneFileResult != ECompilationResult::Succeeded) - { - // if we couldn't parse this file fail. - return OneFileResult; - } - } - - // Success. - return Result; -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -bool FHeaderParser::DependentClassNameFromHeader(const TCHAR* HeaderFilename, FString& OutClassName) -{ - FString DependentClassName(HeaderFilename); - const int32 ExtensionIndex = DependentClassName.Find(TEXT("."), ESearchCase::CaseSensitive); - if (ExtensionIndex != INDEX_NONE) - { - // Generate UHeaderName name for this header. - OutClassName = TEXT("U") + FPaths::GetBaseFilename(MoveTemp(DependentClassName)); - return true; - } - return false; -} -*/ - -/** - * Gets source files ordered by UCLASSes inheritance. - * - * @param CurrentPackage Current package. - * @param AllClasses Current class tree. - * - * @returns Array of source files. - */ -TSet FHeaderParser::GetSourceFilesWithInheritanceOrdering(UPackage* CurrentPackage, FClasses& AllClasses) const -{ - TSet SourceFiles; - - TArray Classes = AllClasses.GetClassesInPackage(); - - // First add source files with the inheritance order. - for (UClass* Class : Classes) - { - const TSharedRef* DefinitionInfoPtr = TypeDefinitionInfoMap.Find(Class); - if (DefinitionInfoPtr == nullptr) - { - continue; - } - - FUnrealSourceFile& SourceFile = (*DefinitionInfoPtr)->GetUnrealSourceFile(); - - if (SourceFile.GetScope()->ContainsTypes()) - { - SourceFiles.Add(&SourceFile); - } - } - - // Then add the rest. - if (const TArray* SourceFilesForPackage = UnrealSourceFilesMap.FindFilesForPackage(CurrentPackage)) - { - for (FUnrealSourceFile* SourceFile : *SourceFilesForPackage) - { - if (SourceFile->GetScope()->ContainsTypes()) - { - SourceFiles.Add(SourceFile); - } - } - } - - return SourceFiles; -} - -// Begins the process of exporting C++ class declarations for native classes in the specified package -void FHeaderParser::ExportNativeHeaders( - UPackage* CurrentPackage, - FClasses& AllClasses, - bool bAllowSaveExportedHeaders, - const FManifestModule& Module, - const FPublicSourceFileSet& PublicSourceFileSet, - const TMap& PackageToManifestModuleMap -) const -{ - TSet SourceFiles = GetSourceFilesWithInheritanceOrdering(CurrentPackage, AllClasses); - if (SourceFiles.Num() > 0) - { - if ( CurrentPackage != NULL ) - { - UE_LOG(LogCompile, Verbose, TEXT("Exporting native class declarations for %s"), *CurrentPackage->GetName()); - } - else - { - UE_LOG(LogCompile, Verbose, TEXT("Exporting native class declarations")); - } - - // Export native class definitions to package header files. - FNativeClassHeaderGenerator( - *this, - CurrentPackage, - SourceFiles, - AllClasses, - bAllowSaveExportedHeaders, - PublicSourceFileSet, - PackageToManifestModuleMap - ); - } -} - -FHeaderParser::FHeaderParser(FFeedbackContext* InWarn, - const FManifestModule& InModule, - const FUnrealSourceFiles& InUnrealSourceFilesMap, - FTypeDefinitionInfoMap& InTypeDefinitionInfoMap, - const FClassDeclarations& InClassDeclarations, - TMap& InEnumUnderlyingTypes, - FRWLock& InEnumUnderlyingTypesLock, - TMap& InClassSerializerMap, - FRWLock& InClassSerializerMapLock) - : FBaseParser() - , DefaultGeneratedCodeVersion(EGeneratedCodeVersion::V1) - , UnrealSourceFilesMap(InUnrealSourceFilesMap) - , TypeDefinitionInfoMap(InTypeDefinitionInfoMap) - , ClassDeclarations(InClassDeclarations) - , Warn(InWarn) - , bSpottedAutogeneratedHeaderInclude(false) - , NestLevel(0) - , TopNest(nullptr) - , CurrentlyParsedModule(&InModule) - , EnumUnderlyingTypes(InEnumUnderlyingTypes) - , EnumUnderlyingTypesLock(InEnumUnderlyingTypesLock) - , ClassSerializerMap(InClassSerializerMap) - , ClassSerializerMapLock(InClassSerializerMapLock) -{ - // Determine if the current module is part of the engine or a game (we are more strict about things for Engine modules) - switch (InModule.ModuleType) - { - case EBuildModuleType::Program: - { - const FString AbsoluteEngineDir = FPaths::ConvertRelativePathToFull(FPaths::EngineDir()); - const FString ModuleDir = FPaths::ConvertRelativePathToFull(InModule.BaseDirectory); - bIsCurrentModulePartOfEngine = ModuleDir.StartsWith(AbsoluteEngineDir); - } - break; - case EBuildModuleType::EngineRuntime: - case EBuildModuleType::EngineUncooked: - case EBuildModuleType::EngineDeveloper: - case EBuildModuleType::EngineEditor: - case EBuildModuleType::EngineThirdParty: - bIsCurrentModulePartOfEngine = true; - break; - case EBuildModuleType::GameRuntime: - case EBuildModuleType::GameUncooked: - case EBuildModuleType::GameDeveloper: - case EBuildModuleType::GameEditor: - case EBuildModuleType::GameThirdParty: - bIsCurrentModulePartOfEngine = false; - break; - default: - bIsCurrentModulePartOfEngine = true; - check(false); - } - - FScriptLocation::Compiler = this; - - // UHTLite - /*static*/ bool bConfigOptionsInitialized = false; - - if (!bConfigOptionsInitialized) - { - // Read Ini options, GConfig must exist by this point - check(GConfig); - - const FName TypeRedirectsKey(TEXT("TypeRedirects")); - const FName StructsWithNoPrefixKey(TEXT("StructsWithNoPrefix")); - const FName StructsWithTPrefixKey(TEXT("StructsWithTPrefix")); - const FName DelegateParameterCountStringsKey(TEXT("DelegateParameterCountStrings")); - const FName GeneratedCodeVersionKey(TEXT("GeneratedCodeVersion")); - - FConfigSection* ConfigSection = GConfig->GetSectionPrivate(TEXT("UnrealHeaderTool"), false, true, GEngineIni); - if (ConfigSection) - { - for (FConfigSection::TIterator It(*ConfigSection); It; ++It) - { - if (It.Key() == TypeRedirectsKey) - { - FString OldType; - FString NewType; - - FParse::Value(*It.Value().GetValue(), TEXT("OldType="), OldType); - FParse::Value(*It.Value().GetValue(), TEXT("NewType="), NewType); - - TypeRedirectMap.Add(MoveTemp(OldType), MoveTemp(NewType)); - } - else if (It.Key() == StructsWithNoPrefixKey) - { - StructsWithNoPrefix.Add(It.Value().GetValue()); - } - else if (It.Key() == StructsWithTPrefixKey) - { - StructsWithTPrefix.Add(It.Value().GetValue()); - } - else if (It.Key() == DelegateParameterCountStringsKey) - { - DelegateParameterCountStrings.Add(It.Value().GetValue()); - } - else if (It.Key() == GeneratedCodeVersionKey) - { - DefaultGeneratedCodeVersion = ToGeneratedCodeVersion(It.Value().GetValue()); - } - } - } - bConfigOptionsInitialized = true; - } -} - -// 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]; -} - -#pragma region Plugins -// Exports the class to all available plugins -void ExportClassToScriptPlugins(UClass* Class, const FManifestModule& Module, IScriptGeneratorPluginInterface& ScriptPlugin, const FTypeDefinitionInfoMap& TypeDefinitionInfoMap) -{ - const TSharedRef* DefinitionInfoRef = TypeDefinitionInfoMap.Find(Class); - if (DefinitionInfoRef == nullptr) - { - const FString Empty = TEXT(""); - ScriptPlugin.ExportClass(Class, Empty, Empty, false); - } - else - { - FUnrealSourceFile& SourceFile = (*DefinitionInfoRef)->GetUnrealSourceFile(); - ScriptPlugin.ExportClass(Class, SourceFile.GetFilename(), SourceFile.GetGeneratedFilename(), SourceFile.HasChanged()); - } -} - -// Exports class tree to all available plugins -void ExportClassTreeToScriptPlugins(const FClassTree* Node, const FManifestModule& Module, IScriptGeneratorPluginInterface& ScriptPlugin, const FTypeDefinitionInfoMap& TypeDefinitionInfoMap) -{ - for (int32 ChildIndex = 0; ChildIndex < Node->NumChildren(); ++ChildIndex) - { - const FClassTree* ChildNode = Node->GetChild(ChildIndex); - ExportClassToScriptPlugins(ChildNode->GetClass(), Module, ScriptPlugin, TypeDefinitionInfoMap); - } - - for (int32 ChildIndex = 0; ChildIndex < Node->NumChildren(); ++ChildIndex) - { - const FClassTree* ChildNode = Node->GetChild(ChildIndex); - ExportClassTreeToScriptPlugins(ChildNode, Module, ScriptPlugin, TypeDefinitionInfoMap); - } -} -#pragma endregion Plugins - -// Parse all headers for classes that are inside CurrentPackage. -ECompilationResult::Type FHeaderParser::ParseAllHeadersInside( - FClasses& ModuleClasses, - FFeedbackContext* Warn, - UPackage* CurrentPackage, - const FManifestModule& Module, -#pragma region Plugins - TArray& ScriptPlugins, -#pragma endregion Plugins - const FUnrealSourceFiles& UnrealSourceFilesMap, - FTypeDefinitionInfoMap& TypeDefinitionInfoMap, - const FPublicSourceFileSet& PublicSourceFileSet, - const TMap& PackageToManifestModuleMap, - const FClassDeclarations& ClassDeclarations, - TMap& EnumUnderlyingTypes, - FRWLock& EnumUnderlyingTypesLock, - TMap& ClassSerializerMap, - FRWLock& ClassSerializerMapLock - ) -{ - SCOPE_SECONDS_COUNTER_UHT(ParseAllHeaders); - - // Disable loading of objects outside of this package (or more exactly, objects which aren't UFields, CDO, or templates) - TGuardValue 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, - Module, - UnrealSourceFilesMap, - TypeDefinitionInfoMap, - ClassDeclarations, - EnumUnderlyingTypes, - EnumUnderlyingTypesLock, - ClassSerializerMap, - ClassSerializerMapLock); - Warn->SetContext(&HeaderParser); - - - // Hierarchically parse all classes. - ECompilationResult::Type Result = ECompilationResult::Succeeded; -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - FName ModuleName = FName(*Module.Name); - bool bNeedsRegeneration = Module.NeedsRegeneration(); - - // Set up a filename for the error context if we don't even get as far parsing a class - FClass* RootClass = ModuleClasses.GetRootClass(); - const TSharedRef& TypeDefinitionInfo = TypeDefinitionInfoMap[RootClass]; - const FUnrealSourceFile& RootSourceFile = TypeDefinitionInfo->GetUnrealSourceFile(); - const FString& RootFilename = RootSourceFile.GetFilename(); - - HeaderParser.Filename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*RootFilename); - - if (const TArray* SourceFiles = PublicSourceFileSet.FindFilesForPackage(CurrentPackage)) - { - for (FUnrealSourceFile* SourceFile : *SourceFiles) - { - if ((!SourceFile->IsParsed() || SourceFile->GetDefinedClassesCount() == 0)) - { - Result = HeaderParser.ParseHeaders(ModuleClasses, SourceFile); - if (Result != ECompilationResult::Succeeded) - { - return Result; - } - } - } - } - if (Result == ECompilationResult::Succeeded) - { - Result = HeaderParser.ParseRestOfModulesSourceFiles(ModuleClasses, CurrentPackage); - } - - if (Result == ECompilationResult::Succeeded) - { - // Validate the sparse class data for all classes in the current package - for (const FClass* Class : ModuleClasses.GetClassesInPackage(CurrentPackage)) - { - CheckSparseClassData(Class); - } - - // Export the autogenerated code wrappers - - // 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); - - double ExportTime = 0.0; - { - FScopedDurationTimer Timer(ExportTime); - HeaderParser.ExportNativeHeaders( - CurrentPackage, - ModuleClasses, - Module.SaveExportedHeaders, - Module, - PublicSourceFileSet, - PackageToManifestModuleMap - ); - } - GHeaderCodeGenTime += ExportTime; - - // Done with header generation - if (HeaderParser.LinesParsed > 0) - { - UE_LOG(LogCompile, Log, TEXT("Success: Parsed %i line(s), %i statement(s) in %.2f secs.\r\n"), HeaderParser.LinesParsed, HeaderParser.StatementsParsed, ExportTime); - } - else - { - UE_LOG(LogCompile, Log, TEXT("Success: Everything is up to date (in %.2f secs)"), ExportTime); - } - } - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch (TCHAR* ErrorMsg) - { - Warn->Log(ELogVerbosity::Error, ErrorMsg); - Result = GCompilationResult; - } -#endif - // Unregister the header parser from the feedback context - Warn->SetContext(NULL); - -#pragma region Plugins - if (Result == ECompilationResult::Succeeded && ScriptPlugins.Num()) - { - FScopedDurationTimer PluginTimeTracker(GPluginOverheadTime); - - FClassTree* RootNode = &ModuleClasses.GetClassTree(); - for (IScriptGeneratorPluginInterface* Plugin : ScriptPlugins) - { - if (Plugin->ShouldExportClassesForModule(Module.Name, Module.ModuleType, Module.GeneratedIncludeDirectory)) - { - ExportClassToScriptPlugins(RootNode->GetClass(), Module, *Plugin, TypeDefinitionInfoMap); - ExportClassTreeToScriptPlugins(RootNode, Module, *Plugin, TypeDefinitionInfoMap); - } - } - } -#pragma endregion Plugins - - return Result; -} - -/** - * 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; -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -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(UE_PTRDIFF_TO_INT32(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); - } -} -*/ - -enum class EBlockDirectiveType -{ - // We're in a CPP block - CPPBlock, - - // We're in a !CPP block - NotCPPBlock, - - // We're in a 0 block - ZeroBlock, - - // We're in a 1 block - OneBlock, - - // We're in a WITH_HOT_RELOAD block - WithHotReload, - - // We're in a WITH_EDITOR block - WithEditor, - - // We're in a WITH_EDITORONLY_DATA block - WithEditorOnlyData, - - // We're in a block with an unrecognized directive - UnrecognizedBlock -}; - -bool ShouldKeepBlockContents(EBlockDirectiveType DirectiveType) -{ - switch (DirectiveType) - { - case EBlockDirectiveType::NotCPPBlock: - case EBlockDirectiveType::OneBlock: - case EBlockDirectiveType::WithHotReload: - case EBlockDirectiveType::WithEditor: - case EBlockDirectiveType::WithEditorOnlyData: - return true; - - case EBlockDirectiveType::CPPBlock: - case EBlockDirectiveType::ZeroBlock: - case EBlockDirectiveType::UnrecognizedBlock: - return false; - } - - check(false); - UE_ASSUME(false); -} - -bool ShouldKeepDirective(EBlockDirectiveType DirectiveType) -{ - switch (DirectiveType) - { - case EBlockDirectiveType::WithHotReload: - case EBlockDirectiveType::WithEditor: - case EBlockDirectiveType::WithEditorOnlyData: - return true; - - case EBlockDirectiveType::CPPBlock: - case EBlockDirectiveType::NotCPPBlock: - case EBlockDirectiveType::ZeroBlock: - case EBlockDirectiveType::OneBlock: - case EBlockDirectiveType::UnrecognizedBlock: - return false; - } - - check(false); - UE_ASSUME(false); -} - -EBlockDirectiveType ParseCommandToBlockDirectiveType(const TCHAR** Str) -{ - if (FParse::Command(Str, TEXT("0"))) - { - return EBlockDirectiveType::ZeroBlock; - } - - if (FParse::Command(Str, TEXT("1"))) - { - return EBlockDirectiveType::OneBlock; - } - - if (FParse::Command(Str, TEXT("CPP"))) - { - return EBlockDirectiveType::CPPBlock; - } - - if (FParse::Command(Str, TEXT("!CPP"))) - { - return EBlockDirectiveType::NotCPPBlock; - } - - if (FParse::Command(Str, TEXT("WITH_HOT_RELOAD"))) - { - return EBlockDirectiveType::WithHotReload; - } - - if (FParse::Command(Str, TEXT("WITH_EDITOR"))) - { - return EBlockDirectiveType::WithEditor; - } - - if (FParse::Command(Str, TEXT("WITH_EDITORONLY_DATA"))) - { - return EBlockDirectiveType::WithEditorOnlyData; - } - - return EBlockDirectiveType::UnrecognizedBlock; -} - -const TCHAR* GetBlockDirectiveTypeString(EBlockDirectiveType DirectiveType) -{ - switch (DirectiveType) - { - case EBlockDirectiveType::CPPBlock: return TEXT("CPP"); - case EBlockDirectiveType::NotCPPBlock: return TEXT("!CPP"); - case EBlockDirectiveType::ZeroBlock: return TEXT("0"); - case EBlockDirectiveType::OneBlock: return TEXT("1"); - case EBlockDirectiveType::WithHotReload: return TEXT("WITH_HOT_RELOAD"); - case EBlockDirectiveType::WithEditor: return TEXT("WITH_EDITOR"); - case EBlockDirectiveType::WithEditorOnlyData: return TEXT("WITH_EDITORONLY_DATA"); - case EBlockDirectiveType::UnrecognizedBlock: return TEXT(""); - } - - check(false); - UE_ASSUME(false); -} - -// Performs a preliminary parse of the text in the specified buffer, pulling out useful information for the header generation process -void FHeaderParser::SimplifiedClassParse(const TCHAR* Filename, const TCHAR* InBuffer, TArray& OutParsedClassArray, TArray& DependentOn, FStringOutputDevice& ClassHeaderTextStrippedOfCppText, FClassDeclarations& ClassDeclarations) -{ - 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 CurrentLine = 0; - const TCHAR* Buffer = InBuffer; - - // Preprocessor pass - while (FParse::Line(&Buffer, StrLine, true)) - { - CurrentLine++; - const TCHAR* Str = *StrLine; - int32 BraceCount = 0; - - bool bIf = FParse::Command(&Str,TEXT("#if")); - if( bIf || FParse::Command(&Str,TEXT("#ifdef")) || FParse::Command(&Str,TEXT("#ifndef")) ) - { - EBlockDirectiveType RootDirective; - if (bIf) - { - RootDirective = ParseCommandToBlockDirectiveType(&Str); - } - else - { - // #ifdef or #ifndef are always treated as CPP - RootDirective = EBlockDirectiveType::UnrecognizedBlock; - } - - TArray> DirectiveStack; - DirectiveStack.Push(RootDirective); - - bool bShouldKeepBlockContents = ShouldKeepBlockContents(RootDirective); - bool bIsZeroBlock = RootDirective == EBlockDirectiveType::ZeroBlock; - - ClassHeaderTextStrippedOfCppText.Logf(TEXT("%s\r\n"), ShouldKeepDirective(RootDirective) ? *StrLine : TEXT("")); - - while ((DirectiveStack.Num() > 0) && FParse::Line(&Buffer, StrLine, 1)) - { - CurrentLine++; - Str = *StrLine; - - bool bShouldKeepLine = bShouldKeepBlockContents; - - bool bIsDirective = false; - if( FParse::Command(&Str,TEXT("#endif")) ) - { - EBlockDirectiveType OldDirective = DirectiveStack.Pop(); - - bShouldKeepLine &= ShouldKeepDirective(OldDirective); - bIsDirective = true; - } - else if( FParse::Command(&Str,TEXT("#if")) || FParse::Command(&Str,TEXT("#ifdef")) || FParse::Command(&Str,TEXT("#ifndef")) ) - { - EBlockDirectiveType Directive = ParseCommandToBlockDirectiveType(&Str); - DirectiveStack.Push(Directive); - - bShouldKeepLine &= ShouldKeepDirective(Directive); - bIsDirective = true; - } - else if (FParse::Command(&Str,TEXT("#elif"))) - { - EBlockDirectiveType NewDirective = ParseCommandToBlockDirectiveType(&Str); - EBlockDirectiveType OldDirective = DirectiveStack.Top(); - - // Check to see if we're mixing ignorable directive types - we don't support this - bool bKeepNewDirective = ShouldKeepDirective(NewDirective); - bool bKeepOldDirective = ShouldKeepDirective(OldDirective); - if (bKeepNewDirective != bKeepOldDirective) - { - FFileLineException::Throwf( - Filename, - CurrentLine, - TEXT("Mixing %s with %s in an #elif preprocessor block is not supported"), - GetBlockDirectiveTypeString(OldDirective), - GetBlockDirectiveTypeString(NewDirective) - ); - } - - DirectiveStack.Top() = NewDirective; - - bShouldKeepLine &= bKeepNewDirective; - bIsDirective = true; - } - else if (FParse::Command(&Str, TEXT("#else"))) - { - switch (DirectiveStack.Top()) - { - case EBlockDirectiveType::ZeroBlock: - DirectiveStack.Top() = EBlockDirectiveType::OneBlock; - break; - - case EBlockDirectiveType::OneBlock: - DirectiveStack.Top() = EBlockDirectiveType::ZeroBlock; - break; - - case EBlockDirectiveType::CPPBlock: - DirectiveStack.Top() = EBlockDirectiveType::NotCPPBlock; - break; - - case EBlockDirectiveType::NotCPPBlock: - DirectiveStack.Top() = EBlockDirectiveType::CPPBlock; - break; - - case EBlockDirectiveType::WithHotReload: - FFileLineException::Throwf(Filename, CurrentLine, TEXT("Bad preprocessor directive in metadata declaration: %s; Only 'CPP', '1' and '0' can have #else directives"), *ClassName); - - case EBlockDirectiveType::UnrecognizedBlock: - case EBlockDirectiveType::WithEditor: - case EBlockDirectiveType::WithEditorOnlyData: - // We allow unrecognized directives, WITH_EDITOR and WITH_EDITORONLY_DATA to have #else blocks. - // However, we don't actually change how UHT processes these #else blocks. - break; - } - - bShouldKeepLine &= ShouldKeepDirective(DirectiveStack.Top()); - bIsDirective = true; - } - else - { - // Check for UHT identifiers inside skipped blocks, unless it's a zero block, because the compiler is going to skip those anyway. - if (!bShouldKeepBlockContents && !bIsZeroBlock) - { - auto FindInitialStr = [](const TCHAR*& FoundSubstr, const FString& StrToSearch, const TCHAR* ConstructName) -> bool - { - if (StrToSearch.StartsWith(ConstructName, ESearchCase::CaseSensitive)) - { - FoundSubstr = ConstructName; - return true; - } - - return false; - }; - - FString TrimmedStrLine = StrLine; - TrimmedStrLine.TrimStartInline(); - - const TCHAR* FoundSubstr = nullptr; - if (FindInitialStr(FoundSubstr, TrimmedStrLine, TEXT("UPROPERTY")) - || FindInitialStr(FoundSubstr, TrimmedStrLine, TEXT("UCLASS")) - || FindInitialStr(FoundSubstr, TrimmedStrLine, TEXT("USTRUCT")) - || FindInitialStr(FoundSubstr, TrimmedStrLine, TEXT("UENUM")) - || FindInitialStr(FoundSubstr, TrimmedStrLine, TEXT("UINTERFACE")) - || FindInitialStr(FoundSubstr, TrimmedStrLine, TEXT("UDELEGATE")) - || FindInitialStr(FoundSubstr, TrimmedStrLine, TEXT("UFUNCTION"))) - { - FFileLineException::Throwf(Filename, CurrentLine, TEXT("%s must not be inside preprocessor blocks, except for WITH_EDITORONLY_DATA"), FoundSubstr); - } - - // Try and determine if this line contains something like a serialize function - if (TrimmedStrLine.Len() > 0) - { - static const FString Str_Void = TEXT("void"); - static const FString Str_Serialize = TEXT("Serialize("); - static const FString Str_FArchive = TEXT("FArchive"); - static const FString Str_FStructuredArchive = TEXT("FStructuredArchive::FSlot"); - - int32 Pos = 0; - if ((Pos = TrimmedStrLine.Find(Str_Void, ESearchCase::CaseSensitive, ESearchDir::FromStart, Pos)) != -1) - { - Pos += Str_Void.Len(); - if ((Pos = TrimmedStrLine.Find(Str_Serialize, ESearchCase::CaseSensitive, ESearchDir::FromStart, Pos)) != -1) - { - Pos += Str_Serialize.Len(); - - if (((TrimmedStrLine.Find(Str_FArchive, ESearchCase::CaseSensitive, ESearchDir::FromStart, Pos)) != -1) || - ((TrimmedStrLine.Find(Str_FStructuredArchive, ESearchCase::CaseSensitive, ESearchDir::FromStart, Pos)) != -1)) - { - FFileLineException::Throwf(Filename, CurrentLine, TEXT("'%s' must not be inside preprocessor blocks, except for WITH_EDITORONLY_DATA"), *TrimmedStrLine); - } - } - } - } - } - } - - ClassHeaderTextStrippedOfCppText.Logf(TEXT("%s\r\n"), bShouldKeepLine ? *StrLine : TEXT("")); - - if (bIsDirective) - { - bShouldKeepBlockContents = Algo::AllOf(DirectiveStack, &ShouldKeepBlockContents); - bIsZeroBlock = DirectiveStack.Contains(EBlockDirectiveType::ZeroBlock); - } - } - } - else if ( FParse::Command(&Str,TEXT("#include")) ) - { - ClassHeaderTextStrippedOfCppText.Logf( TEXT("%s\r\n"), *StrLine ); - } - else - { - ClassHeaderTextStrippedOfCppText.Logf( TEXT("%s\r\n"), *StrLine ); - } - } - - // now start over go look for the class - - int32 CommentDim = 0; - CurrentLine = 0; - Buffer = *ClassHeaderTextStrippedOfCppText; - - const TCHAR* StartOfLine = Buffer; - bool bFoundGeneratedInclude = false; - bool bFoundExportedClasses = 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")) ) - { - // Handle #include directives as if they were 'dependson' keywords. - const FString& DependsOnHeaderName = Str; - - if (DependsOnHeaderName != TEXT("\"UObject/DefineUPropertyMacros.h\"") && DependsOnHeaderName != TEXT("\"UObject/UndefineUPropertyMacros.h\"")) - { - if (bFoundGeneratedInclude) - { - FFileLineException::Throwf(Filename, CurrentLine, TEXT("#include found after .generated.h file - the .generated.h file should always be the last #include in a header")); - } - - 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. - DependentOn.Add(FHeaderProvider(EHeaderProviderSourceType::FileName, FPaths::GetCleanFilename(DependsOnHeaderName.Mid(1, HeaderFilenameEnd - 1)))); - } - } - } - } - 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; - } - } - } - - // Find the first '/' and check for '//' or '/*' or '*/' - if (StrLine.FindChar('/', Pos)) - { - if (Pos >= 0) - { - // Stub out the comments, ignoring anything inside literal strings. - Pos = StrLine.Find(TEXT("//"), ESearchCase::CaseSensitive, ESearchDir::FromStart, Pos); - - // Check if first slash is end of multiline comment and adjust position if necessary. - if (Pos > 0 && StrLine[Pos - 1] == TEXT('*')) - { - ++Pos; - } - - if (Pos >= 0) - { - if (StrBegin == INDEX_NONE || Pos < StrBegin || Pos > StrEnd) - { - StrLine.LeftInline(Pos, false); - } - - if (StrLine.IsEmpty()) - { - continue; - } - } - - // look for a / * ... * / block, ignoring anything inside literal strings - Pos = StrLine.Find(TEXT("/*"), ESearchCase::CaseSensitive, ESearchDir::FromStart, Pos); - EndPos = StrLine.Find(TEXT("*/"), ESearchCase::CaseSensitive, ESearchDir::FromStart, FMath::Max(0, Pos - 1)); - 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.LeftInline(Pos, false); - CommentDim++; - } - } - bProcess = CommentDim <= 1; - } - - if (EndPos >= 0) - { - if (StrBegin == INDEX_NONE || EndPos < StrBegin || EndPos > StrEnd) - { - StrLine.MidInline(EndPos + 2, MAX_int32, false); - CommentDim--; - } - - bProcess = CommentDim <= 0; - } - } - } - - StrLine.TrimStartInline(); - if (!bProcess || StrLine.IsEmpty()) - { - continue; - } - - Str = *StrLine; - - // Get class or interface name - if (const TCHAR* UInterfaceMacroDecl = FCString::Strfind(Str, TEXT("UINTERFACE"))) - { - if (UInterfaceMacroDecl == FCString::Strspn(Str, TEXT("\t ")) + Str) - { - if (UInterfaceMacroDecl[10] != TEXT('(')) - { - FFileLineException::Throwf(Filename, CurrentLine, TEXT("Missing open parenthesis after UINTERFACE")); - } - - FName StrippedInterfaceName; - Parser.ParseClassDeclaration(Filename, StartOfLine + (UInterfaceMacroDecl - Str), CurrentLine, TEXT("UINTERFACE"), /*out*/ StrippedInterfaceName, /*out*/ ClassName, /*out*/ BaseClassName, /*out*/ DependentOn, OutParsedClassArray, ClassDeclarations); - OutParsedClassArray.Add(FSimplifiedParsingClassInfo(MoveTemp(ClassName), MoveTemp(BaseClassName), CurrentLine, true)); - if (!bFoundExportedClasses) - { - if (FClassDeclarationMetaData* Found = ClassDeclarations.Find(StrippedInterfaceName)) - { - bFoundExportedClasses = !(Found->ClassFlags & CLASS_NoExport); - } - } - } - } - - if (const TCHAR* UClassMacroDecl = FCString::Strfind(Str, TEXT("UCLASS"))) - { - if (UClassMacroDecl == FCString::Strspn(Str, TEXT("\t ")) + Str) - { - if (UClassMacroDecl[6] != TEXT('(')) - { - FFileLineException::Throwf(Filename, CurrentLine, TEXT("Missing open parenthesis after UCLASS")); - } - - FName StrippedClassName; - Parser.ParseClassDeclaration(Filename, StartOfLine + (UClassMacroDecl - Str), CurrentLine, TEXT("UCLASS"), /*out*/ StrippedClassName, /*out*/ ClassName, /*out*/ BaseClassName, /*out*/ DependentOn, OutParsedClassArray, ClassDeclarations); - OutParsedClassArray.Add(FSimplifiedParsingClassInfo(MoveTemp(ClassName), MoveTemp(BaseClassName), CurrentLine, false)); - if (!bFoundExportedClasses) - { - if (FClassDeclarationMetaData* Found = ClassDeclarations.Find(StrippedClassName)) - { - bFoundExportedClasses = !(Found->ClassFlags & CLASS_NoExport); - } - } - } - } - } - - StartOfLine = Buffer; - } - - if (bFoundExportedClasses && !bFoundGeneratedInclude) - { - FError::Throwf(TEXT("No #include found for the .generated.h file - the .generated.h file should always be the last #include in a header")); - } -} - -///////////////////////////////////////////////////// -// FHeaderPreParser - -void FHeaderPreParser::ParseClassDeclaration(const TCHAR* Filename, const TCHAR* InputText, int32 InLineNumber, const TCHAR* StartingMatchID, FName& out_StrippedClassName, FString& out_ClassName, FString& out_BaseClassName, TArray& out_RequiredIncludes, const TArray& ParsedClassArray, FClassDeclarations& ClassDeclarations) -{ - const TCHAR* ErrorMsg = TEXT("Class declaration"); - - ResetParser(InputText, InLineNumber); - - // Require 'UCLASS' or 'UINTERFACE' - RequireIdentifier(StartingMatchID, ESearchCase::CaseSensitive, ErrorMsg); - - // New-style UCLASS() syntax - TMap MetaData; - TArray SpecifiersFound; - ReadSpecifierSetInsideMacro(SpecifiersFound, ErrorMsg, MetaData); - - // Require 'class' - RequireIdentifier(TEXT("class"), ESearchCase::CaseSensitive, ErrorMsg); - - // alignas() can come before or after the deprecation macro. - // We can't have both, but the compiler will catch that anyway. - SkipAlignasIfNecessary(*this); - SkipDeprecatedMacroIfNecessary(*this); - SkipAlignasIfNecessary(*this); - - // Read the class name - FString RequiredAPIMacroIfPresent; - ParseNameWithPotentialAPIMacroPrefix(/*out*/ out_ClassName, /*out*/ RequiredAPIMacroIfPresent, StartingMatchID); - - FString ClassNameWithoutPrefixStr = GetClassNameWithPrefixRemoved(out_ClassName); - out_StrippedClassName = *ClassNameWithoutPrefixStr; - - { - auto ConstructDeclarationData = [MetaData = MoveTemp(MetaData), SpecifiersFound = MoveTemp(SpecifiersFound), &RequiredAPIMacroIfPresent]() mutable - { - // Add class declaration meta data so that we can access class flags before the class is fully parsed - TSharedRef DeclarationData = MakeShareable(new FClassDeclarationMetaData()); - DeclarationData->MetaData = MoveTemp(MetaData); - DeclarationData->ParseClassProperties(MoveTemp(SpecifiersFound), RequiredAPIMacroIfPresent); - return DeclarationData; - }; - - ClassDeclarations.AddIfMissing(out_StrippedClassName, MoveTemp(ConstructDeclarationData)); - } - - // Skip optional final keyword - MatchIdentifier(TEXT("final"), ESearchCase::CaseSensitive); - - // Handle inheritance - if (MatchSymbol(TEXT(':'))) - { - // Require 'public' - RequireIdentifier(TEXT("public"), ESearchCase::CaseSensitive, ErrorMsg); - - // Inherits from something - FToken BaseClassNameToken; - if (!GetIdentifier(BaseClassNameToken, true)) - { - FError::Throwf(TEXT("Expected a base class name")); - } - - out_BaseClassName = BaseClassNameToken.Identifier; - - int32 InputLineLocal = InputLine; - auto AddDependencyIfNeeded = [Filename, InputLineLocal, &ParsedClassArray, &out_RequiredIncludes, &ClassNameWithoutPrefixStr](const FString& DependencyClassName) - { - if (!Algo::FindBy(ParsedClassArray, DependencyClassName, &FSimplifiedParsingClassInfo::GetClassName)) - { - FString DependencyClassNameWithoutPrefixStr = GetClassNameWithPrefixRemoved(DependencyClassName); - - if (ClassNameWithoutPrefixStr == DependencyClassNameWithoutPrefixStr) - { - FFileLineException::Throwf(Filename, InputLineLocal, TEXT("A class cannot inherit itself or a type with the same name but a different prefix")); - } - - FString StrippedDependencyName = DependencyClassName.Mid(1); - - // Only add a stripped dependency if the stripped name differs from the stripped class name - // otherwise it's probably a class with a different prefix. - if (StrippedDependencyName != ClassNameWithoutPrefixStr) - { - out_RequiredIncludes.Add(FHeaderProvider(EHeaderProviderSourceType::ClassName, MoveTemp(StrippedDependencyName))); - } - } - }; - - AddDependencyIfNeeded(out_BaseClassName); - - // Get additional inheritance links and rack them up as dependencies if they're UObject derived - while (MatchSymbol(TEXT(','))) - { - // Require 'public' - RequireIdentifier(TEXT("public"), ESearchCase::CaseSensitive, ErrorMsg); - - FToken InterfaceClassNameToken; - if (!GetIdentifier(InterfaceClassNameToken, true)) - { - FFileLineException::Throwf(Filename, InputLine, TEXT("Expected an interface class name")); - } - - AddDependencyIfNeeded(FString(InterfaceClassNameToken.Identifier)); - } - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool FHeaderParser::DefaultValueStringCppFormatToInnerFormat(const FProperty* Property, const FString& CppForm, FString &OutForm) -{ - OutForm = FString(); - if (!Property || CppForm.IsEmpty()) - { - return false; - } - - if (Property->IsA(FClassProperty::StaticClass()) || Property->IsA(FObjectPropertyBase::StaticClass())) - { - const bool bIsNull = FDefaultValueHelper::Is(CppForm, TEXT("NULL")) || FDefaultValueHelper::Is(CppForm, TEXT("nullptr")) || FDefaultValueHelper::Is(CppForm, TEXT("0")); - if (bIsNull) - { - OutForm = TEXT("None"); - } - return bIsNull; // always return as null is the only the processing we can do for object defaults - } - - auto ValidateEnumEntry = [Property, &CppForm](const UEnum* Enum, const FString& EnumValue) - { - const int32 EnumEntryIndex = Enum->GetIndexByName(*EnumValue); - if (EnumEntryIndex == INDEX_NONE) - { - return false; - } - if (Enum->HasMetaData(TEXT("Hidden"), EnumEntryIndex)) - { - FError::Throwf(TEXT("Hidden enum entries cannot be used as default values: %s \"%s\" "), *Property->GetName(), *CppForm); - } - return true; - }; - - if( !Property->IsA(FStructProperty::StaticClass()) ) - { - if( Property->IsA(FIntProperty::StaticClass()) ) - { - int32 Value; - if( FDefaultValueHelper::ParseInt( CppForm, Value) ) - { - OutForm = FString::FromInt(Value); - } - } - else if (Property->IsA(FInt64Property::StaticClass())) - { - int64 Value; - if (FDefaultValueHelper::ParseInt64(CppForm, Value)) - { - OutForm = FString::Printf(TEXT("%lld"), Value); - } - } - else if( Property->IsA(FByteProperty::StaticClass()) ) - { - const UEnum* Enum = CastFieldChecked(const_cast(Property))->Enum; - if( NULL != Enum ) - { - OutForm = FDefaultValueHelper::GetUnqualifiedEnumValue(FDefaultValueHelper::RemoveWhitespaces(CppForm)); - return ValidateEnumEntry(Enum, OutForm); - } - int32 Value; - if( FDefaultValueHelper::ParseInt( CppForm, Value) ) - { - OutForm = FString::FromInt(Value); - return ( 0 <= Value ) && ( 255 >= Value ); - } - } - else if( Property->IsA(FEnumProperty::StaticClass()) ) - { - const FEnumProperty* EnumProp = CastFieldChecked(const_cast(Property)); - if (const UEnum* Enum = CastFieldChecked(const_cast(Property))->GetEnum()) - { - OutForm = FDefaultValueHelper::GetUnqualifiedEnumValue(FDefaultValueHelper::RemoveWhitespaces(CppForm)); - return ValidateEnumEntry(Enum, OutForm); - } - - int64 Value; - if (FDefaultValueHelper::ParseInt64(CppForm, Value)) - { - OutForm = LexToString(Value); - return EnumProp->GetUnderlyingProperty()->CanHoldValue(Value); - } - } - else if( Property->IsA(FFloatProperty::StaticClass()) ) - { - float Value; - if( FDefaultValueHelper::ParseFloat( CppForm, Value) ) - { - OutForm = FString::Printf( TEXT("%f"), Value) ; - } - } - else if( Property->IsA(FDoubleProperty::StaticClass()) ) - { - double Value; - if( FDefaultValueHelper::ParseDouble( CppForm, Value) ) - { - OutForm = FString::Printf( TEXT("%f"), Value) ; - } - } - else if( Property->IsA(FBoolProperty::StaticClass()) ) - { - if( FDefaultValueHelper::Is(CppForm, TEXT("true")) || - FDefaultValueHelper::Is(CppForm, TEXT("false")) ) - { - OutForm = FDefaultValueHelper::RemoveWhitespaces( CppForm ); - } - } - else if( Property->IsA(FNameProperty::StaticClass()) ) - { - if(FDefaultValueHelper::Is( CppForm, TEXT("NAME_None") )) - { - OutForm = TEXT("None"); - return true; - } - return FDefaultValueHelper::StringFromCppString(CppForm, TEXT("FName"), OutForm); - } - else if( Property->IsA(FTextProperty::StaticClass()) ) - { - // Handle legacy cases of FText::FromString being used as default values - // These should be replaced with INVTEXT as FText::FromString can produce inconsistent keys - if (FDefaultValueHelper::StringFromCppString(CppForm, TEXT("FText::FromString"), OutForm)) - { - UE_LOG_WARNING_UHT(TEXT("FText::FromString should be replaced with INVTEXT for default parameter values")); - return true; - } - - // Parse the potential value into an instance - FText ParsedText; - if (FDefaultValueHelper::Is(CppForm, TEXT("FText()")) || FDefaultValueHelper::Is(CppForm, TEXT("FText::GetEmpty()"))) - { - ParsedText = FText::GetEmpty(); - } - else - { - static const FString UHTDummyNamespace = TEXT("__UHT_DUMMY_NAMESPACE__"); - - if (!FTextStringHelper::ReadFromBuffer(*CppForm, ParsedText, *UHTDummyNamespace, nullptr, /*bRequiresQuotes*/true)) - { - return false; - } - - // If the namespace of the parsed text matches the default we gave then this was a LOCTEXT macro which we - // don't allow in default values as they rely on an external macro that is known to C++ but not to UHT - // TODO: UHT could parse these if it tracked the current LOCTEXT_NAMESPACE macro as it parsed - if (TOptional ParsedTextNamespace = FTextInspector::GetNamespace(ParsedText)) - { - if (ParsedTextNamespace.GetValue().Equals(UHTDummyNamespace)) - { - FError::Throwf(TEXT("LOCTEXT default parameter values are not supported; use NSLOCTEXT instead: %s \"%s\" "), *Property->GetName(), *CppForm); - } - } - } - - // Normalize the default value from the parsed value - FTextStringHelper::WriteToBuffer(OutForm, ParsedText, /*bRequiresQuotes*/false); - return true; - } - else if( Property->IsA(FStrProperty::StaticClass()) ) - { - return FDefaultValueHelper::StringFromCppString(CppForm, TEXT("FString"), OutForm); - } - } - else - { - // Cache off the struct types, in case we need them later - UPackage* CoreUObjectPackage = UObject::StaticClass()->GetOutermost(); - static const UScriptStruct* VectorStruct = FindObjectChecked(CoreUObjectPackage, TEXT("Vector")); - static const UScriptStruct* Vector2DStruct = FindObjectChecked(CoreUObjectPackage, TEXT("Vector2D")); - static const UScriptStruct* RotatorStruct = FindObjectChecked(CoreUObjectPackage, TEXT("Rotator")); - static const UScriptStruct* LinearColorStruct = FindObjectChecked(CoreUObjectPackage, TEXT("LinearColor")); - static const UScriptStruct* ColorStruct = FindObjectChecked(CoreUObjectPackage, TEXT("Color")); - - const FStructProperty* StructProperty = CastFieldChecked(const_cast(Property)); - if( StructProperty->Struct == VectorStruct ) - { - FString Parameters; - if(FDefaultValueHelper::Is( CppForm, TEXT("FVector::ZeroVector") )) - { - return true; - } - else if(FDefaultValueHelper::Is(CppForm, TEXT("FVector::UpVector"))) - { - OutForm = FString::Printf(TEXT("%f,%f,%f"), - FVector::UpVector.X, FVector::UpVector.Y, FVector::UpVector.Z); - } - else if(FDefaultValueHelper::Is(CppForm, TEXT("FVector::ForwardVector"))) - { - OutForm = FString::Printf(TEXT("%f,%f,%f"), - FVector::ForwardVector.X, FVector::ForwardVector.Y, FVector::ForwardVector.Z); - } - else if(FDefaultValueHelper::Is(CppForm, TEXT("FVector::RightVector"))) - { - OutForm = FString::Printf(TEXT("%f,%f,%f"), - FVector::RightVector.X, FVector::RightVector.Y, FVector::RightVector.Z); - } - else if( FDefaultValueHelper::GetParameters(CppForm, TEXT("FVector"), Parameters) ) - { - if( FDefaultValueHelper::Is(Parameters, TEXT("ForceInit")) ) - { - return true; - } - FVector Vector; - float Value; - if (FDefaultValueHelper::ParseVector(Parameters, Vector)) - { - OutForm = FString::Printf(TEXT("%f,%f,%f"), - Vector.X, Vector.Y, Vector.Z); - } - else if (FDefaultValueHelper::ParseFloat(Parameters, Value)) - { - OutForm = FString::Printf(TEXT("%f,%f,%f"), - Value, Value, Value); - } - } - } - 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)) - { - OutForm = FString::Printf(TEXT("%f,%f,%f"), - Rotator.Pitch, Rotator.Yaw, Rotator.Roll); - } - } - } - else if( StructProperty->Struct == Vector2DStruct ) - { - if(FDefaultValueHelper::Is( CppForm, TEXT("FVector2D::ZeroVector") )) - { - return true; - } - if(FDefaultValueHelper::Is(CppForm, TEXT("FVector2D::UnitVector"))) - { - OutForm = FString::Printf(TEXT("(X=%3.3f,Y=%3.3f)"), - FVector2D::UnitVector.X, FVector2D::UnitVector.Y); - } - FString Parameters; - if( FDefaultValueHelper::GetParameters(CppForm, TEXT("FVector2D"), Parameters) ) - { - if( FDefaultValueHelper::Is(Parameters, TEXT("ForceInit")) ) - { - return true; - } - FVector2D Vector2D; - if(FDefaultValueHelper::ParseVector2D(Parameters, Vector2D)) - { - OutForm = FString::Printf(TEXT("(X=%3.3f,Y=%3.3f)"), - Vector2D.X, Vector2D.Y); - } - } - } - 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(); - } - } - } - } - 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(); - } - } - } - } - } - - return !OutForm.IsEmpty(); -} - -bool FHeaderParser::TryToMatchConstructorParameterList(FToken Token) -{ - FToken PotentialParenthesisToken; - if (!GetToken(PotentialParenthesisToken)) - { - return false; - } - - if (!PotentialParenthesisToken.Matches(TEXT('('))) - { - UngetToken(PotentialParenthesisToken); - return false; - } - - FClassMetaData* ClassData = GScriptHelper.FindClassData(GetCurrentClass()); - check(ClassData); - - bool bOICtor = false; - bool bVTCtor = false; - - if (!ClassData->bDefaultConstructorDeclared && MatchSymbol(TEXT(')'))) - { - ClassData->bDefaultConstructorDeclared = true; - } - else if (!ClassData->bObjectInitializerConstructorDeclared - || !ClassData->bCustomVTableHelperConstructorDeclared - ) - { - FToken ObjectInitializerParamParsingToken; - - bool bIsConst = false; - bool bIsRef = false; - int32 ParenthesesNestingLevel = 1; - - while (ParenthesesNestingLevel && GetToken(ObjectInitializerParamParsingToken)) - { - // Template instantiation or additional parameter excludes ObjectInitializer constructor. - if (ObjectInitializerParamParsingToken.Matches(TEXT(',')) || ObjectInitializerParamParsingToken.Matches(TEXT('<'))) - { - bOICtor = false; - bVTCtor = false; - break; - } - - if (ObjectInitializerParamParsingToken.Matches(TEXT('('))) - { - ParenthesesNestingLevel++; - continue; - } - - if (ObjectInitializerParamParsingToken.Matches(TEXT(')'))) - { - ParenthesesNestingLevel--; - continue; - } - - if (ObjectInitializerParamParsingToken.Matches(TEXT("const"), ESearchCase::CaseSensitive)) - { - bIsConst = true; - continue; - } - - if (ObjectInitializerParamParsingToken.Matches(TEXT('&'))) - { - bIsRef = true; - continue; - } - - if (ObjectInitializerParamParsingToken.Matches(TEXT("FObjectInitializer"), ESearchCase::CaseSensitive) - || ObjectInitializerParamParsingToken.Matches(TEXT("FPostConstructInitializeProperties"), ESearchCase::CaseSensitive) // Deprecated, but left here, so it won't break legacy code. - ) - { - bOICtor = true; - } - - if (ObjectInitializerParamParsingToken.Matches(TEXT("FVTableHelper"), ESearchCase::CaseSensitive)) - { - bVTCtor = true; - } - } - - // Parse until finish. - while (ParenthesesNestingLevel && GetToken(ObjectInitializerParamParsingToken)) - { - if (ObjectInitializerParamParsingToken.Matches(TEXT('('))) - { - ParenthesesNestingLevel++; - continue; - } - - if (ObjectInitializerParamParsingToken.Matches(TEXT(')'))) - { - ParenthesesNestingLevel--; - continue; - } - } - - ClassData->bObjectInitializerConstructorDeclared = ClassData->bObjectInitializerConstructorDeclared || (bOICtor && bIsRef && bIsConst); - ClassData->bCustomVTableHelperConstructorDeclared = ClassData->bCustomVTableHelperConstructorDeclared || (bVTCtor && bIsRef); - } - - ClassData->bConstructorDeclared = ClassData->bConstructorDeclared || !bVTCtor; - - // 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; -} - -void FHeaderParser::CompileVersionDeclaration(UStruct* Struct) -{ - FUnrealSourceFile* CurrentSourceFilePtr = GetCurrentSourceFile(); - TSharedRef CurrentSrcFile = CurrentSourceFilePtr->AsShared(); - // 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. - EGeneratedCodeVersion 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 - && Token.Matches(TEXT(')'))) - { - CurrentSrcFile->GetGeneratedCodeVersions().FindOrAdd(Struct) = Version; - UngetToken(Token); - return; - } - - // Overwrite with version specified by macro. - Version = ToGeneratedCodeVersion(Token.Identifier); - - CurrentSrcFile->GetGeneratedCodeVersions().FindOrAdd(Struct) = Version; -} - -void FHeaderParser::ResetClassData() -{ - UClass* CurrentClass = GetCurrentClass(); - CurrentClass->PropertiesSize = 0; - - // Set class flags and within. - CurrentClass->ClassFlags &= ~CLASS_RecompilerClear; - - if (UClass* SuperClass = CurrentClass->GetSuperClass()) - { - CurrentClass->ClassFlags |= (SuperClass->ClassFlags) & CLASS_ScriptInherit; - CurrentClass->ClassConfigName = SuperClass->ClassConfigName; - check(SuperClass->ClassWithin); - if (CurrentClass->ClassWithin == nullptr) - { - CurrentClass->ClassWithin = SuperClass->ClassWithin; - } - - // Copy special categories from parent - if (SuperClass->HasMetaData(FHeaderParserNames::NAME_HideCategories)) - { - CurrentClass->SetMetaData(FHeaderParserNames::NAME_HideCategories, *SuperClass->GetMetaData(FHeaderParserNames::NAME_HideCategories)); - } - if (SuperClass->HasMetaData(FHeaderParserNames::NAME_ShowCategories)) - { - CurrentClass->SetMetaData(FHeaderParserNames::NAME_ShowCategories, *SuperClass->GetMetaData(FHeaderParserNames::NAME_ShowCategories)); - } - if (SuperClass->HasMetaData(FHeaderParserNames::NAME_SparseClassDataTypes)) - { - CurrentClass->SetMetaData(FHeaderParserNames::NAME_SparseClassDataTypes, *SuperClass->GetMetaData(FHeaderParserNames::NAME_SparseClassDataTypes)); - } - if (SuperClass->HasMetaData(NAME_HideFunctions)) - { - CurrentClass->SetMetaData(NAME_HideFunctions, *SuperClass->GetMetaData(NAME_HideFunctions)); - } - if (SuperClass->HasMetaData(NAME_AutoExpandCategories)) - { - CurrentClass->SetMetaData(NAME_AutoExpandCategories, *SuperClass->GetMetaData(NAME_AutoExpandCategories)); - } - if (SuperClass->HasMetaData(NAME_AutoCollapseCategories)) - { - CurrentClass->SetMetaData(NAME_AutoCollapseCategories, *SuperClass->GetMetaData(NAME_AutoCollapseCategories)); - } - } - - check(CurrentClass->ClassWithin); -} - -void FHeaderParser::PostPopNestClass(UClass* CurrentClass) -{ - // Validate all the rep notify events here, to make sure they're implemented - VerifyPropertyMarkups(CurrentClass); - - // Iterate over all the interfaces we claim to implement - for (FImplementedInterface& 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 (UFunction* InterfaceFunction : TFieldRange(Interface, EFieldIteratorFlags::ExcludeSuper)) - { - bool bImplemented = false; - - // And try to find one that matches - for (UFunction* ClassFunction : TFieldRange(CurrentClass)) - { - if (ClassFunction->GetFName() != InterfaceFunction->GetFName()) - { - continue; - } - - if ((InterfaceFunction->FunctionFlags & FUNC_Event) && !(ClassFunction->FunctionFlags & FUNC_Event)) - { - FError::Throwf(TEXT("Implementation of function '%s::%s' must be declared as 'event' to match declaration in interface '%s'"), *ClassFunction->GetOuter()->GetName(), *ClassFunction->GetName(), *Interface->GetName()); - } - - if ((InterfaceFunction->FunctionFlags & FUNC_Delegate) && !(ClassFunction->FunctionFlags & FUNC_Delegate)) - { - FError::Throwf(TEXT("Implementation of function '%s::%s' must be declared as 'delegate' to match declaration in interface '%s'"), *ClassFunction->GetOuter()->GetName(), *ClassFunction->GetName(), *Interface->GetName()); - } - - // Making sure all the parameters match up correctly - bImplemented = 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 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) - { - bImplemented = 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 (!bImplemented - && InterfaceFunction->HasAnyFunctionFlags(FUNC_BlueprintCallable) - && !InterfaceFunction->HasAnyFunctionFlags(FUNC_BlueprintEvent) - && !Interface->HasMetaData(NAME_CannotImplementInterfaceInBlueprint)) // FBlueprintMetadata::MD_CannotImplementInterfaceInBlueprint - { - 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 DelegateCache; - FixupDelegateProperties(AllClasses, PoppedFunction, *GetCurrentScope(), DelegateCache); - } -} - -void FHeaderParser::PostPopNestInterface(FClasses& AllClasses, UClass* CurrentInterface) -{ - FClassMetaData* ClassData = GScriptHelper.FindClassData(CurrentInterface); - check(ClassData); - if (ClassData->ContainsDelegates()) - { - TMap DelegateCache; - FixupDelegateProperties(AllClasses, CurrentInterface, FScope::GetTypeScope(ExactCast(CurrentInterface)).Get(), DelegateCache); - } -} - -FDocumentationPolicy FHeaderParser::GetDocumentationPolicyFromName(const FString& PolicyName) -{ - FDocumentationPolicy DocumentationPolicy; - if (FCString::Strcmp(*PolicyName, TEXT("Strict")) == 0) - { - DocumentationPolicy.bClassOrStructCommentRequired = true; - DocumentationPolicy.bFunctionToolTipsRequired = true; - DocumentationPolicy.bMemberToolTipsRequired = true; - DocumentationPolicy.bParameterToolTipsRequired = true; - DocumentationPolicy.bFloatRangesRequired = true; - } - else - { - FError::Throwf(TEXT("Documentation Policy '%s' not yet supported"), *PolicyName); - } - return DocumentationPolicy; -} - - -FDocumentationPolicy FHeaderParser::GetDocumentationPolicyForStruct(UStruct* Struct) -{ - SCOPE_SECONDS_COUNTER_UHT(DocumentationPolicy); - - check(Struct!= nullptr); - - FDocumentationPolicy DocumentationPolicy; - FString DocumentationPolicyName; - if (Struct->GetStringMetaDataHierarchical(NAME_DocumentationPolicy, &DocumentationPolicyName)) - { - DocumentationPolicy = GetDocumentationPolicyFromName(DocumentationPolicyName); - } - return DocumentationPolicy; -} - -void FHeaderParser::CheckDocumentationPolicyForEnum(UEnum* Enum, const TMap& MetaData, const TArray>& Entries) -{ - SCOPE_SECONDS_COUNTER_UHT(DocumentationPolicy); - - check(Enum != nullptr); - - const FString* DocumentationPolicyName = MetaData.Find(NAME_DocumentationPolicy); - if (DocumentationPolicyName == nullptr) - { - return; - } - - check(!DocumentationPolicyName->IsEmpty()); - - FDocumentationPolicy DocumentationPolicy = GetDocumentationPolicyFromName(*DocumentationPolicyName); - if (DocumentationPolicy.bClassOrStructCommentRequired) - { - const FString* EnumToolTip = MetaData.Find(NAME_ToolTip); - if (EnumToolTip == nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Enum '%s' does not provide a tooltip / comment (DocumentationPolicy)."), *Enum->GetName()); - } - } - - TMap ToolTipToEntry; - for (const TMap& Entry : Entries) - { - const FString* EntryName = Entry.Find(NAME_Name); - if (EntryName == nullptr) - { - continue; - } - - const FString* ToolTip = Entry.Find(NAME_ToolTip); - if (ToolTip == nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Enum entry '%s::%s' does not provide a tooltip / comment (DocumentationPolicy)."), *Enum->GetName(), *(*EntryName)); - continue; - } - - const FString* ExistingEntry = ToolTipToEntry.Find(*ToolTip); - if (ExistingEntry != nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Enum entries '%s::%s' and '%s::%s' have identical tooltips / comments (DocumentationPolicy)."), *Enum->GetName(), *(*ExistingEntry), *Enum->GetName(), *(*EntryName)); - } - ToolTipToEntry.Add(*ToolTip, *EntryName); - } -} - -void FHeaderParser::CheckDocumentationPolicyForStruct(UStruct* Struct) -{ - SCOPE_SECONDS_COUNTER_UHT(DocumentationPolicy); - - check(Struct != nullptr); - - FDocumentationPolicy DocumentationPolicy = GetDocumentationPolicyForStruct(Struct); - if (DocumentationPolicy.bClassOrStructCommentRequired) - { - FString ClassTooltip; - if (const FString* ClassTooltipPtr = Struct->FindMetaData(NAME_ToolTip)) - { - ClassTooltip = *ClassTooltipPtr; - } - - if (ClassTooltip.IsEmpty() || ClassTooltip.Equals(Struct->GetName())) - { - UE_LOG_ERROR_UHT(TEXT("Struct '%s' does not provide a tooltip / comment (DocumentationPolicy)."), *Struct->GetName()); - } - } - - if (DocumentationPolicy.bMemberToolTipsRequired) - { - TMap ToolTipToPropertyName; - for (FProperty* Property : TFieldRange(Struct, EFieldIteratorFlags::ExcludeSuper)) - { - FString ToolTip = Property->GetToolTipText().ToString(); - if (ToolTip.IsEmpty() || ToolTip.Equals(Property->GetDisplayNameText().ToString())) - { - UE_LOG_ERROR_UHT(TEXT("Property '%s::%s' does not provide a tooltip / comment (DocumentationPolicy)."), *Struct->GetName(), *Property->GetName()); - continue; - } - const FName* ExistingPropertyName = ToolTipToPropertyName.Find(ToolTip); - if (ExistingPropertyName != nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Property '%s::%s' and '%s::%s' are using identical tooltips (DocumentationPolicy)."), *Struct->GetName(), *ExistingPropertyName->ToString(), *Struct->GetName(), *Property->GetName()); - } - ToolTipToPropertyName.Add(MoveTemp(ToolTip), Property->GetFName()); - } - } - - if (DocumentationPolicy.bFloatRangesRequired) - { - for (FProperty* Property : TFieldRange(Struct, EFieldIteratorFlags::ExcludeSuper)) - { - if(DoesCPPTypeRequireDocumentation(Property->GetCPPType())) - { - const FString& UIMin = Property->GetMetaData(NAME_UIMin); - const FString& UIMax = Property->GetMetaData(NAME_UIMax); - - if(!CheckUIMinMaxRangeFromMetaData(UIMin, UIMax)) - { - UE_LOG_ERROR_UHT(TEXT("Property '%s::%s' does not provide a valid UIMin / UIMax (DocumentationPolicy)."), *Struct->GetName(), *Property->GetName()); - } - } - } - } - - // also compare all tooltips to see if they are unique - if (DocumentationPolicy.bFunctionToolTipsRequired) - { - UClass* Class = Cast(Struct); - if (Class != nullptr) - { - TMap ToolTipToFunc; - for (UFunction* Func : TFieldRange(Class, EFieldIteratorFlags::ExcludeSuper)) - { - FString ToolTip = Func->GetToolTipText().ToString(); - if (ToolTip.IsEmpty()) - { - UE_LOG_ERROR_UHT(TEXT("Function '%s::%s' does not provide a tooltip / comment (DocumentationPolicy)."), *Class->GetName(), *Func->GetName()); - continue; - } - const FName* ExistingFuncName = ToolTipToFunc.Find(ToolTip); - if (ExistingFuncName != nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Functions '%s::%s' and '%s::%s' uses identical tooltips / comments (DocumentationPolicy)."), *Class->GetName(), *(*ExistingFuncName).ToString(), *Class->GetName(), *Func->GetName()); - } - ToolTipToFunc.Add(MoveTemp(ToolTip), Func->GetFName()); - } - } - } -} - -bool FHeaderParser::DoesCPPTypeRequireDocumentation(const FString& CPPType) -{ - return PropertyCPPTypesRequiringUIRanges.Contains(CPPType); -} - -// Validates the documentation for a given method -void FHeaderParser::CheckDocumentationPolicyForFunc(UClass* Class, UFunction* Func) -{ - SCOPE_SECONDS_COUNTER_UHT(DocumentationPolicy); - - check(Class != nullptr); - check(Func != nullptr); - - FDocumentationPolicy DocumentationPolicy = GetDocumentationPolicyForStruct(Class); - if (DocumentationPolicy.bFunctionToolTipsRequired) - { - const FString* FunctionTooltip = Func->FindMetaData(NAME_ToolTip); - if (FunctionTooltip == nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Function '%s::%s' does not provide a tooltip / comment (DocumentationPolicy)."), *Class->GetName(), *Func->GetName()); - } - } - - if (DocumentationPolicy.bParameterToolTipsRequired) - { - const FString* FunctionComment = Func->FindMetaData(NAME_Comment); - if (FunctionComment == nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Function '%s::%s' does not provide a comment (DocumentationPolicy)."), *Class->GetName(), *Func->GetName()); - return; - } - - TMap ParamToolTips = GetParameterToolTipsFromFunctionComment(*FunctionComment); - bool HasAnyParamToolTips = ParamToolTips.Num() > 0; - if (ParamToolTips.Num() == 0) - { - const FString* ReturnValueToolTip = ParamToolTips.Find(NAME_ReturnValue); - if (ReturnValueToolTip != nullptr) - { - HasAnyParamToolTips = false; - } - } - - // only apply the validation for parameter tooltips if a function has any @param statements at all. - if (HasAnyParamToolTips) - { - // ensure each parameter has a tooltip - TSet ExistingFields; - for (FProperty* Property : TFieldRange(Func)) - { - FName ParamName = Property->GetFName(); - if (ParamName == NAME_ReturnValue) - { - continue; - } - const FString* ParamToolTip = ParamToolTips.Find(ParamName); - if (ParamToolTip == nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Function '%s::%s' doesn't provide a tooltip for parameter '%s' (DocumentationPolicy)."), *Class->GetName(), *Func->GetName(), *ParamName.ToString()); - } - ExistingFields.Add(ParamName); - } - - // ensure we don't have parameter tooltips for parameters that don't exist - for (TPair& Pair : ParamToolTips) - { - const FName& ParamName = Pair.Key; - if (ParamName == NAME_ReturnValue) - { - continue; - } - if (!ExistingFields.Contains(ParamName)) - { - UE_LOG_ERROR_UHT(TEXT("Function '%s::%s' provides a tooltip for an unknown parameter '%s' (DocumentationPolicy)."), *Class->GetName(), *Func->GetName(), *Pair.Key.ToString()); - } - } - - // check for duplicate tooltips - TMap ToolTipToParam; - for (TPair& Pair : ParamToolTips) - { - const FName& ParamName = Pair.Key; - if (ParamName == NAME_ReturnValue) - { - continue; - } - const FName* ExistingParam = ToolTipToParam.Find(Pair.Value); - if (ExistingParam != nullptr) - { - UE_LOG_ERROR_UHT(TEXT("Function '%s::%s' uses identical tooltips for parameters '%s' and '%s' (DocumentationPolicy)."), *Class->GetName(), *Func->GetName(), *ExistingParam->ToString(), *Pair.Key.ToString()); - } - ToolTipToParam.Add(MoveTemp(Pair.Value), Pair.Key); - } - } - } -} - -bool FHeaderParser::IsReservedTypeName(const FString& TypeName) -{ - for(const FString& ReservedName : ReservedTypeNames) - { - if(TypeName == ReservedName) - { - return true; - } - } - return false; -} - -bool FHeaderParser::IsReservedTypeName(const FToken& Token) -{ - for (const FString& ReservedName : ReservedTypeNames) - { - if (Token.Matches(*ReservedName, ESearchCase::IgnoreCase)) - { - return true; - } - } - return false; -} - -bool FHeaderParser::CheckUIMinMaxRangeFromMetaData(const FString& UIMin, const FString& UIMax) -{ - if (UIMin.IsEmpty() || UIMax.IsEmpty()) - { - return false; - } - - double UIMinValue = FCString::Atod(*UIMin); - double UIMaxValue = FCString::Atod(*UIMax); - if (UIMin > UIMax) // note that we actually allow UIMin == UIMax to disable the range manually. - { - return false; - } - - return true; -} - -template -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()); - } - } - } - - TFunctionType* Function = new(EC_InternalUseOnlyConstructor, Outer, FuncInfo.Function.Identifier, RF_Public) TFunctionType(FObjectInitializer(), nullptr); - Function->ReturnValueOffset = MAX_uint16; - Function->FirstPropertyToInit = nullptr; - - if (!CurrentScope->IsFileScope()) - { - UStruct* Struct = ((FStructScope*)CurrentScope)->GetStruct(); - - Function->Next = Struct->Children; - Struct->Children = Function; - } - - return Function; -} - -UFunction* FHeaderParser::CreateFunction(const FFuncInfo &FuncInfo) const -{ - return CreateFunctionImpl(FuncInfo, GetCurrentClass(), GetCurrentScope()); -} - -template -UDelegateFunction* FHeaderParser::CreateDelegateFunction(const FFuncInfo &FuncInfo) const -{ - FFileScope* CurrentFileScope = GetCurrentFileScope(); - FUnrealSourceFile* LocSourceFile = CurrentFileScope ? CurrentFileScope->GetSourceFile() : nullptr; - UObject* CurrentPackage = LocSourceFile ? LocSourceFile->GetPackage() : nullptr; - return CreateFunctionImpl(FuncInfo, IsInAClass() ? (UObject*)GetCurrentClass() : CurrentPackage, GetCurrentScope()); -} diff --git a/Engine/Source/Programs/UHTLite/Private/HeaderParser.h b/Engine/Source/Programs/UHTLite/Private/HeaderParser.h deleted file mode 100644 index 4f0e5c485406..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/HeaderParser.h +++ /dev/null @@ -1,1161 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "ParserHelper.h" -#include "BaseParser.h" -#include "Misc/CompilationResult.h" -#include "Scope.h" - -class UClass; -enum class EGeneratedCodeVersion : uint8; -class FFeedbackContext; -class UPackage; -struct FManifestModule; -class IScriptGeneratorPluginInterface; -class FStringOutputDevice; -class FProperty; -class FUnrealSourceFile; -class UFunction; -class UEnum; -class UScriptStruct; -class UDelegateFunction; -class UStruct; -class FClass; -class FClasses; -class FScope; -class FHeaderProvider; - -extern double GPluginOverheadTime; -extern double GHeaderCodeGenTime; - -/*----------------------------------------------------------------------------- - Constants & types. ------------------------------------------------------------------------------*/ - -enum {MAX_NEST_LEVELS = 16}; - -/* Code nesting types. */ -enum class ENestType -{ - GlobalScope, - Class, - FunctionDeclaration, - Interface, - NativeInterface -}; - -/** Types of statements to allow within a particular nesting block. */ -enum class ENestAllowFlags -{ - None = 0, - Function = 1, // Allow Event declarations at this level. - VarDecl = 2, // Allow variable declarations at this level. - Class = 4, // Allow class definition heading. - Return = 8, // Allow 'return' within a function. - TypeDecl = 16, // Allow declarations which do not affect memory layout, such as structs, enums, and consts, but not implicit delegates - ImplicitDelegateDecl = 32, // Allow implicit delegates (i.e. those not decorated with UDELEGATE) to be declared -}; - -ENUM_CLASS_FLAGS(ENestAllowFlags) - -namespace EDelegateSpecifierAction -{ - enum Type - { - DontParse, - Parse - }; -} - -/** The category of variable declaration being parsed */ -namespace EVariableCategory -{ - enum Type - { - RegularParameter, - ReplicatedParameter, - Return, - Member - }; -} - -enum class ELayoutMacroType -{ - None = -1, - Array, - ArrayEditorOnly, - Bitfield, - BitfieldEditorOnly, - Field, - FieldEditorOnly, - FieldInitialized, -}; - -/** Information for a particular nesting level. */ -class FNestInfo -{ - /** Link to the stack node. */ - FScope* Scope; - -public: - /** - * Gets nesting scope. - */ - FScope* GetScope() const - { - return Scope; - } - - /** - * Sets nesting scope. - */ - void SetScope(FScope* InScope) - { - this->Scope = InScope; - } - - /** Statement that caused the nesting. */ - ENestType NestType; - - /** Types of statements to allow at this nesting level. */ - ENestAllowFlags Allow; -}; - -struct FIndexRange -{ - int32 StartIndex; - int32 Count; -}; - -/** - * The FRigVMParameter represents a single parameter of a method - * marked up with RIGVM_METHOD. - * Each parameter can be marked with Constant, Input or Output - * metadata - this struct simplifies access to that information. - */ -struct FRigVMParameter -{ - FRigVMParameter() - : Name() - , Type() - , bConstant(false) - , bInput(false) - , bOutput(false) - , MaxArraySize() - , Getter() - , CastName() - , CastType() - , bEditorOnly(false) - { - } - - FString Name; - FString Type; - bool bConstant; - bool bInput; - bool bOutput; - FString MaxArraySize; - FString Getter; - FString CastName; - FString CastType; - bool bEditorOnly; - - const FString& NameOriginal(bool bCastName = false) const - { - return (bCastName && !CastName.IsEmpty()) ? CastName : Name; - } - - const FString& TypeOriginal(bool bCastType = false) const - { - return (bCastType && !CastType.IsEmpty()) ? CastType : Type; - } - - FString Declaration(bool bCastType = false, bool bCastName = false) const - { - return FString::Printf(TEXT("%s %s"), *TypeOriginal(bCastType), *NameOriginal(bCastName)); - } - - FString BaseType(bool bCastType = false) const - { - const FString& String = TypeOriginal(bCastType); - int32 LesserPos = 0; - if (String.FindChar('<', LesserPos)) - { - return String.Mid(0, LesserPos); - } - return String; - } - - FString ExtendedType(bool bCastType = false) const - { - const FString& String = TypeOriginal(bCastType); - int32 LesserPos = 0; - if(String.FindChar('<', LesserPos)) - { - return String.Mid(LesserPos); - } - return String; - } - - FString TypeConstRef(bool bCastType = false) const - { - const FString& String = TypeNoRef(bCastType); - if (String.StartsWith(TEXT("T"), ESearchCase::CaseSensitive) || String.StartsWith(TEXT("F"), ESearchCase::CaseSensitive)) - { - return FString::Printf(TEXT("const %s&"), *String); - } - return FString::Printf(TEXT("const %s"), *String); - } - - FString TypeRef(bool bCastType = false) const - { - const FString& String = TypeNoRef(bCastType); - return FString::Printf(TEXT("%s&"), *String); - } - - FString TypeNoRef(bool bCastType = false) const - { - const FString& String = TypeOriginal(bCastType); - if (String.EndsWith(TEXT("&"))) - { - return String.LeftChop(1); - } - return String; - } - - FString TypeVariableRef(bool bCastType = false) const - { - return IsConst() ? TypeConstRef(bCastType) : TypeRef(bCastType); - } - - FString Variable(bool bCastType = false, bool bCastName = false) const - { - return FString::Printf(TEXT("%s %s"), *TypeVariableRef(bCastType), *NameOriginal(bCastName)); - } - - bool IsConst() const - { - return bConstant || (bInput && !bOutput); - } - - bool IsArray() const - { - return BaseType().Equals(TEXT("TArray")); - } - - bool RequiresCast() const - { - return !CastType.IsEmpty() && !CastName.IsEmpty(); - } -}; - -/** - * The FRigVMParameterArray represents the parameters in a notation - * of a function marked with RIGVM_METHOD. The parameter array can - * produce a comma separated list of names or parameter declarations. - */ -struct FRigVMParameterArray -{ -public: - int32 Num() const { return Parameters.Num(); } - const FRigVMParameter& operator[](int32 InIndex) const { return Parameters[InIndex]; } - FRigVMParameter& operator[](int32 InIndex) { return Parameters[InIndex]; } - TArray::RangedForConstIteratorType begin() const { return Parameters.begin(); } - TArray::RangedForConstIteratorType end() const { return Parameters.end(); } - TArray::RangedForIteratorType begin() { return Parameters.begin(); } - TArray::RangedForIteratorType end() { return Parameters.end(); } - - int32 Add(const FRigVMParameter& InParameter) - { - return Parameters.Add(InParameter); - } - - FString Names(bool bLeadingSeparator = false, const TCHAR* Separator = TEXT(", "), bool bCastType = false, bool bIncludeEditorOnly = true) const - { - if (Parameters.Num() == 0) - { - return FString(); - } - TArray NameArray; - for (const FRigVMParameter& Parameter : Parameters) - { - if (!bIncludeEditorOnly && Parameter.bEditorOnly) - { - continue; - } - NameArray.Add(Parameter.NameOriginal(bCastType)); - } - - if (NameArray.Num() == 0) - { - return FString(); - } - - FString Joined = FString::Join(NameArray, Separator); - if (bLeadingSeparator) - { - return FString::Printf(TEXT("%s%s"), Separator, *Joined); - } - return Joined; - } - - FString Declarations(bool bLeadingSeparator = false, const TCHAR* Separator = TEXT(", "), bool bCastType = false, bool bCastName = false, bool bIncludeEditorOnly = true) const - { - if (Parameters.Num() == 0) - { - return FString(); - } - TArray DeclarationArray; - for (const FRigVMParameter& Parameter : Parameters) - { - if (!bIncludeEditorOnly && Parameter.bEditorOnly) - { - continue; - } - DeclarationArray.Add(Parameter.Variable(bCastType, bCastName)); - } - - if (DeclarationArray.Num() == 0) - { - return FString(); - } - - FString Joined = FString::Join(DeclarationArray, Separator); - if (bLeadingSeparator) - { - return FString::Printf(TEXT("%s%s"), Separator, *Joined); - } - return Joined; - } - -private: - TArray Parameters; -}; - -/** - * A single info dataset for a function marked with RIGVM_METHOD. - * This struct provides access to its name, the return type and all parameters. - */ -struct FRigVMMethodInfo -{ - FString ReturnType; - FString Name; - FRigVMParameterArray Parameters; - - FString ReturnPrefix() const - { - return (ReturnType.IsEmpty() || (ReturnType == TEXT("void"))) ? TEXT("") : TEXT("return "); - } -}; - -/** - * An info dataset providing access to all functions marked with RIGVM_METHOD - * for each struct. - */ -struct FRigVMStructInfo -{ - FString Name; - FRigVMParameterArray Members; - TArray Methods; -}; - -typedef TMap FRigVMStructMap; - -struct ClassDefinitionRange -{ - ClassDefinitionRange(const TCHAR* InStart, const TCHAR* InEnd) - : Start(InStart) - , End(InEnd) - , bHasGeneratedBody(false) - { } - - ClassDefinitionRange() - : Start(nullptr) - , End(nullptr) - , bHasGeneratedBody(false) - { } - - void Validate() - { - if (End <= Start) - { - FError::Throwf(TEXT("The class definition range is invalid. Most probably caused by previous parsing error.")); - } - } - - const TCHAR* Start; - const TCHAR* End; - bool bHasGeneratedBody; -}; - -#ifndef UHT_DOCUMENTATION_POLICY_DEFAULT -#define UHT_DOCUMENTATION_POLICY_DEFAULT false -#endif - -struct FDocumentationPolicy -{ - bool bClassOrStructCommentRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; - bool bFunctionToolTipsRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; - bool bMemberToolTipsRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; - bool bParameterToolTipsRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; - bool bFloatRangesRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; -}; - -///////////////////////////////////////////////////// -// FHeaderParser - -// -// Header parser class. Extracts metadata from annotated C++ headers and gathers enough -// information to autogenerate additional headers and other boilerplate code. -// -class FHeaderParser : public FBaseParser, public FContextSupplier -{ -public: - // Default version of generated code. Defaults to oldest possible, unless specified otherwise in config. - EGeneratedCodeVersion DefaultGeneratedCodeVersion; - - // Compute the function parameter size and save the return offset - static void ComputeFunctionParametersSize(UClass* InClass); - - // Parse all headers for classes that are inside LimitOuter. - static ECompilationResult::Type ParseAllHeadersInside( - FClasses& ModuleClasses, - FFeedbackContext* Warn, - UPackage* LimitOuter, - const FManifestModule& Module, -#pragma region Plugins - TArray& ScriptPlugins, -#pragma endregion Plugins - const FUnrealSourceFiles& UnrealSourceFilesMap, - FTypeDefinitionInfoMap& TypeDefinitionInfoMap, - const FPublicSourceFileSet& PublicSourceFileSet, - const TMap& PackageToManifestModuleMap, - const FClassDeclarations& ClassDeclarations, - TMap& EnumUnderlyingTypes, - FRWLock& EnumUnderlyingTypesLock, - TMap& InClassSerializerMap, - FRWLock& InClassSerializerMapLock - ); - - // Performs a preliminary parse of the text in the specified buffer, pulling out: - // Class name and parent class name - // Is it an interface - // The list of other classes/interfaces it is dependent on - // - // It also splits the buffer up into: - // ScriptText (text outside of #if CPP and #if DEFAULTS blocks) - static void SimplifiedClassParse(const TCHAR* Filename, const TCHAR* Buffer, TArray& OutParsedClassArray, TArray& DependentOn, FStringOutputDevice& ScriptText, FClassDeclarations& ClassDeclarations); - - /** - * Returns True if the given class name includes a valid Unreal prefix and matches up with the given original class Name. - * - * @param InNameToCheck - Name w/ potential prefix to check - * @param OriginalClassName - Name of class w/ no prefix to check against - */ - static bool ClassNameHasValidPrefix(const FString& InNameToCheck, const FString& OriginalClassName); - - /** - * Tries to convert the header file name to a class name (with 'U' prefix) - * - * @param HeaderFilename Filename. - * @param OutClass The resulting class name (if successfull) - * @return true if the filename was a header filename (.h), false otherwise (in which case OutClassName is unmodified). - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - static bool DependentClassNameFromHeader(const TCHAR* HeaderFilename, FString& OutClassName); - */ - - /** - * Transforms CPP-formated string containing default value, to inner formated string - * If it cannot be transformed empty string is returned. - * - * @param Property The property that owns the default value. - * @param CppForm A CPP-formated string. - * @param out InnerForm Inner formated string - * @return true on success, false otherwise. - */ - static bool DefaultValueStringCppFormatToInnerFormat(const FProperty* Property, const FString& CppForm, FString &InnerForm); - - /** - * Parse Class's annotated headers and optionally its child classes. Marks the class as CLASS_Parsed. - * - * @param AllClasses the class tree containing all classes in the current package - * @param HeaderParser the header parser - * @param SourceFile Source file info. - * - * @return Result enumeration. - */ - ECompilationResult::Type ParseHeaders(FClasses& AllClasses, FUnrealSourceFile* SourceFile); - -protected: - friend struct FScriptLocation; - friend struct FNativeClassHeaderGenerator; - - // UHTLite - const FUnrealSourceFiles& UnrealSourceFilesMap; - FTypeDefinitionInfoMap& TypeDefinitionInfoMap; - const FClassDeclarations& ClassDeclarations; - - // For compiling messages and errors. - FFeedbackContext* Warn; - - // Filename currently being parsed - FString Filename; - - // Was the first include in the file a validly formed auto-generated header include? - bool bSpottedAutogeneratedHeaderInclude; - - // Current nest level, starts at 0. - int32 NestLevel; - - // Top nesting level. - FNestInfo* TopNest; - - /** - * Gets current nesting scope. - */ - FScope* GetCurrentScope() const - { - return TopNest->GetScope(); - } - - /** - * Gets current file scope. - */ - FFileScope* GetCurrentFileScope() const - { - int32 Index = 0; - if (!TopNest) - { - check(!NestLevel); - return nullptr; - } - while (TopNest[Index].NestType != ENestType::GlobalScope) - { - --Index; - } - - return (FFileScope*)TopNest[Index].GetScope(); - } - - /** - * Gets current source file. - */ - FUnrealSourceFile* GetCurrentSourceFile() const - { - return CurrentSourceFile; - } - - void SetCurrentSourceFile(FUnrealSourceFile* UnrealSourceFile) - { - CurrentSourceFile = UnrealSourceFile; - } - - /** - * Gets current class scope. - */ - FStructScope* GetCurrentClassScope() const - { - check(TopNest->NestType == ENestType::Class || TopNest->NestType == ENestType::Interface || TopNest->NestType == ENestType::NativeInterface); - - return (FStructScope*)TopNest->GetScope(); - } - - /** - * Tells if parser is currently in a class. - */ - bool IsInAClass() const - { - int32 Index = 0; - while (TopNest[Index].NestType != ENestType::GlobalScope) - { - if (TopNest[Index].NestType == ENestType::Class || TopNest->NestType == ENestType::Interface || TopNest->NestType == ENestType::NativeInterface) - { - return true; - } - - --Index; - } - - return false; - } - - /** - * Gets current class. - */ - UClass* GetCurrentClass() const - { - return (UClass*)GetCurrentClassScope()->GetStruct(); - } - - /** - * Gets current class's metadata. - */ - FClassMetaData* GetCurrentClassData() - { - return GScriptHelper.FindClassData(GetCurrentClass()); - } - - // Information about all nesting levels. - FNestInfo Nest[MAX_NEST_LEVELS]; - - // enum for complier directives used to build up the directive stack - struct ECompilerDirective - { - enum Type - { - // this directive is insignificant and does not change the code generation at all - Insignificant = 0, - // this indicates we are in a WITH_EDITOR #if-Block - WithEditor = 1<<0, - // this indicates we are in a WITH_EDITORONLY_DATA #if-Block - WithEditorOnlyData = 1<<1, - }; - }; - - /** - * Compiler directive nest in which the parser currently is - * NOTE: compiler directives are combined when more are added onto the stack, so - * checking the only the top of stack is enough to determine in which #if-Block(s) the current code - * is. - * - * ex. Stack.Num() == 1 while entering #if WITH_EDITOR: - * CompilerDirectiveStack[1] == CompilerDirectiveStack[0] | ECompilerDirective::WithEditor == - * CompilerDirecitveStack[1] == CompilerDirectiveStack.Num()-1 | ECompilerDirective::WithEditor - * - * ex. Stack.Num() == 2 while entering #if WITH_EDITOR: - * CompilerDirectiveStack[3] == CompilerDirectiveStack[0] | CompilerDirectiveStack[1] | CompilerDirectiveStack[2] | ECompilerDirective::WithEditor == - * CompilerDirecitveStack[3] == CompilerDirectiveStack.Num()-1 | ECompilerDirective::WithEditor - */ - TArray CompilerDirectiveStack; - - // Pushes the Directive specified to the CompilerDirectiveStack according to the rules described above - void FORCEINLINE PushCompilerDirective(ECompilerDirective::Type Directive) - { - CompilerDirectiveStack.Push(CompilerDirectiveStack.Num()>0 ? (CompilerDirectiveStack[CompilerDirectiveStack.Num()-1] | Directive) : Directive); - } - - /** - * The starting class flags (i.e. the class flags that were set before the - * CLASS_RecompilerClear mask was applied) for the class currently being compiled - */ - uint32 PreviousClassFlags; - - // For new-style classes, used to keep track of an unmatched {} pair - bool bEncounteredNewStyleClass_UnmatchedBrackets; - - // Indicates that UCLASS/USTRUCT/UINTERFACE has already been parsed in this .h file.. - bool bHaveSeenUClass; - - // Indicates that a GENERATED_UCLASS_BODY or GENERATED_BODY has been found in the UClass. - bool bClassHasGeneratedBody; - - // Indicates that a GENERATED_UINTERFACE_BODY has been found in the UClass. - bool bClassHasGeneratedUInterfaceBody; - - // Indicates that a GENERATED_IINTERFACE_BODY has been found in the UClass. - bool bClassHasGeneratedIInterfaceBody; - - // public, private, etc at the current parse spot - EAccessSpecifier CurrentAccessSpecifier; - - //////////////////////////////////////////////////// - - // Special parsed struct names that do not require a prefix - TArray StructsWithNoPrefix; - - // Special parsed struct names that have a 'T' prefix - TArray StructsWithTPrefix; - - // Mapping from 'human-readable' macro substring to # of parameters for delegate declarations - // Index 0 is 1 parameter, Index 1 is 2, etc... - TArray DelegateParameterCountStrings; - - // Types that have been renamed, treat the old deprecated name as the new name for code generation - TMap TypeRedirectMap; - - // List of all used identifiers for net service function declarations (every function must be unique) - TMap UsedRPCIds; - // List of all net service functions with undeclared response functions - TMap RPCsNeedingHookup; - - // List of all multiplex methods defined on structs - FRigVMStructMap StructRigVMMap; - - // Constructor. - explicit FHeaderParser(FFeedbackContext* InWarn, - const FManifestModule& InModule, - const FUnrealSourceFiles& InUnrealSourceFilesMap, - FTypeDefinitionInfoMap& InTypeDefinitionInfoMap, - const FClassDeclarations& InClassDeclarations, - TMap& InEnumUnderlyingTypes, - FRWLock& InEnumUnderlyingTypesLock, - TMap& InClassSerializerMap, - FRWLock& InClassSerializerMapLock - ); - - virtual ~FHeaderParser() - { - if ( FScriptLocation::Compiler == this ) - { - FScriptLocation::Compiler = NULL; - } - } - - // Returns true if the token is a dynamic delegate declaration - bool IsValidDelegateDeclaration(const FToken& Token) const; - - // Returns true if the current token is a bitfield type - bool IsBitfieldProperty(ELayoutMacroType LayoutMacroType); - - // Parse the parameter list of a function or delegate declaration - void ParseParameterList(FClasses& AllClasses, UFunction* Function, bool bExpectCommaBeforeName = false, TMap* MetaData = NULL); - - // Modify token to fix redirected types if needed - void RedirectTypeIdentifier(FToken& Token) const; - -public: - // Throws if a specifier value wasn't provided - static void RequireSpecifierValue(const FPropertySpecifier& Specifier, bool bRequireExactlyOne = false); - static FString RequireExactlyOneSpecifierValue(const FPropertySpecifier& Specifier); - - /** - * 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 - */ - static UField* FindField( UStruct* InScope, const TCHAR* InIdentifier, bool bIncludeParents=true, UClass* FieldClass=UField::StaticClass(), const TCHAR* Thing=nullptr ); - static FField* FindProperty(UStruct* InScope, const TCHAR* InIdentifier, bool bIncludeParents = true, FFieldClass* FieldClass = FField::StaticClass(), const TCHAR* Thing = nullptr); - - // Checks ToValidate to make sure that its associated sparse class data struct, if one exists, is a valid structure to use for storing sparse class data. - static void CheckSparseClassData(const UStruct* ToValidate); - -protected: - - /** - * Parse rest of the module's source files. - * - * @param AllClasses The class tree containing all classes in the current package. - * @param ModulePackage Current package. - * @param HeaderParser The header parser. - * - * @return Result enumeration. - */ - ECompilationResult::Type ParseRestOfModulesSourceFiles(FClasses& AllClasses, UPackage* ModulePackage); - - //@TODO: Remove this method - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - static void ParseClassName(const TCHAR* Temp, FString& ClassName); - */ - - /** - * @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. - */ - static FString FormatCommentForToolTip(const FString& Input); - - /** - * Retrieves parameter comments / tooltips from the function comment - * @param Input An input string, expected to be a script comment. - * @return The map of parameter name to comment per parameter - */ - static TMap GetParameterToolTipsFromFunctionComment(const FString& Input); - - /** - * Begins the process of exporting C++ class declarations for native classes in the specified package - * - * @param CurrentPackage The package being compiled. - * @param AllClasses The class tree for CurrentPackage. - * @param Module Currently exported module. - */ - void ExportNativeHeaders( - UPackage* CurrentPackage, - FClasses& AllClasses, - bool bAllowSaveExportedHeaders, - const FManifestModule& Module, - const FPublicSourceFileSet& PublicSourceFileSet, - const TMap& PackageToManifestModuleMap - ) const; - - // UHTLite - bool DoesAnythingInHierarchyHaveDefaultToInstanced(UClass* TestClass) const; - - // UHTLite - TSet GetSourceFilesWithInheritanceOrdering(UPackage* CurrentPackage, FClasses& AllClasses) const; - - // UHTLite - FProperty* CreateVariableProperty(FPropertyBase& VarProperty, FFieldVariant Scope, const FName& Name, EObjectFlags ObjectFlags, EVariableCategory::Type VariableCategory, FUnrealSourceFile* UnrealSourceFile) const; - - // UHTLite - void AddModuleRelativePathToMetadata(FUnrealSourceFile& SourceFile, TMap& MetaData) const; - void AddModuleRelativePathToMetadata(UField* Type, TMap& MetaData) const; - - // UHTLite - void AddIncludePathToMetadata(UField* Type, TMap& MetaData) const; - - // FContextSupplier interface. - virtual FString GetContext() override; - // End of FContextSupplier interface. - - // High-level compiling functions. - /** - * Parses given source file. - * - * @param AllClasses The class tree for current package. - * @param SourceFile Source file to parse. - * - * @returns Compilation result enum. - */ - ECompilationResult::Type ParseHeader(FClasses& AllClasses, FUnrealSourceFile* SourceFile); - void CompileDirective(FClasses& AllClasses); - void FinalizeScriptExposedFunctions(UClass* Class); - UEnum* CompileEnum(); - UScriptStruct* CompileStructDeclaration(FClasses& AllClasses); - bool CompileDeclaration(FClasses& AllClasses, TArray& DelegatesToFixup, FToken& Token); - - /** Skip C++ (noexport) declaration. */ - bool SkipDeclaration(FToken& Token); - /** Similar to MatchSymbol() but will return to the exact location as on entry if the symbol was not found. */ - bool SafeMatchSymbol(const TCHAR Match); - void HandleOneInheritedClass(FClasses& AllClasses, UClass* Class, FString&& InterfaceName); - FClass* ParseClassNameDeclaration(FClasses& AllClasses, FString& DeclaredClassName, FString& RequiredAPIMacroIfPresent); - - /** The property style of a variable declaration being parsed */ - struct EPropertyDeclarationStyle - { - enum Type - { - None, - UPROPERTY - }; - }; - - /** - * Resets current class data back to its defaults. - */ - void ResetClassData(); - - /** - * Create new function object based on given info structure. - */ - UFunction* CreateFunction(const FFuncInfo &FuncInfo) const; - - /** - * Create new delegate function object based on given info structure. - */ - template - UDelegateFunction* CreateDelegateFunction(const FFuncInfo &FuncInfo) const; - - UClass* CompileClassDeclaration(FClasses& AllClasses); - UDelegateFunction* CompileDelegateDeclaration(FClasses& AllClasses, const TCHAR* DelegateIdentifier, EDelegateSpecifierAction::Type SpecifierAction = EDelegateSpecifierAction::DontParse); - void CompileFunctionDeclaration(FClasses& AllClasses); - void CompileVariableDeclaration (FClasses& AllClasses, UStruct* Struct); - void CompileInterfaceDeclaration(FClasses& AllClasses); - void CompileRigVMMethodDeclaration(FClasses& AllClasses, UStruct* Struct); - void ParseRigVMMethodParameters(UStruct* Struct); - - FClass* ParseInterfaceNameDeclaration(FClasses& AllClasses, FString& DeclaredInterfaceName, FString& RequiredAPIMacroIfPresent); - bool TryParseIInterfaceClass(FClasses& AllClasses); - - bool CompileStatement(FClasses& AllClasses, TArray& DelegatesToFixup); - - // Checks to see if a particular kind of command is allowed on this nesting level. - bool IsAllowedInThisNesting(ENestAllowFlags AllowFlags); - - // 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 CheckAllow(const TCHAR* Thing, ENestAllowFlags AllowFlags); - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - UStruct* GetSuperScope( UStruct* CurrentScope, const FName& SearchName ); - - void SkipStatements( int32 SubCount, const TCHAR* ErrorTag ); - */ - - /** - * Parses a variable or return value declaration and determines the variable type and property flags. - * - * @param AllClasses the class tree for CurrentPackage - * @param Scope struct to create the property in - * @param VarProperty will be filled in with type and property flag data for the property declaration that was parsed - * @param Disallow contains a mask of variable modifiers that are disallowed in this context - * @param OuterPropertyType only specified when compiling the inner properties for arrays or maps. corresponds to the FToken for the outer property declaration. - * @param PropertyDeclarationStyle if the variable is defined with a UPROPERTY - * @param VariableCategory what kind of variable is being parsed - * @param ParsedVarIndexRange The source text [Start, End) index range for the parsed type. - */ - void GetVarType( - FClasses& AllClasses, - FScope* Scope, - FPropertyBase& VarProperty, - EPropertyFlags Disallow, - const FToken* OuterPropertyType, - EPropertyDeclarationStyle::Type PropertyDeclarationStyle, - EVariableCategory::Type VariableCategory, - FIndexRange* ParsedVarIndexRange = nullptr, - ELayoutMacroType* OutLayoutMacroType = nullptr); - - /** - * Parses a variable name declaration and creates a new FProperty object. - * - * @param Scope struct to create the property in - * @param VarProperty type and propertyflag info for the new property (inout) - * @param VariableCategory what kind of variable is being created - * - * @return a pointer to the new FProperty if successful, or NULL if there was no property to parse - */ - FProperty* GetVarNameAndDim( - UStruct* Struct, - FToken& VarProperty, - EVariableCategory::Type VariableCategory, - ELayoutMacroType LayoutMacroType = ELayoutMacroType::None); - - /** - * Returns whether the specified class can be referenced from the class currently being compiled. - * - * @param Scope The scope we are currently parsing. - * @param CheckClass The class we want to reference. - * - * @return true if the specified class is an intrinsic type or if the class has successfully been parsed - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - bool AllowReferenceToClass(UStruct* Scope, UClass* CheckClass) const; - */ - - /** - * @return true if Scope has FProperty objects in its list of fields - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - static bool HasMemberProperties( const UStruct* Scope ); - */ - - /** - * Parses optional metadata text. - * - * @param MetaData the metadata map to store parsed metadata in - * @param FieldName the field being parsed (used for logging) - * - * @return true if metadata was specified - */ - void ParseFieldMetaData(TMap& MetaData, const TCHAR* FieldName); - - /** - * Formats the current comment, if any, and adds it to the metadata as a tooltip. - * - * @param MetaData the metadata map to store the tooltip in - */ - void AddFormattedPrevCommentAsTooltipMetaData(TMap& MetaData); - - /** - * Tries to parse the token as an access protection specifier (public:, protected:, or private:) - * - * @return EAccessSpecifier this is, or zero if it is none - */ - EAccessSpecifier ParseAccessProtectionSpecifier(const FToken& Token); - - const TCHAR* NestTypeName( ENestType NestType ); - - FClass* GetQualifiedClass(const FClasses& AllClasses, const TCHAR* Thing); - - /** - * Increase the nesting level, setting the new top nesting level to - * the one specified. If pushing a function or state and it overrides a similar - * thing declared on a lower nesting level, verifies that the override is legal. - * - * @param NestType the new nesting type - * @param InNode @todo - */ - void PushNest(ENestType NestType, UStruct* InNode, FUnrealSourceFile* SourceFile = nullptr); - void PopNest(ENestType NestType, const TCHAR* Descr); - - /** - * Tasks that need to be done after popping function declaration - * from parsing stack. - * - * @param AllClasses The class tree for current package. - * @param PoppedFunction Function that have just been popped. - */ - void PostPopFunctionDeclaration(FClasses& AllClasses, UFunction* PoppedFunction); - - /** - * Tasks that need to be done after popping interface definition - * from parsing stack. - * - * @param AllClasses The class tree for current package. - * @param CurrentInterface Interface that have just been popped. - */ - void PostPopNestInterface(FClasses& AllClasses, UClass* CurrentInterface); - - /** - * Tasks that need to be done after popping class definition - * from parsing stack. - * - * @param CurrentClass Class that have just been popped. - */ - void PostPopNestClass(UClass* CurrentClass); - - /** - * Binds all delegate properties declared in ValidationScope the delegate functions specified in the variable declaration, verifying that the function is a valid delegate - * within the current scope. This must be done once the entire class has been parsed because instance delegate properties must be declared before the delegate declaration itself. - * - * @todo: this function will no longer be required once the post-parse fixup phase is added (TTPRO #13256) - * - * @param AllClasses the class tree for CurrentPackage - * @param Struct the struct to validate delegate properties for - * @param Scope the current scope - * @param DelegateCache cached map of delegates that have already been found; used for faster lookup. - */ - void FixupDelegateProperties(FClasses& AllClasses, UStruct* ValidationScope, FScope& Scope, TMap& DelegateCache); - - // Retry functions. - void InitScriptLocation( FScriptLocation& Retry ); - void ReturnToLocation( const FScriptLocation& Retry, bool Binary=1, bool Text=1 ); - - /** - * 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 GetFindFlagForPropertyName(const TCHAR* PropertyName); - - static void ValidatePropertyIsDeprecatedIfNecessary(const FPropertyBase& VarProperty, const FToken* OuterPropertyType); - -private: - // Source file currently parsed by UHT. - FUnrealSourceFile* CurrentSourceFile; - - // Module currently parsed by UHT. - const FManifestModule* CurrentlyParsedModule; - - // True if the module currently being parsed is part of the engine, as opposed to being part of a game - bool bIsCurrentModulePartOfEngine; - - /** - * Tries to match constructor parameter list. Assumes that constructor - * name is already matched. - * - * If fails it reverts all parsing done. - * - * @param Token Token to start parsing from. - * - * @returns True if matched. False otherwise. - */ - bool TryToMatchConstructorParameterList(FToken Token); - - // Parses possible version declaration in generated code, e.g. GENERATED_BODY(). - void CompileVersionDeclaration(UStruct* Struct); - - // Verifies that all specified class's UProperties with function associations have valid targets - void VerifyPropertyMarkups( UClass* TargetClass ); - - // Verifies the target function meets the criteria for a blueprint property getter - void VerifyBlueprintPropertyGetter(FProperty* Property, UFunction* TargetFunction); - - // Verifies the target function meets the criteria for a blueprint property setter - void VerifyBlueprintPropertySetter(FProperty* Property, UFunction* TargetFunction); - - // Verifies the target function meets the criteria for a replication notify callback - void VerifyRepNotifyCallback(FProperty* Property, UFunction* TargetFunction); - - // Constructs the policy from a string - static FDocumentationPolicy GetDocumentationPolicyFromName(const FString& PolicyName); - - // Constructs the policy for documentation checks for a given struct - static FDocumentationPolicy GetDocumentationPolicyForStruct(UStruct* Struct); - - // Property types to provide UI Min and Max ranges - static const TArray PropertyCPPTypesRequiringUIRanges; - - // Returns true if a given CPP types required ui checking - static bool DoesCPPTypeRequireDocumentation(const FString& CPPType); - - // Validates the documentation for a given enum - void CheckDocumentationPolicyForEnum(UEnum* Enum, const TMap& MetaData, const TArray>& Entries); - - // Validates the documentation for a given struct - void CheckDocumentationPolicyForStruct(UStruct* Struct); - - // Validates the documentation for a given method - void CheckDocumentationPolicyForFunc(UClass* Class, UFunction* Func); - - // Checks if a valid range has been found on the provided metadata - bool CheckUIMinMaxRangeFromMetaData(const FString& UIMin, const FString& UIMax); - - // Names that cannot be used enums, UStructs, or UClasses - static const TArray ReservedTypeNames; - -public: - /** - * Checks if the given token uses one of the reserved type names. - * - * @param TypeName String of the type to check (For UObject/UClass, use the stripped name) - * @return True if the TypeName is a reserved name - */ - static bool IsReservedTypeName(const FString& TypeName); - - /** - * Checks if the given token uses one of the reserved type names. - * - * @param Token The token to check - * @return True if the Token is using a reserved name - */ - static bool IsReservedTypeName(const FToken& Token); - - // UHTLite - TMap ClassDefinitionRanges; - TMap ArrayDimensions; - mutable TSet UnsizedProperties; - TSet EditorOnlyDataTypes; - TSet PropertyUsesMemoryImageAllocator; - - // UHTLite NOTE: These are read-only in the code-generation phase - TMap& EnumUnderlyingTypes; - FRWLock& EnumUnderlyingTypesLock; - - // UHTLite NOTE: These are read-only in the code-generation phase - TMap& ClassSerializerMap; - FRWLock& ClassSerializerMapLock; -}; - -///////////////////////////////////////////////////// -// FHeaderPreParser - -class FHeaderPreParser : public FBaseParser -{ -public: - FHeaderPreParser() - { - } - - void ParseClassDeclaration( - const TCHAR* Filename, - const TCHAR* InputText, - int32 InLineNumber, - const TCHAR* - StartingMatchID, - FName& out_StrippedClassName, - FString& out_ClassName, - FString& out_BaseClassName, - TArray& out_ClassNames, - const TArray& ParsedClassArray, - FClassDeclarations& ClassDeclarations - ); -}; diff --git a/Engine/Source/Programs/UHTLite/Private/HeaderProvider.cpp b/Engine/Source/Programs/UHTLite/Private/HeaderProvider.cpp deleted file mode 100644 index f6f1d3750085..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/HeaderProvider.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "HeaderProvider.h" -#include "UnrealHeaderTool.h" -#include "UnrealTypeDefinitionInfo.h" -#include "ClassMaps.h" - -FHeaderProvider::FHeaderProvider(EHeaderProviderSourceType InType, FString&& InId)//, bool bInAutoInclude/* = false*/) - : Type(InType) - , Id(MoveTemp(InId)) - , Cache(nullptr) -{ - -} - -FUnrealSourceFile* FHeaderProvider::Resolve(const FUnrealSourceFiles& UnrealSourceFilesMap, const FTypeDefinitionInfoMap& TypeDefinitionInfoMap) -{ - if (Type != EHeaderProviderSourceType::Resolved) - { - if (Type == EHeaderProviderSourceType::ClassName) - { - FName IdName(*Id, FNAME_Find); - if (const TSharedRef* Source = TypeDefinitionInfoMap.FindByName(IdName)) - { - Cache = &(*Source)->GetUnrealSourceFile(); - } - } - else if (const TSharedRef* Source = UnrealSourceFilesMap.Find(Id)) - { - Cache = &Source->Get(); - } - - Type = EHeaderProviderSourceType::Resolved; - } - - return Cache; -} - -FString FHeaderProvider::ToString() const -{ - return FString::Printf(TEXT("%s %s"), Type == EHeaderProviderSourceType::ClassName ? TEXT("class") : TEXT("file"), *Id); -} - -const FString& FHeaderProvider::GetId() const -{ - return Id; -} - -bool operator==(const FHeaderProvider& A, const FHeaderProvider& B) -{ - return A.Type == B.Type && A.Id == B.Id; -} diff --git a/Engine/Source/Programs/UHTLite/Private/HeaderProvider.h b/Engine/Source/Programs/UHTLite/Private/HeaderProvider.h deleted file mode 100644 index 4d5172aca254..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/HeaderProvider.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Containers/UnrealString.h" - -class FUnrealSourceFile; - -enum class EHeaderProviderSourceType -{ - ClassName, - FileName, - Resolved -}; - -class FHeaderProvider -{ - friend bool operator==(const FHeaderProvider& A, const FHeaderProvider& B); -public: - FHeaderProvider(EHeaderProviderSourceType Type, FString&& Id); - - FUnrealSourceFile* Resolve(const struct FUnrealSourceFiles& UnrealSourceFilesMap, const struct FTypeDefinitionInfoMap& TypeDefinitionInfoMap); - - FString ToString() const; - - const FString& GetId() const; - -private: - EHeaderProviderSourceType Type; - FString Id; - FUnrealSourceFile* Cache; -}; - -bool operator==(const FHeaderProvider& A, const FHeaderProvider& B); diff --git a/Engine/Source/Programs/UHTLite/Private/IScriptGeneratorPluginInterface.cpp b/Engine/Source/Programs/UHTLite/Private/IScriptGeneratorPluginInterface.cpp deleted file mode 100644 index 8d09cf68fc1f..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/IScriptGeneratorPluginInterface.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "IScriptGeneratorPluginInterface.h" -#include "UnrealHeaderTool.h" -#include "UObject/ErrorException.h" -#include "Algo/FindSortedStringCaseInsensitive.h" - -EBuildModuleType::Type EBuildModuleType::Parse(const TCHAR* Value) -{ - static const TCHAR* AlphabetizedTypes[] = { - TEXT("EngineDeveloper"), - TEXT("EngineEditor"), - TEXT("EngineRuntime"), - TEXT("EngineThirdParty"), - TEXT("EngineUncooked"), - TEXT("GameDeveloper"), - TEXT("GameEditor"), - TEXT("GameRuntime"), - TEXT("GameThirdParty"), - TEXT("GameUncooked"), - TEXT("Program") - }; - - int32 TypeIndex = Algo::FindSortedStringCaseInsensitive(Value, AlphabetizedTypes); - if (TypeIndex < 0) - { - FError::Throwf(TEXT("Unrecognized EBuildModuleType name: %s"), Value); - } - - static EBuildModuleType::Type AlphabetizedValues[] = { - EngineDeveloper, - EngineEditor, - EngineRuntime, - EngineThirdParty, - EngineUncooked, - GameDeveloper, - GameEditor, - GameRuntime, - GameThirdParty, - GameUncooked, - Program - }; - - return AlphabetizedValues[TypeIndex]; -} - -EPackageOverrideType::Type EPackageOverrideType::Parse(const TCHAR* Value) -{ - static const TCHAR* AlphabetizedTypes[] = { - TEXT("EditorOnly"), - TEXT("EngineDeveloper"), - TEXT("EngineUncookedOnly"), - TEXT("GameDeveloper"), - TEXT("GameUncookedOnly"), - TEXT("None"), - }; - - int32 TypeIndex = Algo::FindSortedStringCaseInsensitive(Value, AlphabetizedTypes); - if (TypeIndex < 0) - { - FError::Throwf(TEXT("Unrecognized EPackageOverrideType name: %s"), Value); - } - - static EPackageOverrideType::Type AlphabetizedValues[] = { - EditorOnly, - EngineDeveloper, - EngineUncookedOnly, - GameDeveloper, - GameUncookedOnly, - None - }; - - return AlphabetizedValues[TypeIndex]; -} \ No newline at end of file diff --git a/Engine/Source/Programs/UHTLite/Private/Manifest.cpp b/Engine/Source/Programs/UHTLite/Private/Manifest.cpp deleted file mode 100644 index 58e8882ebf90..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Manifest.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "Manifest.h" -#include "UnrealHeaderTool.h" -#include "Misc/DateTime.h" -#include "Logging/LogMacros.h" -#include "HAL/FileManager.h" -#include "Misc/FileHelper.h" -#include "Misc/Paths.h" -#include "UObject/ErrorException.h" -#include "Misc/PackageName.h" -#include "UnrealHeaderToolGlobals.h" -#include "Serialization/JsonTypes.h" -#include "Serialization/JsonSerializer.h" - -namespace -{ - template struct TJsonFieldType; - template <> struct TJsonFieldType { static const EJson Value = EJson::Number; }; - template <> struct TJsonFieldType { static const EJson Value = EJson::String; }; - template <> struct TJsonFieldType { static const EJson Value = EJson::Boolean; }; - template <> struct TJsonFieldType>> { static const EJson Value = EJson::Array; }; - template <> struct TJsonFieldType> { static const EJson Value = EJson::Object; }; - - template - void GetJsonValue(T& OutVal, const TSharedPtr& JsonValue, const TCHAR* Outer) - { - if (JsonValue->Type != TJsonFieldType::Value) - { - FError::Throwf(TEXT("'%s' is the wrong type"), Outer); - } - - JsonValue->AsArgumentType(OutVal); - } - - template - void GetJsonFieldValue(T& OutVal, const TSharedPtr& JsonObject, const TCHAR* FieldName, const TCHAR* Outer) - { - TSharedPtr* JsonValue = JsonObject->Values.Find(FieldName); - - if (!JsonValue) - { - FError::Throwf(TEXT("Unable to find field '%s' in '%s'"), FieldName, Outer); - } - - if ((*JsonValue)->Type != TJsonFieldType::Value) - { - FError::Throwf(TEXT("Field '%s' in '%s' is the wrong type"), Outer); - } - - (*JsonValue)->AsArgumentType(OutVal); - } - - void ProcessHeaderArray(FString* OutStringArray, const TArray>& InJsonArray, const TCHAR* Outer) - { - for (int32 Index = 0, Count = InJsonArray.Num(); Index != Count; ++Index) - { - GetJsonValue(*OutStringArray++, InJsonArray[Index], *FString::Printf(TEXT("%s[%d]"), Outer, Index)); - } - } -} - - -FManifest FManifest::LoadFromFile(const FString& Filename) -{ - FManifest Result; - FString FilenamePath = FPaths::GetPath(Filename); - FString Json; - - if (!FFileHelper::LoadFileToString(Json, *Filename)) - { - FError::Throwf(TEXT("Unable to load manifest: %s"), *Filename); - } - - TSharedPtr RootObject = TSharedPtr(); - TSharedRef> Reader = TJsonReaderFactory::Create(Json); - - if (!FJsonSerializer::Deserialize(Reader, RootObject)) - { - FError::Throwf(TEXT("Manifest is malformed: %s"), *Filename); - } - - TArray> ModulesArray; - - GetJsonFieldValue(Result.IsGameTarget, RootObject, TEXT("IsGameTarget"), TEXT("{manifest root}")); - GetJsonFieldValue(Result.RootLocalPath, RootObject, TEXT("RootLocalPath"), TEXT("{manifest root}")); - Result.RootBuildPath = Result.RootLocalPath + FPlatformMisc::GetDefaultPathSeparator(); - GetJsonFieldValue(Result.TargetName, RootObject, TEXT("TargetName"), TEXT("{manifest root}")); - GetJsonFieldValue(Result.ExternalDependenciesFile, RootObject, TEXT("ExternalDependenciesFile"), TEXT("{manifest root}")); - GetJsonFieldValue(ModulesArray, RootObject, TEXT("Modules"), TEXT("{manifest root}")); - - UE_LOG(LogCompile, Log, TEXT("Loaded manifest: %s"), *Filename); - UE_LOG(LogCompile, Log, TEXT("Manifest.IsGameTarget=%s"), Result.IsGameTarget ? TEXT("True") : TEXT("False")); - UE_LOG(LogCompile, Log, TEXT("Manifest.RootLocalPath=%s"), *Result.RootLocalPath); - UE_LOG(LogCompile, Log, TEXT("Manifest.RootBuildPath=%s"), *Result.RootBuildPath); - UE_LOG(LogCompile, Log, TEXT("Manifest.TargetName=%s"), *Result.TargetName); - UE_LOG(LogCompile, Log, TEXT("Manifest.Modules=%d"), ModulesArray.Num()); - - Result.RootLocalPath = FPaths::ConvertRelativePathToFull(FilenamePath, Result.RootLocalPath); - Result.RootBuildPath = FPaths::ConvertRelativePathToFull(FilenamePath, Result.RootBuildPath); - - // Ensure directories end with a slash, because this aids their use with FPaths::MakePathRelativeTo. - if (!Result.RootLocalPath.EndsWith(TEXT("/"))) - { - Result.RootLocalPath += TEXT("/"); - } - - if (!Result.RootBuildPath.EndsWith(TEXT("/"))) - { - Result.RootBuildPath += TEXT("/"); - } - - int32 ModuleIndex = 0; - - for (const TSharedPtr& Module : ModulesArray) - { - const TSharedPtr& ModuleObj = Module->AsObject(); - - TArray> ClassesHeaders; - TArray> PublicHeaders; - TArray> PrivateHeaders; - - Result.Modules.AddZeroed(); - FManifestModule& KnownModule = Result.Modules.Last(); - - FString Outer = FString::Printf(TEXT("Modules[%d]"), ModuleIndex); - FString GeneratedCodeVersionString; - GetJsonFieldValue(KnownModule.Name, ModuleObj, TEXT("Name"), *Outer); - GetJsonFieldValue(KnownModule.BaseDirectory, ModuleObj, TEXT("BaseDirectory"), *Outer); - GetJsonFieldValue(KnownModule.IncludeBase, ModuleObj, TEXT("IncludeBase"), *Outer); - GetJsonFieldValue(KnownModule.GeneratedIncludeDirectory, ModuleObj, TEXT("OutputDirectory"), *Outer); - GetJsonFieldValue(KnownModule.SaveExportedHeaders, ModuleObj, TEXT("SaveExportedHeaders"), *Outer); - GetJsonFieldValue(ClassesHeaders, ModuleObj, TEXT("ClassesHeaders"), *Outer); - GetJsonFieldValue(PublicHeaders, ModuleObj, TEXT("PublicHeaders"), *Outer); - GetJsonFieldValue(PrivateHeaders, ModuleObj, TEXT("PrivateHeaders"), *Outer); - GetJsonFieldValue(KnownModule.GeneratedCPPFilenameBase, ModuleObj, TEXT("GeneratedCPPFilenameBase"), *Outer); - GetJsonFieldValue(GeneratedCodeVersionString, ModuleObj, TEXT("UHTGeneratedCodeVersion"), *Outer); - KnownModule.GeneratedCodeVersion = ToGeneratedCodeVersion(GeneratedCodeVersionString); - - FString ModuleTypeText; - GetJsonFieldValue(ModuleTypeText, ModuleObj, TEXT("ModuleType"), *Outer); - KnownModule.ModuleType = EBuildModuleType::Parse(*ModuleTypeText); - - FString OverrideModuleTypeText; - GetJsonFieldValue(OverrideModuleTypeText, ModuleObj, TEXT("OverrideModuleType"), *Outer); - KnownModule.OverrideModuleType = EPackageOverrideType::Parse(*OverrideModuleTypeText); - - KnownModule.LongPackageName = FPackageName::ConvertToLongScriptPackageName(*KnownModule.Name); - - // Convert relative paths - KnownModule.BaseDirectory = FPaths::ConvertRelativePathToFull(FilenamePath, KnownModule.BaseDirectory); - KnownModule.IncludeBase = FPaths::ConvertRelativePathToFull(FilenamePath, KnownModule.IncludeBase); - KnownModule.GeneratedIncludeDirectory = FPaths::ConvertRelativePathToFull(FilenamePath, KnownModule.GeneratedIncludeDirectory); - KnownModule.GeneratedCPPFilenameBase = FPaths::ConvertRelativePathToFull(FilenamePath, KnownModule.GeneratedCPPFilenameBase); - - // Ensure directories end with a slash, because this aids their use with FPaths::MakePathRelativeTo. - if (!KnownModule.BaseDirectory .EndsWith(TEXT("/"))) { KnownModule.BaseDirectory .AppendChar(TEXT('/')); } - if (!KnownModule.IncludeBase .EndsWith(TEXT("/"))) { KnownModule.IncludeBase .AppendChar(TEXT('/')); } - if (!KnownModule.GeneratedIncludeDirectory.EndsWith(TEXT("/"))) { KnownModule.GeneratedIncludeDirectory.AppendChar(TEXT('/')); } - - KnownModule.PublicUObjectClassesHeaders.AddZeroed(ClassesHeaders.Num()); - KnownModule.PublicUObjectHeaders .AddZeroed(PublicHeaders .Num()); - KnownModule.PrivateUObjectHeaders .AddZeroed(PrivateHeaders.Num()); - - ProcessHeaderArray(KnownModule.PublicUObjectClassesHeaders.GetData(), ClassesHeaders, *(Outer + TEXT(".ClassHeaders"))); - ProcessHeaderArray(KnownModule.PublicUObjectHeaders .GetData(), PublicHeaders , *(Outer + TEXT(".PublicHeaders" ))); - ProcessHeaderArray(KnownModule.PrivateUObjectHeaders .GetData(), PrivateHeaders, *(Outer + TEXT(".PrivateHeaders"))); - - // Sort the headers alphabetically. This is just to add determinism to the compilation dependency order, since we currently - // don't rely on explicit includes (but we do support 'dependson') - // @todo uht: Ideally, we should sort these by sensical order before passing them in -- or better yet, follow include statements ourselves in here. - KnownModule.PublicUObjectClassesHeaders.Sort(); - KnownModule.PublicUObjectHeaders .Sort(); - KnownModule.PrivateUObjectHeaders .Sort(); - - UE_LOG(LogCompile, Log, TEXT(" %s"), *KnownModule.Name); - UE_LOG(LogCompile, Log, TEXT(" .BaseDirectory=%s"), *KnownModule.BaseDirectory); - UE_LOG(LogCompile, Log, TEXT(" .IncludeBase=%s"), *KnownModule.IncludeBase); - UE_LOG(LogCompile, Log, TEXT(" .GeneratedIncludeDirectory=%s"), *KnownModule.GeneratedIncludeDirectory); - UE_LOG(LogCompile, Log, TEXT(" .SaveExportedHeaders=%s"), KnownModule.SaveExportedHeaders ? TEXT("True") : TEXT("False")); - UE_LOG(LogCompile, Log, TEXT(" .GeneratedCPPFilenameBase=%s"), *KnownModule.GeneratedCPPFilenameBase); - UE_LOG(LogCompile, Log, TEXT(" .ModuleType=%s"), *ModuleTypeText); - - ++ModuleIndex; - } - - return Result; -} - -bool FManifestModule::NeedsRegeneration() const -{ - if (ShouldForceRegeneration()) - { - return true; - } - FString Timestamp; - TCHAR TimestampText[] = TEXT("Timestamp"); - Timestamp.Empty(GeneratedIncludeDirectory.Len() + UE_ARRAY_COUNT(TimestampText)); - Timestamp += GeneratedIncludeDirectory; - Timestamp += TEXT("Timestamp"); - - if (!FPaths::FileExists(Timestamp)) - { - // No timestamp, must regenerate. - return true; - } - - FDateTime TimestampFileLastModify = IFileManager::Get().GetTimeStamp(*Timestamp); - - for (const FString& Header : PublicUObjectClassesHeaders) - { - if (IFileManager::Get().GetTimeStamp(*Header) > TimestampFileLastModify) - { - UE_LOG(LogCompile, Log, TEXT("File %s is newer than last timestamp. Regenerating reflection data for module %s."), *Header, *Name); - return true; - } - } - - for (const FString& Header : PublicUObjectHeaders) - { - if (IFileManager::Get().GetTimeStamp(*Header) > TimestampFileLastModify) - { - UE_LOG(LogCompile, Log, TEXT("File %s is newer than last timestamp. Regenerating reflection data for module %s."), *Header, *Name); - return true; - } - } - - for (const FString& Header : PrivateUObjectHeaders) - { - if (IFileManager::Get().GetTimeStamp(*Header) > TimestampFileLastModify) - { - UE_LOG(LogCompile, Log, TEXT("File %s is newer than last timestamp. Regenerating reflection data for module %s."), *Header, *Name); - return true; - } - } - - // No header is newer than timestamp, no need to regenerate. - return false; -} - -bool FManifestModule::IsCompatibleWith(const FManifestModule& ManifestModule) -{ - return ModuleType == ManifestModule.ModuleType - && SaveExportedHeaders == ManifestModule.SaveExportedHeaders - && GeneratedCodeVersion == ManifestModule.GeneratedCodeVersion - && Name == ManifestModule.Name - && LongPackageName == ManifestModule.LongPackageName - && BaseDirectory == ManifestModule.BaseDirectory - && IncludeBase == ManifestModule.IncludeBase - && GeneratedIncludeDirectory == ManifestModule.GeneratedIncludeDirectory - && PublicUObjectClassesHeaders == ManifestModule.PublicUObjectClassesHeaders - && PublicUObjectHeaders == ManifestModule.PublicUObjectHeaders - && PrivateUObjectHeaders == ManifestModule.PrivateUObjectHeaders - && GeneratedCPPFilenameBase == ManifestModule.GeneratedCPPFilenameBase; -} diff --git a/Engine/Source/Programs/UHTLite/Private/Manifest.h b/Engine/Source/Programs/UHTLite/Private/Manifest.h deleted file mode 100644 index 3d7d6ccb40ee..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Manifest.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "GeneratedCodeVersion.h" -#include "IScriptGeneratorPluginInterface.h" - -class FArchive; - -struct FManifestModule -{ - /** The name of the module */ - FString Name; - - /** Module type */ - EBuildModuleType::Type ModuleType; - - /** Overridden package settings to add additional flags that can help with organization */ - EPackageOverrideType::Type OverrideModuleType; - - /** Long package name for this module's UObject class */ - FString LongPackageName; - - /** Base directory of this module on disk */ - FString BaseDirectory; - - /** The directory to which #includes from this module should be relative */ - FString IncludeBase; - - /** Directory where generated include files should go */ - FString GeneratedIncludeDirectory; - - /** List of C++ public 'Classes' header files with UObjects in them (legacy) */ - TArray PublicUObjectClassesHeaders; - - /** List of C++ public header files with UObjects in them */ - TArray PublicUObjectHeaders; - - /** List of C++ private header files with UObjects in them */ - TArray PrivateUObjectHeaders; - - /** Base (i.e. extensionless) path+filename of where to write out the module's .generated.* files */ - FString GeneratedCPPFilenameBase; - - /** Whether or not to write out headers that have changed */ - bool SaveExportedHeaders; - - /** Version of generated code. */ - EGeneratedCodeVersion GeneratedCodeVersion; - - /** Returns true if module headers were modified since last code generation. */ - bool NeedsRegeneration() const; - - /** Returns true if modules are compatible. Used to determine if module data can be loaded from makefile. */ - bool IsCompatibleWith(const FManifestModule& ManifestModule); - - friend FArchive& operator<<(FArchive& Ar, FManifestModule& ManifestModule) - { - Ar << ManifestModule.Name; - Ar << ManifestModule.ModuleType; - Ar << ManifestModule.OverrideModuleType; - Ar << ManifestModule.LongPackageName; - Ar << ManifestModule.BaseDirectory; - Ar << ManifestModule.IncludeBase; - Ar << ManifestModule.GeneratedIncludeDirectory; - Ar << ManifestModule.PublicUObjectClassesHeaders; - Ar << ManifestModule.PublicUObjectHeaders; - Ar << ManifestModule.PrivateUObjectHeaders; - Ar << ManifestModule.GeneratedCPPFilenameBase; - Ar << ManifestModule.SaveExportedHeaders; - Ar << ManifestModule.GeneratedCodeVersion; - - return Ar; - } - - bool ShouldForceRegeneration() const - { - return bForceRegeneration; - } - - void ForceRegeneration() - { - bForceRegeneration = true; - } - -private: - bool bForceRegeneration; -}; - - -struct FManifest -{ - bool IsGameTarget; - FString RootLocalPath; - FString RootBuildPath; - FString TargetName; - FString ExternalDependenciesFile; - - /** Ordered list of modules that define UObjects or UStructs, which we may need to generate - code for. The list is in module dependency order, such that most dependent modules appear first. */ - TArray Modules; - - /** - * Loads a *.uhtmanifest from the specified filename. - * - * @param Filename The filename of the manifest to load. - * @return The loaded module info. - */ - static FManifest LoadFromFile(const FString& Filename); - - friend FArchive& operator<<(FArchive& Ar, FManifest& Manifest) - { - Ar << Manifest.IsGameTarget; - Ar << Manifest.RootLocalPath; - Ar << Manifest.RootBuildPath; - Ar << Manifest.TargetName; - Ar << Manifest.Modules; - - return Ar; - } -}; diff --git a/Engine/Source/Programs/UHTLite/Private/NativeClassExporter.h b/Engine/Source/Programs/UHTLite/Private/NativeClassExporter.h deleted file mode 100644 index b06af6f1a001..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/NativeClassExporter.h +++ /dev/null @@ -1,615 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Classes.h" -#include "Async/TaskGraphInterfaces.h" -#include "HeaderParser.h" - -class FUnrealSourceFile; -class UPackage; -class FProperty; -class UFunction; -class UStruct; -class UField; -class UClass; -class UEnum; -class UScriptStruct; -class UDelegateFunction; -class FClassMetaData; -class FOutputDevice; -struct FFuncInfo; -struct FPreloadHeaderFileInfo; - -// -// FNativeClassHeaderGenerator -// - -namespace EExportFunctionHeaderStyle -{ - enum Type - { - Definition, - Declaration - }; -} - -namespace EExportFunctionType -{ - enum Type - { - Interface, - Function, - Event - }; -} - -class FClass; -class FClasses; -class FModuleClasses; -class FScope; - -// These are declared in this way to allow swapping out the classes for something more optimized in the future -typedef FStringOutputDevice FUHTStringBuilder; -typedef FStringOutputDeviceCountLines FUHTStringBuilderLineCounter; - -enum class EExportingState -{ - Normal, - TypeEraseDelegates -}; - -enum class EExportCallbackType -{ - Interface, - Class -}; - -struct FPropertyNamePointerPair -{ - FPropertyNamePointerPair(FString InName, FProperty* InProp) - : Name(MoveTemp(InName)) - , Prop(InProp) - { - } - - FString Name; - FProperty* Prop; -}; - -FString CreateUTF8LiteralString(const FString& Str); - -struct FNativeClassHeaderGenerator -{ -private: - // UHTLite - const FHeaderParser& HeaderParser; - FString API; - FString APIStringPrivate; - - /** - * Gets API string for this header. - */ - const FString& GetAPIString() const - { - return APIStringPrivate; - } - - const UPackage* Package; - - /** A collection of structures used to gather various kinds of references conveniently grouped together to make passing easier */ - struct FReferenceGatherers - { - FReferenceGatherers(TSet* InUniqueCrossModuleReferences, TSet& InPackageHeaderPaths, TArray& InTempHeaderPaths) - : UniqueCrossModuleReferences(InUniqueCrossModuleReferences) - , PackageHeaderPaths(InPackageHeaderPaths) - , TempHeaderPaths(InTempHeaderPaths) - { - } - - /** Set of already exported cross-module references, to prevent duplicates */ - TSet* UniqueCrossModuleReferences; - /** Array of all header filenames from the current package. */ - TSet& PackageHeaderPaths; - /** Array of temp filenames that for files to overwrite headers */ - TArray& TempHeaderPaths; - /** Forward declarations that we need. */ - TSet ForwardDeclarations; - - }; - - /** the existing disk version of the header for this package's names */ - FString OriginalNamesHeader; - - /** References to the tasks to save the temp files so as to be able to wait and be sure they are complete before moving them */ - FGraphEventArray TempSaveTasks; - - /** If false, exported headers will not be saved to disk */ - bool bAllowSaveExportedHeaders; - - /** - * Exports the struct's C++ properties to the HeaderText output device and adds special - * compiler directives for GCC to pack as we expect. - * - * @param Out alternate output device - * @param Struct UStruct to export properties - * @param TextIndent Current text indentation - */ - void ExportProperties(FOutputDevice& Out, UStruct* Struct, int32 TextIndent) const; - - /** Return the name of the singleton function that returns the UObject for Item */ - static const FString& GetPackageSingletonName(const UPackage* Item, TSet* UniqueCrossModuleReferences); - - /** Return the name of the singleton function that returns the UObject for Item */ - static const FString& GetSingletonName(UField* Item, TSet* UniqueCrossModuleReferences, bool bRequiresValidObject=true); - - /** Return the address of the singleton function - handles nullptr */ - static FString GetSingletonNameFuncAddr(UField* Item, TSet* UniqueCrossModuleReferences, bool bRequiresValidObject=true); - - /** - * Returns the name (overridden if marked up) with TEXT("") or "" wrappers for use in a string literal. - */ - template - static FString GetOverriddenNameForLiteral(const T* Item) - { - const FString& OverriddenName = Item->GetMetaData(TEXT("OverrideNativeName")); - if (!OverriddenName.IsEmpty()) - { - return TEXT("TEXT(\"") + OverriddenName + TEXT("\")"); - } - return TEXT("\"") + Item->GetName() + TEXT("\""); - } - - /** - * Returns the name (overridden if marked up) or "" wrappers for use in a string literal. - */ - template - static FString GetUTF8OverriddenNameForLiteral(const T* Item) - { - const FString& OverriddenName = Item->GetMetaData(TEXT("OverrideNativeName")); - if (!OverriddenName.IsEmpty()) - { - return CreateUTF8LiteralString(OverriddenName); - } - return CreateUTF8LiteralString(Item->GetName()); - } - - /** - * Export functions used to find and call C++ or script implementation of a script function in the interface - */ - void ExportInterfaceCallFunctions(FOutputDevice& OutCpp, FUHTStringBuilder& Out, FReferenceGatherers& OutReferenceGatherers, const TArray& CallbackFunctions, const TCHAR* ClassName) const; - - /** - * Export UInterface boilerplate. - * - * @param UInterfaceBoilerplate Device to export to. - * @param Class Interface to export. - * @param FriendText Friend text for this boilerplate. - */ - static void ExportUInterfaceBoilerplate(FUHTStringBuilder& UInterfaceBoilerplate, FClass* Class, const FString& FriendText); - -public: - - enum class EExportClassOutFlags - { - None = 0x0, - NeedsPushModelHeaders = 0x1 << 0, - }; - -private: - - /** - * After all of the dependency checking, and setup for isolating the generated code, actually export the class - * - * @param OutEnums Output device for enums declarations. - * @param OutputGetter The function to call to get the output. - * @param SourceFile Source file to export. - */ - void ExportClassFromSourceFileInner( - FOutputDevice& OutGeneratedHeaderText, - FOutputDevice& OutputGetter, - FOutputDevice& OutDeclarations, - FReferenceGatherers& OutReferenceGatherers, - FClass* Class, - const FUnrealSourceFile& SourceFile, - EExportClassOutFlags& OutFlags - ) const; - - /** - * After all of the dependency checking, but before actually exporting the class, set up the generated code - * - * @param SourceFile Source file to export. - */ - bool WriteHeader(const FPreloadHeaderFileInfo& FileInfo, const FString& InBodyText, const TSet& InAdditionalHeaders, FReferenceGatherers& InOutReferenceGatherers, FGraphEventRef& OutTempSaveTask) const; - - /** - * Returns a string in the format CLASS_Something|CLASS_Something which represents all class flags that are set for the specified - * class which need to be exported as part of the DECLARE_CLASS macro - */ - static FString GetClassFlagExportText( UClass* Class ); - - /** - * Exports the header text for the enum specified - * - * @param Out the output device for the mirror struct - * @param Enums the enum to export - */ - void ExportEnum(FOutputDevice& Out, UEnum* Enum) const; - - /** - * Exports the inl text for enums declared in non-UClass headers. - * - * @param OutputGetter The function to call to get the output. - * @param Enum the enum to export - */ - void ExportGeneratedEnumInitCode(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UEnum* Enum) const; - - /** - * Exports the macro declarations for GENERATED_BODY() for each Foo in the struct specified - * - * @param Out output device - * @param Struct The struct to export - */ - void ExportGeneratedStructBodyMacros(FOutputDevice& OutGeneratedHeaderText, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UScriptStruct* Struct) const; - - /** - * Exports a local mirror of the specified struct; used to get offsets - * - * @param Out the output device for the mirror struct - * @param Structs the struct to export - * @param TextIndent the current indentation of the header exporter - */ - void ExportMirrorsForNoexportStruct(FOutputDevice& Out, UScriptStruct* Struct, int32 TextIndent) const; - - /** - * Exports the parameter struct declarations for the list of functions specified - * - * @param Function the function that (may) have parameters which need to be exported - * @return true if the structure generated is not completely empty - */ - static bool WillExportEventParms( UFunction* Function ); - - /** - * Exports C++ type declarations for delegates - * - * @param Out output device - * @param SourceFile Source file of the delegate. - * @param DelegateFunctions the functions that have parameters which need to be exported - */ - void ExportDelegateDeclaration(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UFunction* Function) const; - - /** - * Exports C++ type definitions for delegates - * - * @param Out output device - * @param SourceFile Source file of the delegate. - * @param DelegateFunctions the functions that have parameters which need to be exported - */ - void ExportDelegateDefinition(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UFunction* Function) const; - - /** - * Exports the parameter struct declarations for the given function. - * - * @param Out output device - * @param Function the function that have parameters which need to be exported - * @param Indent number of spaces to put before each line - * @param bOutputConstructor If true, output a constructor for the param struct - */ - void ExportEventParm(FUHTStringBuilder& Out, TSet& PropertyFwd, UFunction* Function, int32 Indent, bool bOutputConstructor, EExportingState ExportingState) const; - - /** - * Move the temp header files into the .h files - * - * @param PackageName Name of the package being saved - * @param TempHeaderPaths Names of all the headers to move - */ - static void ExportUpdatedHeaders(FString&& PackageName, TArray&& TempHeaderPaths, FGraphEventArray& InTempSaveTasks); - - /** - * Exports the generated cpp file for all functions/events/delegates in package. - */ - static void ExportGeneratedCPP(FOutputDevice& Out, const TSet& InCrossModuleReferences, const TCHAR* EmptyLinkFunctionPostfix, const TCHAR* Body, const TCHAR* OtherIncludes); - - /** - * Get the intrinsic null value for this property - * - * @param Prop the property to get the null value for - * @param bMacroContext true when exporting the P_GET* macro, false when exporting the friendly C++ function header - * @param bInitializer if true, will just return ForceInit instead of FStruct(ForceInit) - * - * @return the intrinsic null value for the property (0 for ints, TEXT("") for strings, etc.) - */ - static FString GetNullParameterValue( FProperty* Prop, bool bInitializer = false ); - - /** - * Exports a native function prototype - * - * @param Out Where to write the exported function prototype to. - * @param FunctionData data representing the function to export - * @param FunctionType Whether to export this function prototype as an event stub, an interface or a native function stub. - * @param FunctionHeaderStyle Whether we're outputting a declaration or definition. - * @param ExtraParam Optional extra parameter that will be added to the declaration as the first argument - */ - void ExportNativeFunctionHeader( - FOutputDevice& Out, - TSet& OutFwdDecls, - const FFuncInfo& FunctionData, - EExportFunctionType::Type FunctionType, - EExportFunctionHeaderStyle::Type FunctionHeaderStyle, - const TCHAR* ExtraParam, - const TCHAR* APIString - ) const; - - /** - * Runs checks whether necessary RPC functions exist for function described by FunctionData. - * - * @param FunctionData Data representing the function to export. - * @param ClassName Name of currently parsed class. - * @param ImplementationPosition Position in source file of _Implementation function for function described by FunctionData. - * @param ValidatePosition Position in source file of _Validate function for function described by FunctionData. - * @param SourceFile Currently analyzed source file. - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - void CheckRPCFunctions(FReferenceGatherers& OutReferenceGatherers, const FFuncInfo& FunctionData, const FString& ClassName, int32 ImplementationPosition, int32 ValidatePosition, const FUnrealSourceFile& SourceFile) const; - */ - - /** - * Exports the native stubs for the list of functions specified - * - * @param SourceFile current source file - * @param Class class - * @param ClassData class data - */ - void ExportNativeFunctions(FOutputDevice& OutGeneratedHeaderText, FOutputDevice& OutGeneratedCPPText, FOutputDevice& OutMacroCalls, FOutputDevice& OutNoPureDeclsMacroCalls, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UClass* Class, FClassMetaData* ClassData) const; - - /** - * Export the actual internals to a standard thunk function - * - * @param RPCWrappers output device for writing - * @param Function given function - * @param FunctionData function data for the current function - * @param Parameters list of parameters in the function - * @param Return return parameter for the function - */ - void ExportFunctionThunk(FUHTStringBuilder& RPCWrappers, FReferenceGatherers& OutReferenceGatherers, UFunction* Function, const FFuncInfo& FunctionData, const TArray& Parameters, FProperty* Return) const; - - /** Exports the native function registration code for the given class. */ - static void ExportNatives(FOutputDevice& Out, FClass* Class); - - /** - * Exports generated singleton functions for UObjects that used to be stored in .u files. - * - * @param Out The destination to write to. - * @param SourceFile The source file being processed. - * @param Class Class to export - * @param OutFriendText (Output parameter) Friend text - */ - void ExportNativeGeneratedInitCode(FOutputDevice& Out, FOutputDevice& OutDeclarations, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, FClass* Class, FUHTStringBuilder& OutFriendText) const; - - /** - * Export given function. - * - * @param Out The destination to write to. - * @param Function Given function. - * @param bIsNoExport Is in NoExport class. - */ - void ExportFunction(FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const FUnrealSourceFile& SourceFile, UFunction* Function, bool bIsNoExport) const; - - /** - * Exports a generated singleton function to setup the package for compiled-in classes. - * - * @param Out The destination to write to. - * @param Package Package to export code for. - **/ - void ExportGeneratedPackageInitCode(FOutputDevice& Out, const TCHAR* InDeclarations, const UPackage* Package, uint32 CRC, TMap>& PackageSingletons); - - /** - * Function to output the C++ code necessary to set up the given array of properties - * - * @param DeclOut String output device to send the generated declarations to - * @param Out String output device to send the generated code to - * @param Scope The scope to prefix on all variable definitions - * @param Properties Array of properties to export - * @param Spaces String of spaces to use as an indent for the declaration - * @param Spaces String of spaces to use as an indent - * - * @return A pair of strings which represents the pointer and a count of the emitted properties. - */ - TTuple OutputProperties(FOutputDevice& DeclOut, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const TCHAR* Scope, const TArray& Properties, const TCHAR* DeclSpaces, const TCHAR* Spaces) const; - - /** - * Function to output the C++ code necessary to set up a property - * - * @param DeclOut String output device to send the generated declarations to - * @param Out String output device to send the generated code to - * @param Scope The scope to prefix on all variable definitions - * @param Prop Property to export - * @param DeclSpaces String of spaces to use as an indent for the declaration - * @param Spaces String of spaces to use as an indent - **/ - void OutputProperty(FOutputDevice& DeclOut, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, const TCHAR* Scope, TArray& PropertyNamesAndPointers, FProperty* Prop, const TCHAR* DeclSpaces, const TCHAR* Spaces) const; - - /** - * Function to output the C++ code necessary to set up a property, including an array property and its inner, array dimensions, etc. - * - * @param DeclOut The destination to write declarations to. - * @param Out The destination to write to. - * @param Prop Property to export - * @param OffsetStr String specifying the property offset - * @param Name Name for the generated variable - * @param DeclSpaces String of spaces to use as an indent for the declaration - * @param Spaces String of spaces to use as an indent - * @param SourceStruct Structure that the property offset is relative to - **/ - void PropertyNew(FOutputDevice& DeclOut, FOutputDevice& Out, FReferenceGatherers& OutReferenceGatherers, FProperty* Prop, const TCHAR* OffsetStr, const TCHAR* Name, const TCHAR* DeclSpaces, const TCHAR* Spaces, const TCHAR* SourceStruct = nullptr) const; - - /** - * Exports the proxy definitions for the list of enums specified - * - * @param SourceFile current source file - */ - void ExportCallbackFunctions( - FOutputDevice& OutGeneratedHeaderText, - FOutputDevice& Out, - TSet& OutFwdDecls, - const TArray& CallbackFunctions, - const TCHAR* CallbackWrappersMacroName, - EExportCallbackType ExportCallbackType, - const TCHAR* APIString - ) const; - - /** - * Determines if the property has alternate export text associated with it and if so replaces the text in PropertyText with the - * alternate version. (for example, structs or properties that specify a native type using export-text). Should be called immediately - * after ExportCppDeclaration() - * - * @param Prop the property that is being exported - * @param PropertyText the string containing the text exported from ExportCppDeclaration - */ - static void ApplyAlternatePropertyExportText(FProperty* Prop, FUHTStringBuilder& PropertyText, EExportingState ExportingState); - - /** - * Create a temp header file name from the header name - * - * @param CurrentFilename The filename off of which the current filename will be generated - * @param bReverseOperation Get the header from the temp file name instead - * - * @return The generated string - */ - static FString GenerateTempHeaderName( const FString& CurrentFilename, bool bReverseOperation = false ); - - /** - * Saves a generated header if it has changed. - * - * @param HeaderPath Header Filename - * @param NewHeaderContents Contents of the generated header. - * @return True if the header contents has changed, false otherwise. - */ - bool SaveHeaderIfChanged(FReferenceGatherers& OutReferenceGatherers, const FPreloadHeaderFileInfo& FileInfo, FString&& NewHeaderContents, FGraphEventRef& OutSaveTaskRef) const; - - /** - * Deletes all .generated.h files which do not correspond to any of the classes. - */ - static void DeleteUnusedGeneratedHeaders(TSet&& PackageHeaderPathSet); - - /** - * Exports macros that manages UObject constructors. - * - * @param VTableOut The destination to write vtable helpers to. - * @param StandardUObjectConstructorsMacroCall The destination to write standard constructor macros to. - * @param EnhancedUObjectConstructorsMacroCall The destination to write enhanced constructor macros to. - * @param ConstructorsMacroPrefix Prefix for constructors macro. - * @param Class Class for which to export macros. - */ - static void ExportConstructorsMacros(FOutputDevice& OutGeneratedHeaderText, FOutputDevice& VTableOut, FOutputDevice& StandardUObjectConstructorsMacroCall, FOutputDevice& EnhancedUObjectConstructorsMacroCall, const FString& ConstructorsMacroPrefix, FClass* Class, const TCHAR* APIArg); - - // UHTLite - TArray GetSourceFilesInDependencyOrder(const TSet& SourceFiles, const TSet& Ignore) const; - - // UHTLite - void GetSourceFilesInDependencyOrderRecursive(TArray& OutTest, FUnrealSourceFile* SourceFile, TSet& VisitedSet, bool bCheckDependenciesOnly, const TSet& Ignore) const; - - // UHTLite - void WriteReplicatedMacroData( - const ClassDefinitionRange& ClassRange, - const TCHAR* ClassCPPName, - const TCHAR* APIArg, - FClass* Class, - FClass* SuperClass, - FOutputDevice& Writer, - const FUnrealSourceFile& SourceFile, - FNativeClassHeaderGenerator::EExportClassOutFlags& OutFlags) const; - - // UHTLite - void ExportNetData(FOutputDevice& Out, UClass* Class, const TCHAR* APIArg) const; - - // UHTLite NOTE: This function modifies GeneratedCodeHashes! - void AddGeneratedCodeHash(void* Field, uint32 Hash) const; - - // UHTLite - FString GetGeneratedCodeHashTag(void* Field) const; - - // UHTLite NOTE: Globals! - static TMap GeneratedCodeHashes; - static FRWLock GeneratedCodeHashesLock; - -public: - - /** - * Properties in source files generated from blueprint assets have a symbol name that differs from the source asset. - * This function returns the original name of the field (rather than the native, symbol name). - */ - template - static FString GetOverriddenName(const T* Item) - { - const FString& OverriddenName = Item->GetMetaData(TEXT("OverrideNativeName")); - if (!OverriddenName.IsEmpty()) - { - return OverriddenName.ReplaceCharWithEscapedChar(); - } - return Item->GetName(); - } - - template - static FName GetOverriddenFName(const T* Item) - { - FString OverriddenName = Item->GetMetaData(TEXT("OverrideNativeName")); - if (!OverriddenName.IsEmpty()) - { - return FName(*OverriddenName); - } - return Item->GetFName(); - } - - template - static FString GetOverriddenPathName(const T* Item) - { - return FString::Printf(TEXT("%s.%s"), *FClass::GetTypePackageName(Item), *GetOverriddenName(Item)); - } - - // Constructor - FNativeClassHeaderGenerator( - const FHeaderParser& InHeaderParser, - const UPackage* InPackage, - const TSet& SourceFiles, - FClasses& AllClasses, - bool InAllowSaveExportedHeaders, - const FPublicSourceFileSet& PublicSourceFileSet, - const TMap& PackageToManifestModuleMap - ); - - /** - * Gets string with function return type. - * - * @param Function Function to get return type of. - * @return FString with function return type. - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - static FString GetFunctionReturnString(UFunction* Function, FReferenceGatherers& OutReferenceGatherers); - */ - - /** - * Gets string with function parameters (with names). - * - * @param Function Function to get parameters of. - * @return FString with function parameters. - */ - FString GetFunctionParameterString(UFunction* Function, FReferenceGatherers& OutReferenceGatherers) const; - - /** - * Checks if function is missing "virtual" specifier. - * - * @param SourceFile SourceFile where function is declared. - * @param FunctionNamePosition Position of name of function in SourceFile. - * @return true if function misses "virtual" specifier, false otherwise. - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - static bool IsMissingVirtualSpecifier(const FString& SourceFile, int32 FunctionNamePosition); - */ -}; - -ENUM_CLASS_FLAGS(FNativeClassHeaderGenerator::EExportClassOutFlags); \ No newline at end of file diff --git a/Engine/Source/Programs/UHTLite/Private/ParserClass.cpp b/Engine/Source/Programs/UHTLite/Private/ParserClass.cpp deleted file mode 100644 index 2bc7ebd23f6b..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ParserClass.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "ParserClass.h" -#include "UnrealHeaderTool.h" -#include "UObject/Package.h" -#include "Templates/Casts.h" - -const FName FClass::NAME_ReplaceConverted(TEXT("ReplaceConverted")); - -FString FClass::GetNameWithPrefix(EEnforceInterfacePrefix::Type EnforceInterfacePrefix) const -{ - const TCHAR* Prefix = 0; - - if (HasAnyClassFlags(CLASS_Interface)) - { - // Grab the expected prefix for interfaces (U on the first one, I on the second one) - switch (EnforceInterfacePrefix) - { - case EEnforceInterfacePrefix::None: - // For old-style files: "I" for interfaces, unless it's the actual "Interface" class, which gets "U" - if (GetFName() == NAME_Interface) - { - Prefix = TEXT("U"); - } - else - { - Prefix = TEXT("I"); - } - break; - - case EEnforceInterfacePrefix::I: - Prefix = TEXT("I"); - break; - - case EEnforceInterfacePrefix::U: - Prefix = TEXT("U"); - break; - - default: - check(false); - } - } - else - { - // Get the expected class name with prefix - Prefix = GetPrefixCPP(); - } - - return FString::Printf(TEXT("%s%s"), Prefix, *GetName()); -} - -FClass* FClass::GetSuperClass() const -{ - return static_cast(static_cast(this)->GetSuperClass()); -} - -FClass* FClass::GetClassWithin() const -{ - return (FClass*)ClassWithin; -} - -TArray FClass::GetInterfaceTypes() const -{ - TArray Result; - for (const FImplementedInterface& i : Interfaces) - { - Result.Add((FClass*)i.Class); - } - return Result; -} - -void FClass::GetHideCategories(TArray& OutHideCategories) const -{ - if (HasMetaData(FHeaderParserNames::NAME_HideCategories)) - { - const FString& HideCategories = GetMetaData(FHeaderParserNames::NAME_HideCategories); - HideCategories.ParseIntoArray(OutHideCategories, TEXT(" "), true); - } -} - -void FClass::GetShowCategories(TArray& OutShowCategories) const -{ - if (HasMetaData(FHeaderParserNames::NAME_ShowCategories)) - { - const FString& ShowCategories = GetMetaData(FHeaderParserNames::NAME_ShowCategories); - ShowCategories.ParseIntoArray(OutShowCategories, TEXT(" "), true); - } -} - -void FClass::GetSparseClassDataTypes(TArray& OutSparseClassDataTypes) const -{ - if (HasMetaData(FHeaderParserNames::NAME_SparseClassDataTypes)) - { - const FString& SparseClassDataTypes = GetMetaData(FHeaderParserNames::NAME_SparseClassDataTypes); - SparseClassDataTypes.ParseIntoArray(OutSparseClassDataTypes, TEXT(" "), true); - } -} - -bool FClass::IsOwnedByDynamicType(const UField* Field) -{ - for (const UField* OuterField = Cast(Field->GetOuter()); OuterField; OuterField = Cast(OuterField->GetOuter())) - { - if (IsDynamic(OuterField)) - { - return true; - } - } - return false; -} - -bool FClass::IsOwnedByDynamicType(const FField* Field) -{ - for (FFieldVariant Owner = Field->GetOwnerVariant(); Owner.IsValid(); Owner = Owner.GetOwnerVariant()) - { - if (Owner.IsUObject()) - { - return IsOwnedByDynamicType(Cast(Owner.ToUObject())); - } - else if (IsDynamic(Owner.ToField())) - { - return true; - } - } - return false; -} - -static TMap> UFieldTypePackageNames; -static TMap> FFieldTypePackageNames; - -template -const FString& GetTypePackageName_Inner(const T* Field, TMap>& TypePackageNames) -{ - static FRWLock Lock; - FRWScopeLock TypeNameLock(Lock, SLT_ReadOnly); - - TUniquePtr* TypePackageName = TypePackageNames.Find(Field); - if (TypePackageName == nullptr) - { - FString PackageName = Field->GetMetaData(FClass::NAME_ReplaceConverted); - if (PackageName.Len()) - { - int32 ObjectDotIndex = INDEX_NONE; - // Strip the object name - if (PackageName.FindChar(TEXT('.'), ObjectDotIndex)) - { - PackageName.MidInline(0, ObjectDotIndex, false); - } - } - else - { - PackageName = Field->GetOutermost()->GetName(); - } - - TypeNameLock.ReleaseReadOnlyLockAndAcquireWriteLock_USE_WITH_CAUTION(); - - // Check the map again in case another thread had also been waiting on writing this data and got the write lock first - TypePackageName = TypePackageNames.Find(Field); - if (TypePackageName == nullptr) - { - TypePackageName = &TypePackageNames.Add(Field, MakeUnique(MoveTemp(PackageName))); - } - } - return **TypePackageName; -} - -const FString& FClass::GetTypePackageName(const UField* Field) -{ - return GetTypePackageName_Inner(Field, UFieldTypePackageNames); -} - -const FString& FClass::GetTypePackageName(const FField* Field) -{ - return GetTypePackageName_Inner(Field, FFieldTypePackageNames); -} diff --git a/Engine/Source/Programs/UHTLite/Private/ParserClass.h b/Engine/Source/Programs/UHTLite/Private/ParserClass.h deleted file mode 100644 index f8df526978d7..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ParserClass.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/Class.h" -#include "UObject/Field.h" - -class UField; - -struct EEnforceInterfacePrefix -{ - enum Type - { - None, - I, - U - }; -}; - -class FClasses; - -class FClass : public UClass -{ -public: - FClass(); - - /** - * Returns the name of the given class with a valid prefix. - * - * @param InClass Class used to create a valid class name with prefix - */ - FString GetNameWithPrefix(EEnforceInterfacePrefix::Type EnforceInterfacePrefix = EEnforceInterfacePrefix::None) const; - - /** - * Returns the super class of this class, or NULL if there is no superclass. - * - * @return The super class of this class. - */ - FClass* GetSuperClass() const; - - /** - * Returns the 'within' class of this class. - * - * @return The 'within' class of this class. - */ - FClass* GetClassWithin() const; - - TArray GetInterfaceTypes() const; - - void GetHideCategories(TArray& OutHideCategories) const; - void GetShowCategories(TArray& OutShowCategories) const; - void GetSparseClassDataTypes(TArray& OutSparseClassDataTypes) const; - - /** Helper function that checks if the field is a dynamic type (can be constructed post-startup) */ - template - static bool IsDynamic(const T* Field) - { - return Field->HasMetaData(NAME_ReplaceConverted); - } - - /** Helper function that checks if the field is belongs to a dynamic type */ - static bool IsOwnedByDynamicType(const UField* Field); - static bool IsOwnedByDynamicType(const FField* Field); - - /** Helper function to get the source replaced package name */ - static const FString& GetTypePackageName(const UField* Field); - static const FString& GetTypePackageName(const FField* Field); - - static const FName NAME_ReplaceConverted; -}; diff --git a/Engine/Source/Programs/UHTLite/Private/ParserHelper.cpp b/Engine/Source/Programs/UHTLite/Private/ParserHelper.cpp deleted file mode 100644 index e7b24777163a..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ParserHelper.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - - -#include "ParserHelper.h" -#include "UnrealHeaderTool.h" -#include "Algo/Find.h" -#include "Misc/DefaultValueHelper.h" - -///////////////////////////////////////////////////// -// FClassMetaData - -/** - * Finds the metadata for the property specified - * - * @param Prop the property to search for - * - * @return pointer to the metadata for the property specified, or NULL - * if the property doesn't exist in the list (for example, if it - * is declared in a package that is already compiled and has had its - * source stripped) - */ -FTokenData* FClassMetaData::FindTokenData( FProperty* Prop ) -{ - check(Prop); - - FTokenData* Result = nullptr; - UObject* Outer = Prop->GetOwner(); - check(Outer); - UClass* OuterClass = nullptr; - if (Outer->IsA()) - { - Result = GlobalPropertyData.Find(Prop); - - if (Result == nullptr) - { - OuterClass = Cast(Outer); - - if (OuterClass != nullptr && OuterClass->GetSuperClass() != OuterClass) - { - OuterClass = OuterClass->GetSuperClass(); - } - } - } - else - { - UFunction* OuterFunction = Cast(Outer); - if ( OuterFunction != NULL ) - { - // function parameter, return, or local property - FFunctionData* FuncData = nullptr; - if (FFunctionData::TryFindForFunction(OuterFunction, FuncData)) - { - FPropertyData& FunctionParameters = FuncData->GetParameterData(); - Result = FunctionParameters.Find(Prop); - if ( Result == NULL ) - { - Result = FuncData->GetReturnTokenData(); - } - } - else - { - OuterClass = OuterFunction->GetOwnerClass(); - } - } - else - { - // struct property - UScriptStruct* OuterStruct = Cast(Outer); - check(OuterStruct != NULL); - OuterClass = OuterStruct->GetOwnerClass(); - } - } - - if (Result == nullptr && OuterClass != nullptr) - { - FClassMetaData* SuperClassData = GScriptHelper.FindClassData(OuterClass); - if (SuperClassData && SuperClassData != this) - { - Result = SuperClassData->FindTokenData(Prop); - } - } - - return Result; -} - -void FClassMetaData::AddInheritanceParent(FString&& InParent, FUnrealSourceFile* UnrealSourceFile) -{ - MultipleInheritanceParents.Add(new FMultipleInheritanceBaseClass(MoveTemp(InParent))); -} - -void FClassMetaData::AddInheritanceParent(UClass* ImplementedInterfaceClass, FUnrealSourceFile* UnrealSourceFile) -{ - MultipleInheritanceParents.Add(new FMultipleInheritanceBaseClass(ImplementedInterfaceClass)); -} - -///////////////////////////////////////////////////// -// FPropertyBase - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -const TCHAR* FPropertyBase::GetPropertyTypeText( EPropertyType Type ) -{ - switch ( Type ) - { - CASE_TEXT(CPT_None); - CASE_TEXT(CPT_Byte); - CASE_TEXT(CPT_Int8); - CASE_TEXT(CPT_Int16); - CASE_TEXT(CPT_Int); - CASE_TEXT(CPT_Int64); - CASE_TEXT(CPT_UInt16); - CASE_TEXT(CPT_UInt32); - CASE_TEXT(CPT_UInt64); - CASE_TEXT(CPT_Bool); - CASE_TEXT(CPT_Bool8); - CASE_TEXT(CPT_Bool16); - CASE_TEXT(CPT_Bool32); - CASE_TEXT(CPT_Bool64); - CASE_TEXT(CPT_Float); - CASE_TEXT(CPT_Double); - CASE_TEXT(CPT_ObjectReference); - CASE_TEXT(CPT_Interface); - CASE_TEXT(CPT_Name); - CASE_TEXT(CPT_Delegate); - CASE_TEXT(CPT_Struct); - CASE_TEXT(CPT_String); - CASE_TEXT(CPT_Text); - CASE_TEXT(CPT_MulticastDelegate); - CASE_TEXT(CPT_SoftObjectReference); - CASE_TEXT(CPT_WeakObjectReference); - CASE_TEXT(CPT_LazyObjectReference); - CASE_TEXT(CPT_Map); - CASE_TEXT(CPT_Set); - CASE_TEXT(CPT_FieldPath); - CASE_TEXT(CPT_MAX); - } - - return TEXT(""); -} -*/ - -///////////////////////////////////////////////////// -// FToken - -/** - * Copies the properties from this token into another. - * - * @param Other the token to copy this token's properties to. - */ -FToken& FToken::operator=(const FToken& Other) -{ - FPropertyBase::operator=((FPropertyBase&)Other); - - TokenType = Other.TokenType; - TokenName = Other.TokenName; - bTokenNameInitialized = Other.bTokenNameInitialized; - StartPos = Other.StartPos; - StartLine = Other.StartLine; - TokenProperty = Other.TokenProperty; - - FCString::Strncpy(Identifier, Other.Identifier, NAME_SIZE); - FMemory::Memcpy(String, Other.String, sizeof(String)); - - return *this; -} - -FToken& FToken::operator=(FToken&& Other) -{ - FPropertyBase::operator=(MoveTemp(Other)); - - TokenType = Other.TokenType; - TokenName = Other.TokenName; - bTokenNameInitialized = Other.bTokenNameInitialized; - StartPos = Other.StartPos; - StartLine = Other.StartLine; - TokenProperty = Other.TokenProperty; - - FCString::Strncpy(Identifier, Other.Identifier, NAME_SIZE); - FMemory::Memcpy(String, Other.String, sizeof(String)); - - return *this; -} - -///////////////////////////////////////////////////// -// FAdvancedDisplayParameterHandler -static const FName NAME_AdvancedDisplay(TEXT("AdvancedDisplay")); - -FAdvancedDisplayParameterHandler::FAdvancedDisplayParameterHandler(const TMap* MetaData) - : NumberLeaveUnmarked(-1), AlreadyLeft(0), bUseNumber(false) -{ - if(MetaData) - { - const FString* FoundString = MetaData->Find(NAME_AdvancedDisplay); - if(FoundString) - { - FoundString->ParseIntoArray(ParametersNames, TEXT(","), true); - for(int32 NameIndex = 0; NameIndex < ParametersNames.Num();) - { - FString& ParameterName = ParametersNames[NameIndex]; - ParameterName.TrimStartAndEndInline(); - if(ParameterName.IsEmpty()) - { - ParametersNames.RemoveAtSwap(NameIndex); - } - else - { - ++NameIndex; - } - } - if(1 == ParametersNames.Num()) - { - bUseNumber = FDefaultValueHelper::ParseInt(ParametersNames[0], NumberLeaveUnmarked); - } - } - } -} - -bool FAdvancedDisplayParameterHandler::ShouldMarkParameter(const FString& ParameterName) -{ - if(bUseNumber) - { - if(NumberLeaveUnmarked < 0) - { - return false; - } - if(AlreadyLeft < NumberLeaveUnmarked) - { - AlreadyLeft++; - return false; - } - return true; - } - return ParametersNames.Contains(ParameterName); -} - -bool FAdvancedDisplayParameterHandler::CanMarkMore() const -{ - return bUseNumber ? (NumberLeaveUnmarked > 0) : (0 != ParametersNames.Num()); -} - -TMap > FFunctionData::FunctionDataMap; - -FFunctionData* FFunctionData::FindForFunction(UFunction* Function) -{ - TUniqueObj* Output = FunctionDataMap.Find(Function); - - check(Output); - - return &(*Output).Get(); -} - -// UHTLite NOTE: Not required for code-gen. Will be refactored later. -/* -FFunctionData* FFunctionData::Add(UFunction* Function) -{ - TUniqueObj& Output = FunctionDataMap.Add(Function); - - return &Output.Get(); -} -*/ - -FFunctionData* FFunctionData::Add(FFuncInfo&& FunctionInfo) -{ - TUniqueObj& Output = FunctionDataMap.Emplace(FunctionInfo.FunctionReference, MoveTemp(FunctionInfo)); - - return &Output.Get(); -} - -bool FFunctionData::TryFindForFunction(UFunction* Function, FFunctionData*& OutData) -{ - TUniqueObj* Output = FunctionDataMap.Find(Function); - - if (!Output) - { - return false; - } - - OutData = &(*Output).Get(); - return true; -} - -FClassMetaData* FCompilerMetadataManager::AddClassData(UStruct* Struct, FUnrealSourceFile* UnrealSourceFile) -{ - TUniquePtr* pClassData = Find(Struct); - if (pClassData == NULL) - { - pClassData = &Emplace(Struct, new FClassMetaData()); - } - - return pClassData->Get(); -} - -FClassMetaData* FCompilerMetadataManager::AddInterfaceClassData(UStruct* Struct, FUnrealSourceFile* UnrealSourceFile) -{ - FClassMetaData* ClassData = AddClassData(Struct, UnrealSourceFile); - ClassData->ParsedInterface = EParsedInterface::ParsedUInterface; - InterfacesToVerify.Emplace(Struct, ClassData); - return ClassData; -} - -FTokenData* FPropertyData::Set(FProperty* InKey, FTokenData&& InValue, FUnrealSourceFile* UnrealSourceFile) -{ - FTokenData* Result = NULL; - - TSharedPtr* pResult = Super::Find(InKey); - if (pResult != NULL) - { - Result = pResult->Get(); - *Result = MoveTemp(InValue); - } - else - { - pResult = &Super::Emplace(InKey, new FTokenData(MoveTemp(InValue))); - Result = pResult->Get(); - } - - return Result; -} - -void FCompilerMetadataManager::CheckForNoIInterfaces() -{ - for (const TPair& StructDataPair : InterfacesToVerify) - { - if (StructDataPair.Value->ParsedInterface == EParsedInterface::ParsedUInterface) - { - FString Name = StructDataPair.Key->GetName(); - FError::Throwf(TEXT("UInterface 'U%s' parsed without a corresponding 'I%s'"), *Name, *Name); - } - } - InterfacesToVerify.Reset(); -} \ No newline at end of file diff --git a/Engine/Source/Programs/UHTLite/Private/ParserHelper.h b/Engine/Source/Programs/UHTLite/Private/ParserHelper.h deleted file mode 100644 index 52ef3f3defa0..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/ParserHelper.h +++ /dev/null @@ -1,1948 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/ErrorException.h" -#include "UObject/Package.h" -#include "UObject/MetaData.h" -#include "UObject/TextProperty.h" -#include "UObject/EnumProperty.h" -#include "UObject/FieldPathProperty.h" -#include "UnrealHeaderToolGlobals.h" -#include "ClassMaps.h" -#include "Templates/UniqueObj.h" -#include "Templates/UniquePtr.h" - -class UEnum; -class UScriptStruct; -class FProperty; -class FUnrealSourceFile; -class UObject; -class UField; -class UMetaData; -class FHeaderParser; - -extern class FCompilerMetadataManager GScriptHelper; - -/*----------------------------------------------------------------------------- - FPropertyBase. ------------------------------------------------------------------------------*/ - -enum EFunctionExportFlags -{ - FUNCEXPORT_Final =0x00000001, // function declaration included "final" keyword. Used to differentiate between functions that have FUNC_Final only because they're private - // =0x00000002, - // =0x00000004, - FUNCEXPORT_RequiredAPI =0x00000008, // Function should be exported as a public API function - FUNCEXPORT_Inline =0x00000010, // export as an inline static C++ function - FUNCEXPORT_CppStatic =0x00000020, // Export as a real C++ static function, causing thunks to call via ClassName::FuncName instead of this->FuncName - FUNCEXPORT_CustomThunk =0x00000040, // Export no thunk function; the user will manually define a custom one - // =0x00000080, - // =0x00000100, -}; - -enum EPropertyHeaderExportFlags -{ - PROPEXPORT_Public =0x00000001, // property should be exported as public - PROPEXPORT_Private =0x00000002, // property should be exported as private - PROPEXPORT_Protected =0x00000004, // property should be exported as protected -}; - -struct EPointerType -{ - enum Type - { - None, - Native - }; -}; - -struct EArrayType -{ - enum Type - { - None, - Static, - Dynamic, - Set - }; -}; - -enum class EAllocatorType -{ - Default, - MemoryImage -}; - -struct ERefQualifier -{ - enum Type - { - None, - ConstRef, - NonConstRef - }; -}; - -enum class EIntType -{ - None, - Sized, // e.g. int32, int16 - Unsized // e.g. int, unsigned int -}; - -#ifndef CASE_TEXT -#define CASE_TEXT(txt) case txt: return TEXT(#txt) -#endif - -/** - * Basic information describing a type. - */ -class FPropertyBase : public TSharedFromThis -{ -public: - // Variables. - EPropertyType Type; - EArrayType::Type ArrayType; - EAllocatorType AllocatorType = EAllocatorType::Default; - EPropertyFlags PropertyFlags; - EPropertyFlags ImpliedPropertyFlags; - ERefQualifier::Type RefQualifier; // This is needed because of legacy stuff - FString mangles the flags for reasons that have become lost in time but we need this info for testing for invalid replicated function signatures. - - TSharedPtr MapKeyProp; - - /** - * A mask of EPropertyHeaderExportFlags which are used for modifying how this property is exported to the native class header - */ - uint32 PropertyExportFlags; - union - { - class UEnum* Enum; - class UClass* PropertyClass; - class UScriptStruct* Struct; - class UFunction* Function; - class FFieldClass* PropertyPathClass; -#if PLATFORM_64BITS - int64 StringSize; -#else - int32 StringSize; -#endif - }; - - UClass* MetaClass; - FName DelegateName; - UClass* DelegateSignatureOwnerClass; - FName RepNotifyName; - - /** Raw string (not type-checked) used for specifying special text when exporting a property to the *Classes.h file */ - FString ExportInfo; - - /** Map of key value pairs that will be added to the package's UMetaData for this property */ - TMap MetaData; - - EPointerType::Type PointerType; - EIntType IntType; - -public: - /** @name Constructors */ - //@{ - FPropertyBase(const FPropertyBase&) = default; - FPropertyBase(FPropertyBase&&) = default; - FPropertyBase& operator=(const FPropertyBase&) = default; - FPropertyBase& operator=(FPropertyBase&&) = default; - - explicit FPropertyBase(EPropertyType InType) - : Type (InType) - , ArrayType (EArrayType::None) - , PropertyFlags (CPF_None) - , ImpliedPropertyFlags(CPF_None) - , RefQualifier (ERefQualifier::None) - , PropertyExportFlags (PROPEXPORT_Public) - , StringSize (0) - , MetaClass (nullptr) - , DelegateName (NAME_None) - , DelegateSignatureOwnerClass(nullptr) - , RepNotifyName (NAME_None) - , PointerType (EPointerType::None) - , IntType (GetSizedIntTypeFromPropertyType(InType)) - { - } - - explicit FPropertyBase(EPropertyType InType, EIntType InIntType) - : Type (InType) - , ArrayType (EArrayType::None) - , PropertyFlags (CPF_None) - , ImpliedPropertyFlags(CPF_None) - , RefQualifier (ERefQualifier::None) - , PropertyExportFlags (PROPEXPORT_Public) - , StringSize (0) - , MetaClass (nullptr) - , DelegateName (NAME_None) - , DelegateSignatureOwnerClass(nullptr) - , RepNotifyName (NAME_None) - , PointerType (EPointerType::None) - , IntType (InIntType) - { - } - - explicit FPropertyBase(UEnum* InEnum, EPropertyType InType) - : Type (InType) - , ArrayType (EArrayType::None) - , PropertyFlags (CPF_None) - , ImpliedPropertyFlags(CPF_None) - , RefQualifier (ERefQualifier::None) - , PropertyExportFlags (PROPEXPORT_Public) - , Enum (InEnum) - , MetaClass (nullptr) - , DelegateName (NAME_None) - , DelegateSignatureOwnerClass(nullptr) - , RepNotifyName (NAME_None) - , PointerType (EPointerType::None) - , IntType (GetSizedIntTypeFromPropertyType(InType)) - { - } - - explicit FPropertyBase(UClass* InClass, bool bIsWeak = false, bool bWeakIsAuto = false, bool bIsLazy = false, bool bIsSoft = false) - : Type (CPT_ObjectReference) - , ArrayType (EArrayType::None) - , PropertyFlags (CPF_None) - , ImpliedPropertyFlags(CPF_None) - , RefQualifier (ERefQualifier::None) - , PropertyExportFlags (PROPEXPORT_Public) - , PropertyClass (InClass) - , MetaClass (nullptr) - , DelegateName (NAME_None) - , DelegateSignatureOwnerClass(nullptr) - , RepNotifyName (NAME_None) - , PointerType (EPointerType::None) - , IntType (EIntType::None) - { - // if this is an interface class, we use the FInterfaceProperty class instead of FObjectProperty - if ( InClass->HasAnyClassFlags(CLASS_Interface) ) - { - Type = CPT_Interface; - } - if (bIsLazy) - { - Type = CPT_LazyObjectReference; - } - else if (bIsSoft) - { - Type = CPT_SoftObjectReference; - } - else if (bIsWeak) - { - Type = CPT_WeakObjectReference; - if (bWeakIsAuto) - { - PropertyFlags |= CPF_AutoWeak; - } - } - } - - explicit FPropertyBase(UScriptStruct* InStruct) - : Type (CPT_Struct) - , ArrayType (EArrayType::None) - , PropertyFlags (CPF_None) - , ImpliedPropertyFlags(CPF_None) - , RefQualifier (ERefQualifier::None) - , PropertyExportFlags (PROPEXPORT_Public) - , Struct (InStruct) - , MetaClass (NULL) - , DelegateName (NAME_None) - , DelegateSignatureOwnerClass(nullptr) - , RepNotifyName (NAME_None) - , PointerType (EPointerType::None) - , IntType (EIntType::None) - { - } - - explicit FPropertyBase(FProperty* Property) - : PropertyExportFlags(PROPEXPORT_Public) - , DelegateName (NAME_None) - , DelegateSignatureOwnerClass(nullptr) - , RepNotifyName (NAME_None) - , IntType (EIntType::None) - { - checkSlow(Property); - - EArrayType::Type ArrType = EArrayType::None; - EPropertyFlags PropagateFlags = CPF_None; - FFieldClass* ClassOfProperty = Property->GetClass(); - - if( ClassOfProperty==FArrayProperty::StaticClass() ) - { - ArrType = EArrayType::Dynamic; - - // if we're an array, save up Parm flags so we can propagate them. - // below the array will be assigned the inner property flags. This allows propagation of Parm flags (out, optional..) - PropagateFlags = Property->PropertyFlags & CPF_ParmFlags; - Property = CastFieldChecked(Property)->Inner; - ClassOfProperty = Property->GetClass(); - } - - if( ClassOfProperty==FByteProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Byte); - Enum = CastField(Property)->Enum; - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FEnumProperty::StaticClass() ) - { - FEnumProperty* EnumProp = CastField(Property); - FNumericProperty* UnderlyingProp = EnumProp->GetUnderlyingProperty(); - - *this = FPropertyBase( - UnderlyingProp->IsA() ? CPT_Int8 - : UnderlyingProp->IsA() ? CPT_Int16 - : UnderlyingProp->IsA() ? CPT_Int - : UnderlyingProp->IsA() ? CPT_Int64 - : UnderlyingProp->IsA() ? CPT_Byte - : UnderlyingProp->IsA() ? CPT_UInt16 - : UnderlyingProp->IsA() ? CPT_UInt32 - : UnderlyingProp->IsA() ? CPT_UInt64 - : CPT_None - ); - check(this->Type != CPT_None); - Enum = EnumProp->Enum; - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FInt8Property::StaticClass() ) - { - *this = FPropertyBase(CPT_Int8); - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FInt16Property::StaticClass() ) - { - *this = FPropertyBase(CPT_Int16); - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FIntProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Int); - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FInt64Property::StaticClass() ) - { - *this = FPropertyBase(CPT_Int64); - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FUInt16Property::StaticClass() ) - { - *this = FPropertyBase(CPT_UInt16); - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FUInt32Property::StaticClass() ) - { - *this = FPropertyBase(CPT_UInt32); - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FUInt64Property::StaticClass() ) - { - *this = FPropertyBase(CPT_UInt64); - IntType = EIntType::Sized; - } - else if( ClassOfProperty==FBoolProperty::StaticClass() ) - { - FBoolProperty* BoolProperty = CastField(Property); - if (BoolProperty->IsNativeBool()) - { - *this = FPropertyBase(CPT_Bool); - } - else - { - switch( BoolProperty->ElementSize ) - { - case sizeof(uint8): - *this = FPropertyBase(CPT_Bool8); - break; - case sizeof(uint16): - *this = FPropertyBase(CPT_Bool16); - break; - case sizeof(uint32): - *this = FPropertyBase(CPT_Bool32); - break; - case sizeof(uint64): - *this = FPropertyBase(CPT_Bool64); - break; - } - } - } - else if( ClassOfProperty==FFloatProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Float); - } - else if( ClassOfProperty==FDoubleProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Double); - } - else if( ClassOfProperty==FClassProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_ObjectReference); - PropertyClass = CastField(Property)->PropertyClass; - MetaClass = CastField(Property)->MetaClass; - } - else if( ClassOfProperty==FObjectProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_ObjectReference); - PropertyClass = CastField(Property)->PropertyClass; - } - else if( ClassOfProperty==FWeakObjectProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_WeakObjectReference); - PropertyClass = CastField(Property)->PropertyClass; - } - else if( ClassOfProperty==FLazyObjectProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_LazyObjectReference); - PropertyClass = CastField(Property)->PropertyClass; - } - else if( ClassOfProperty==FSoftClassProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_SoftObjectReference); - PropertyClass = CastField(Property)->PropertyClass; - MetaClass = CastField(Property)->MetaClass; - } - else if( ClassOfProperty==FSoftObjectProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_SoftObjectReference); - PropertyClass = CastField(Property)->PropertyClass; - } - else if( ClassOfProperty==FNameProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Name); - } - else if( ClassOfProperty==FStrProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_String); - } - else if( ClassOfProperty==FTextProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Text); - } - else if( ClassOfProperty==FStructProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Struct); - Struct = CastField(Property)->Struct; - } - else if( ClassOfProperty==FDelegateProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Delegate); - Function = CastField(Property)->SignatureFunction; - } - else if( ClassOfProperty==FMulticastDelegateProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_MulticastDelegate); - // @todo delegate: Any other setup for calling multi-cast delegates from script needed? - Function = CastField(Property)->SignatureFunction; - } - else if ( ClassOfProperty==FInterfaceProperty::StaticClass() ) - { - *this = FPropertyBase(CPT_Interface); - PropertyClass = CastField(Property)->InterfaceClass; - } - else if (ClassOfProperty == FFieldPathProperty::StaticClass()) - { - *this = FPropertyBase(CPT_FieldPath); - PropertyPathClass = CastField(Property)->PropertyClass; - } - else - { - UE_LOG(LogCompile, Fatal, TEXT("Unknown property type '%s'"), *Property->GetFullName() ); - } - ArrayType = ArrType; - PropertyFlags = Property->PropertyFlags | PropagateFlags; - ImpliedPropertyFlags = CPF_None; - RefQualifier = ERefQualifier::None; - PointerType = EPointerType::None; - } - - explicit FPropertyBase(FFieldClass* InPropertyClass, EPropertyType InType) - : Type(InType) - , ArrayType(EArrayType::None) - , PropertyFlags(CPF_None) - , ImpliedPropertyFlags(CPF_None) - , RefQualifier(ERefQualifier::None) - , PropertyExportFlags(PROPEXPORT_Public) - , PropertyPathClass(InPropertyClass) - , MetaClass(nullptr) - , DelegateName(NAME_None) - , DelegateSignatureOwnerClass(nullptr) - , RepNotifyName(NAME_None) - , PointerType(EPointerType::None) - , IntType(GetSizedIntTypeFromPropertyType(InType)) - { - } - //@} - - /** @name Functions */ - //@{ - - /** - * Returns whether this token represents an object reference - */ - bool IsObject() const - { - return Type == CPT_ObjectReference || Type == CPT_Interface || Type == CPT_WeakObjectReference || Type == CPT_LazyObjectReference || Type == CPT_SoftObjectReference; - } - - bool IsContainer() const - { - return (ArrayType != EArrayType::None || MapKeyProp.IsValid()); - } - - /** - * Determines whether this token's type is compatible with another token's type. - * - * @param Other the token to check against this one. - * Given the following example expressions, VarA is Other and VarB is 'this': - * VarA = VarB; - * - * function func(type VarB) {} - * func(VarA); - * - * static operator==(type VarB_1, type VarB_2) {} - * if ( VarA_1 == VarA_2 ) {} - * - * @param bDisallowGeneralization controls whether it should be considered a match if this token's type is a generalization - * of the other token's type (or vice versa, when dealing with structs - * @param bIgnoreImplementedInterfaces controls whether two types can be considered a match if one type is an interface implemented - * by the other type. - */ - bool MatchesType( const FPropertyBase& Other, bool bDisallowGeneralization, bool bIgnoreImplementedInterfaces=false ) const - { - check(Type!=CPT_None || !bDisallowGeneralization); - - bool bIsObjectType = IsObject(); - bool bOtherIsObjectType = Other.IsObject(); - bool bIsObjectComparison = bIsObjectType && bOtherIsObjectType; - bool bReverseClassChainCheck = true; - - // If converting to an l-value, we require an exact match with an l-value. - if( (PropertyFlags&CPF_OutParm) != 0 ) - { - // if the other type is not an l-value, disallow - if ( (Other.PropertyFlags&CPF_OutParm) == 0 ) - { - return false; - } - - // if the other type is const and we are not const, disallow - if ( (Other.PropertyFlags&CPF_ConstParm) != 0 && (PropertyFlags&CPF_ConstParm) == 0 ) - { - return false; - } - - if ( Type == CPT_Struct ) - { - // Allow derived structs to be passed by reference, unless this is a dynamic array of structs - bDisallowGeneralization = bDisallowGeneralization || ArrayType == EArrayType::Dynamic || Other.ArrayType == EArrayType::Dynamic; - } - - // if Type == CPT_ObjectReference, out object function parm; allow derived classes to be passed in - // if Type == CPT_Interface, out interface function parm; allow derived classes to be passed in - else if ( (PropertyFlags & CPF_ConstParm) == 0 || !IsObject() ) - { - // all other variable types must match exactly when passed as the value to an 'out' parameter - bDisallowGeneralization = true; - } - - // both types are objects, but one is an interface and one is an object reference - else if ( bIsObjectComparison && Type != Other.Type ) - { - return false; - } - } - else if ((Type == CPT_ObjectReference || Type == CPT_WeakObjectReference || Type == CPT_LazyObjectReference || Type == CPT_SoftObjectReference) && Other.Type != CPT_Interface && (PropertyFlags & CPF_ReturnParm)) - { - bReverseClassChainCheck = false; - } - - // Check everything. - if( Type==CPT_None && (Other.Type==CPT_None || !bDisallowGeneralization) ) - { - // If Other has no type, accept anything. - return true; - } - else if( Type != Other.Type && !bIsObjectComparison ) - { - // Mismatched base types. - return false; - } - else if( ArrayType != Other.ArrayType ) - { - // Mismatched array types. - return false; - } - else if( Type==CPT_Byte ) - { - // Make sure enums match, or we're generalizing. - return Enum==Other.Enum || (Enum==NULL && !bDisallowGeneralization); - } - else if( bIsObjectType ) - { - check(PropertyClass!=NULL); - - // Make sure object types match, or we're generalizing. - if( bDisallowGeneralization ) - { - // Exact match required. - return PropertyClass==Other.PropertyClass && MetaClass==Other.MetaClass; - } - else if( Other.PropertyClass==NULL ) - { - // Cannonical 'None' matches all object classes. - return true; - } - else - { - // Generalization is ok (typical example of this check would look like: VarA = VarB;, where this is VarB and Other is VarA) - if ( Other.PropertyClass->IsChildOf(PropertyClass) ) - { - if ( !bIgnoreImplementedInterfaces || ((Type == CPT_Interface) == (Other.Type == CPT_Interface)) ) - { - if ( !PropertyClass->IsChildOf(UClass::StaticClass()) || MetaClass == NULL || Other.MetaClass->IsChildOf(MetaClass) || - (bReverseClassChainCheck && (Other.MetaClass == NULL || MetaClass->IsChildOf(Other.MetaClass))) ) - { - return true; - } - } - } - // check the opposite class chain for object types - else if (bReverseClassChainCheck && Type != CPT_Interface && bIsObjectComparison && PropertyClass != NULL && PropertyClass->IsChildOf(Other.PropertyClass)) - { - if (!Other.PropertyClass->IsChildOf(UClass::StaticClass()) || MetaClass == NULL || Other.MetaClass == NULL || MetaClass->IsChildOf(Other.MetaClass) || Other.MetaClass->IsChildOf(MetaClass)) - { - return true; - } - } - - if ( PropertyClass->HasAnyClassFlags(CLASS_Interface) && !bIgnoreImplementedInterfaces ) - { - if ( Other.PropertyClass->ImplementsInterface(PropertyClass) ) - { - return true; - } - } - - return false; - } - } - else if( Type==CPT_Struct ) - { - check(Struct!=NULL); - check(Other.Struct!=NULL); - - if ( Struct == Other.Struct ) - { - // struct types match exactly - return true; - } - - // returning false here prevents structs related through inheritance from being used interchangeably, such as passing a derived struct as the value for a parameter - // that expects the base struct, or vice versa. An easier example is assignment (e.g. Vector = Plane or Plane = Vector). - // there are two cases to consider (let's use vector and plane for the example): - // - Vector = Plane; - // in this expression, 'this' is the vector, and Other is the plane. This is an unsafe conversion, as the destination property type is used to copy the r-value to the l-value - // so in this case, the VM would call CopyCompleteValue on the FPlane struct, which would copy 16 bytes into the l-value's buffer; However, the l-value buffer will only be - // 12 bytes because that is the size of FVector - // - Plane = Vector; - // in this expression, 'this' is the plane, and Other is the vector. This is a safe conversion, since only 12 bytes would be copied from the r-value into the l-value's buffer - // (which would be 16 bytes). The problem with allowing this conversion is that what to do with the extra member (e.g. Plane.W); should it be left alone? should it be zeroed? - // difficult to say what the correct behavior should be, so let's just ignore inheritance for the sake of determining whether two structs are identical - - // Previously, the logic for determining whether this is a generalization of Other was reversed; this is very likely the culprit behind all current issues with - // using derived structs interchangeably with their base versions. The inheritance check has been fixed; for now, allow struct generalization and see if we can find any further - // issues with allowing conversion. If so, then we disable all struct generalization by returning false here. - // return false; - - if ( bDisallowGeneralization ) - { - return false; - } - - // Generalization is ok if this is not a dynamic array - if ( ArrayType != EArrayType::Dynamic && Other.ArrayType != EArrayType::Dynamic ) - { - if ( !Other.Struct->IsChildOf(Struct) && Struct->IsChildOf(Other.Struct) ) - { - return true; - } - } - - return false; - } - else - { - // General match. - return true; - } - } - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - FString Describe() - { - return FString::Printf( - TEXT("Type:%s Flags:%lli ImpliedFlags:%lli Enum:%s PropertyClass:%s Struct:%s Function:%s MetaClass:%s"), - GetPropertyTypeText(Type), PropertyFlags, ImpliedPropertyFlags, - Enum!=NULL?*Enum->GetName():TEXT(""), - PropertyClass!=NULL?*PropertyClass->GetName():TEXT("NULL"), - Struct!=NULL?*Struct->GetName():TEXT("NULL"), - Function!=NULL?*Function->GetName():TEXT("NULL"), - MetaClass!=NULL?*MetaClass->GetName():TEXT("NULL") - ); - } - */ - - //@} - - EIntType GetSizedIntTypeFromPropertyType(EPropertyType PropType) - { - switch (PropType) - { - case CPT_Byte: - case CPT_UInt16: - case CPT_UInt32: - case CPT_UInt64: - case CPT_Int8: - case CPT_Int16: - case CPT_Int: - case CPT_Int64: - return EIntType::Sized; - - default: - return EIntType::None; - } - } - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - static const TCHAR* GetPropertyTypeText( EPropertyType Type ); - */ - - friend struct FPropertyBaseArchiveProxy; -}; - -// -// Token types. -// -enum ETokenType -{ - TOKEN_None = 0x00, // No token. - TOKEN_Identifier = 0x01, // Alphanumeric identifier. - TOKEN_Symbol = 0x02, // Symbol. - TOKEN_Const = 0x03, // A constant. - TOKEN_Max = 0x0D -}; - -/*----------------------------------------------------------------------------- - FToken. ------------------------------------------------------------------------------*/ -// -// Information about a token that was just parsed. -// -class FToken : public FPropertyBase -{ -public: - /** @name Variables */ - //@{ - /** Type of token. */ - ETokenType TokenType; - -private: - /** Whether TokenName has been looked up */ - mutable bool bTokenNameInitialized; - /** Name of token, lazily initialized */ - mutable FName TokenName; - -public: - /** Starting position in script where this token came from. */ - int32 StartPos; - /** Starting line in script. */ - int32 StartLine; - /** Always valid. */ - TCHAR Identifier[NAME_SIZE]; - /** property that corresponds to this FToken - null if this Token doesn't correspond to a FProperty */ - FProperty* TokenProperty; - - -public: - - union - { - // TOKEN_Const values. - uint8 Byte; // If CPT_Byte. - int64 Int64; // If CPT_Int64. - int32 Int; // If CPT_Int. - bool NativeBool; // if CPT_Bool - float Float; // If CPT_Float. - double Double; // If CPT_Double. - uint8 NameBytes[sizeof(FName)]; // If CPT_Name. - TCHAR String[MAX_STRING_CONST_SIZE]; // If CPT_String - }; - //@} - - FToken& operator=(const FToken& Other); - FToken& operator=(FToken&& Other); - - FString GetConstantValue() const - { - if (TokenType == TOKEN_Const) - { - switch (Type) - { - case CPT_Byte: - return FString::Printf(TEXT("%u"), Byte); - case CPT_Int64: - return FString::Printf(TEXT("%" INT64_FMT), Int64); - case CPT_Int: - return FString::Printf(TEXT("%i"), Int); - case CPT_Bool: - // Don't use FCoreTexts::True/FCoreTexts::False here because they can be localized - return FString::Printf(TEXT("%s"), NativeBool ? *(FName::GetEntry(NAME_TRUE)->GetPlainNameString()) : *(FName::GetEntry(NAME_FALSE)->GetPlainNameString())); - case CPT_Float: - return FString::Printf(TEXT("%f"), Float); - case CPT_Double: - return FString::Printf(TEXT("%f"), Double); - case CPT_Name: - return FString::Printf(TEXT("%s"), *(*(FName*)NameBytes).ToString()); - case CPT_String: - return String; - - // unsupported (parsing never produces a constant token of these types) - default: - return TEXT("InvalidTypeForAToken"); - } - } - else - { - return TEXT("NotConstant"); - } - } - - // Constructors. - FToken() - : FPropertyBase(CPT_None) - , TokenProperty(NULL) - { - InitToken(CPT_None); - } - - // copy constructors - explicit FToken(EPropertyType InType) - : FPropertyBase(InType) - , TokenProperty(NULL) - { - InitToken(InType); - } - - // copy constructors - FToken(const FPropertyBase& InType) - : FPropertyBase(CPT_None) - , TokenProperty(NULL) - { - InitToken( CPT_None ); - (FPropertyBase&)*this = InType; - } - - FToken(const FToken& InToken) - : FPropertyBase(CPT_None) - { - *this = InToken; - } - - FToken(FToken&& InToken) - : FPropertyBase(CPT_None) - { - *this = MoveTemp(InToken); - } - - // Inlines. - void InitToken( EPropertyType InType ) - { - (FPropertyBase&)*this = FPropertyBase(InType); - TokenType = TOKEN_None; - TokenName = NAME_None; - StartPos = 0; - StartLine = 0; - *Identifier = 0; - FMemory::Memzero(String); - } - bool Matches(const TCHAR Ch) const - { - return TokenType==TOKEN_Symbol && Identifier[0] == Ch && Identifier[1] == 0; - } - bool Matches( const TCHAR* Str, ESearchCase::Type SearchCase ) const - { - return (TokenType==TOKEN_Identifier || TokenType==TOKEN_Symbol) && ((SearchCase == ESearchCase::CaseSensitive) ? !FCString::Strcmp(Identifier, Str) : !FCString::Stricmp(Identifier, Str)); - } - bool IsBool() const - { - return Type == CPT_Bool || Type == CPT_Bool8 || Type == CPT_Bool16 || Type == CPT_Bool32 || Type == CPT_Bool64; - } - FName GetTokenName() const - { - if (!bTokenNameInitialized) - { - TokenName = FName(Identifier, FNAME_Find); - bTokenNameInitialized = true; - } - return TokenName; - } - void ClearTokenName() - { - bTokenNameInitialized = false; - TokenName = NAME_None; - } - - // Setters. - - void SetIdentifier( const TCHAR* InString) - { - InitToken(CPT_None); - TokenType = TOKEN_Identifier; - FCString::Strncpy(Identifier, InString, NAME_SIZE); - bTokenNameInitialized = false; - } - - void SetConstInt64( int64 InInt64 ) - { - (FPropertyBase&)*this = FPropertyBase(CPT_Int64); - Int64 = InInt64; - TokenType = TOKEN_Const; - } - void SetConstInt( int32 InInt ) - { - (FPropertyBase&)*this = FPropertyBase(CPT_Int); - Int = InInt; - TokenType = TOKEN_Const; - } - void SetConstBool( bool InBool ) - { - (FPropertyBase&)*this = FPropertyBase(CPT_Bool); - NativeBool = InBool; - TokenType = TOKEN_Const; - } - void SetConstFloat( float InFloat ) - { - (FPropertyBase&)*this = FPropertyBase(CPT_Float); - Float = InFloat; - TokenType = TOKEN_Const; - } - void SetConstDouble( double InDouble ) - { - (FPropertyBase&)*this = FPropertyBase(CPT_Double); - Double = InDouble; - TokenType = TOKEN_Const; - } - void SetConstName( FName InName ) - { - (FPropertyBase&)*this = FPropertyBase(CPT_Name); - *(FName *)NameBytes = InName; - TokenType = TOKEN_Const; - } - void SetConstString( const TCHAR* InString, int32 MaxLength=MAX_STRING_CONST_SIZE ) - { - check(MaxLength>0); - (FPropertyBase&)*this = FPropertyBase(CPT_String); - if( InString != String ) - { - FCString::Strncpy( String, InString, MaxLength ); - } - TokenType = TOKEN_Const; - } - void SetConstChar(TCHAR InChar) - { - //@TODO: Treating this like a string for now, nothing consumes it - (FPropertyBase&)*this = FPropertyBase(CPT_String); - String[0] = InChar; - String[1] = 0; - TokenType = TOKEN_Const; - } - //!!struct constants - - // Getters. - bool GetConstInt( int32& I ) const - { - if( TokenType==TOKEN_Const && Type==CPT_Int64 ) - { - I = (int32)Int64; - return 1; - } - else if( TokenType==TOKEN_Const && Type==CPT_Int ) - { - I = Int; - return 1; - } - else if( TokenType==TOKEN_Const && Type==CPT_Byte ) - { - I = Byte; - return 1; - } - else if( TokenType==TOKEN_Const && Type==CPT_Float && Float==FMath::TruncToInt(Float)) - { - I = (int32) Float; - return 1; - } - else if (TokenType == TOKEN_Const && Type == CPT_Double && Double == FMath::TruncToInt((float)Double)) - { - I = (int32) Double; - return 1; - } - else return 0; - } - - bool GetConstInt64( int64& I ) const - { - if( TokenType==TOKEN_Const && Type==CPT_Int64 ) - { - I = Int64; - return 1; - } - else if( TokenType==TOKEN_Const && Type==CPT_Int ) - { - I = Int; - return 1; - } - else if( TokenType==TOKEN_Const && Type==CPT_Byte ) - { - I = Byte; - return 1; - } - else if( TokenType==TOKEN_Const && Type==CPT_Float && Float==FMath::TruncToInt(Float)) - { - I = (int32) Float; - return 1; - } - else if (TokenType == TOKEN_Const && Type == CPT_Double && Double == FMath::TruncToInt((float)Double)) - { - I = (int32) Double; - return 1; - } - else return 0; - } - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - FString Describe() - { - return FString::Printf( - TEXT("Property:%s Type:%s TokenName:%s ConstValue:%s Struct:%s Flags:%lli Implied:%lli"), - TokenProperty!=NULL?*TokenProperty->GetName():TEXT("NULL"), - GetPropertyTypeText(Type), *GetTokenName().ToString(), *GetConstantValue(), - Struct!=NULL?*Struct->GetName():TEXT("NULL"), - PropertyFlags, - ImpliedPropertyFlags - ); - } - */ - - friend struct FTokenArchiveProxy; -}; - -/** - * Information about a function being compiled. - */ -struct FFuncInfo -{ - /** @name Variables */ - //@{ - /** Name of the function or operator. */ - FToken Function; - /** Function flags. */ - EFunctionFlags FunctionFlags; - /** Function flags which are only required for exporting */ - uint32 FunctionExportFlags; - /** Number of parameters expected for operator. */ - int32 ExpectParms; - /** Pointer to the UFunction corresponding to this FFuncInfo */ - UFunction* FunctionReference; - /** Name of the wrapper function that marshalls the arguments and does the indirect call **/ - FString MarshallAndCallName; - /** Name of the actual implementation **/ - FString CppImplName; - /** Name of the actual validation implementation **/ - FString CppValidationImplName; - /** Name for callback-style names **/ - FString UnMarshallAndCallName; - /** Endpoint name */ - FString EndpointName; - /** Identifier for an RPC call to a platform service */ - uint16 RPCId; - /** Identifier for an RPC call expecting a response */ - uint16 RPCResponseId; - /** Delegate macro line in header. */ - int32 MacroLine; - /** Position in file where this function was declared. Points to first char of function name. */ - int32 InputPos; - /** Whether this function represents a sealed event */ - bool bSealedEvent; - /** TRUE if the function is being forced to be considered as impure by the user */ - bool bForceBlueprintImpure; - - //@} - - /** Constructor. */ - FFuncInfo() - : Function() - , FunctionFlags(FUNC_None) - , FunctionExportFlags(0) - , ExpectParms(0) - , FunctionReference(nullptr) - , RPCId(0) - , RPCResponseId(0) - , MacroLine(-1) - , InputPos(-1) - , bSealedEvent(false) - , bForceBlueprintImpure(false) - {} - - FFuncInfo(const FFuncInfo& Other) = default; - FFuncInfo(FFuncInfo&& Other) = default; - FFuncInfo& operator=(FFuncInfo&& Other) = default; - - /** Set the internal function names based on flags **/ - void SetFunctionNames() - { - FString FunctionName = FunctionReference->GetName(); - if( FunctionReference->HasAnyFunctionFlags( FUNC_Delegate ) ) - { - FunctionName.LeftChopInline( FString(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX).Len(), false ); - } - UnMarshallAndCallName = FString(TEXT("exec")) + FunctionName; - - if (FunctionReference->HasAnyFunctionFlags(FUNC_BlueprintEvent)) - { - MarshallAndCallName = FunctionName; - } - else - { - MarshallAndCallName = FString(TEXT("event")) + FunctionName; - } - - if (FunctionReference->HasAllFunctionFlags(FUNC_Native | FUNC_Net)) - { - MarshallAndCallName = FunctionName; - if (FunctionReference->HasAllFunctionFlags(FUNC_NetResponse)) - { - // Response function implemented by programmer and called directly from thunk - CppImplName = FunctionReference->GetName(); - } - else - { - if (CppImplName.IsEmpty()) - { - CppImplName = FunctionReference->GetName() + TEXT("_Implementation"); - } - else if (CppImplName == FunctionName) - { - FError::Throwf(TEXT("Native implementation function must be different than original function name.")); - } - - if (CppValidationImplName.IsEmpty() && FunctionReference->HasAllFunctionFlags(FUNC_NetValidate)) - { - CppValidationImplName = FunctionReference->GetName() + TEXT("_Validate"); - } - else if(CppValidationImplName == FunctionName) - { - FError::Throwf(TEXT("Validation function must be different than original function name.")); - } - } - } - - if (FunctionReference->HasAllFunctionFlags(FUNC_Delegate)) - { - MarshallAndCallName = FString(TEXT("delegate")) + FunctionName; - } - - if (FunctionReference->HasAllFunctionFlags(FUNC_BlueprintEvent | FUNC_Native)) - { - MarshallAndCallName = FunctionName; - CppImplName = FunctionReference->GetName() + TEXT("_Implementation"); - } - - if (CppImplName.IsEmpty()) - { - CppImplName = FunctionName; - } - } -}; - -/** - * Stores "compiler" data about an FToken. "Compiler" data is data that is associated with a - * specific property, function or class that is only needed during script compile. - * This class is designed to make adding new compiler data very simple. - * - * - stores the raw evaluated bytecode associated with an FToken - */ -struct FTokenData -{ - /** The token tracked by this FTokenData. */ - FToken Token; - - FTokenData() = default; - FTokenData(FTokenData&&) = default; - FTokenData& operator=(FTokenData&&) = default; - FTokenData(FToken&& InToken) - : Token(MoveTemp(InToken)) - { - } -}; - -/** - * Class for storing data about a list of properties. Though FToken contains a reference to its - * associated FProperty, it's faster lookup to use the FProperty as the key in a TMap. - */ -class FPropertyData : public TMap< FProperty*, TSharedPtr > -{ - typedef TMap > Super; - -public: - /** - * Returns the value associated with a specified key. - * @param Key - The key to search for. - * @return A pointer to the value associated with the specified key, or NULL if the key isn't contained in this map. The pointer - * is only valid until the next change to any key in the map. - */ - FTokenData* Find(FProperty* Key) - { - FTokenData* Result = NULL; - - TSharedPtr* pResult = Super::Find(Key); - if ( pResult != NULL ) - { - Result = pResult->Get(); - } - return Result; - } - const FTokenData* Find(FProperty* Key) const - { - const FTokenData* Result = NULL; - - const TSharedPtr* pResult = Super::Find(Key); - if ( pResult != NULL ) - { - Result = pResult->Get(); - } - return Result; - } - - /** - * Sets the value associated with a key. If the key already exists in the map, uses the same - * value pointer and reinitalized the FTokenData with the input value. - * - * @param InKey the property to get a token wrapper for - * @param InValue the token wrapper for the specified key - * - * @return a pointer to token data created associated with the property - */ - FTokenData* Set(FProperty* InKey, FTokenData&& InValue, FUnrealSourceFile* UnrealSourceFile); - - /** - * (debug) Dumps the values of this FPropertyData to the log file - * - * @param Indent number of spaces to insert at the beginning of each line - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - void Dump( int32 Indent ) - { - for (auto& Kvp : *this) - { - TSharedPtr& PointerVal = Kvp.Value; - FToken& Token = PointerVal->Token; - if ( Token.Type != CPT_None ) - { - UE_LOG(LogCompile, Log, TEXT("%s%s"), FCString::Spc(Indent), *Token.Describe()); - } - } - } - */ -}; - -/** - * Class for storing additional data about compiled structs and struct properties - */ -class FStructData -{ -public: - /** info about the struct itself */ - FToken StructData; - -private: - /** info for the properties contained in this struct */ - FPropertyData StructPropertyData; - -public: - /** - * Adds a new struct property token - * - * @param PropertyToken token that should be added to the list - */ - void AddStructProperty(FTokenData&& PropertyToken, FUnrealSourceFile* UnrealSourceFile) - { - check(PropertyToken.Token.TokenProperty); - StructPropertyData.Set(PropertyToken.Token.TokenProperty, MoveTemp(PropertyToken), UnrealSourceFile); - } - - FPropertyData& GetStructPropertyData() - { - return StructPropertyData; - } - const FPropertyData& GetStructPropertyData() const - { - return StructPropertyData; - } - - /** - * (debug) Dumps the values of this FStructData to the log file - * - * @param Indent number of spaces to insert at the beginning of each line - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - void Dump( int32 Indent ) - { - UE_LOG(LogCompile, Log, TEXT("%s%s"), FCString::Spc(Indent), *StructData.Describe()); - - UE_LOG(LogCompile, Log, TEXT("%sproperties:"), FCString::Spc(Indent)); - StructPropertyData.Dump(Indent + 4); - } - */ - - /** Constructor */ - FStructData(FToken&& StructToken) : StructData(MoveTemp(StructToken)) {} - - friend struct FStructDataArchiveProxy; -}; - -/** - * Class for storing additional data about compiled function properties. - */ -class FFunctionData -{ - /** info about the function associated with this FFunctionData */ - FFuncInfo FunctionData; - - /** return value for this function */ - FTokenData ReturnTypeData; - - /** function parameter data */ - FPropertyData ParameterData; - - /** - * Adds a new parameter token - * - * @param PropertyToken token that should be added to the list - */ - void AddParameter(FToken&& PropertyToken, FUnrealSourceFile* UnrealSourceFile) - { - check(PropertyToken.TokenProperty); - ParameterData.Set(PropertyToken.TokenProperty, MoveTemp(PropertyToken), UnrealSourceFile); - } - - /** - * Sets the value of the return token for this function - * - * @param PropertyToken token that should be added - */ - void SetReturnData(FToken&& PropertyToken ) - { - check(PropertyToken.TokenProperty); - ReturnTypeData.Token = MoveTemp(PropertyToken); - } - -public: - /** Constructors */ - FFunctionData() = default; - FFunctionData(FFunctionData&& Other) = default; - FFunctionData(FFuncInfo&& inFunctionData ) - : FunctionData(MoveTemp(inFunctionData)) - {} - - FFunctionData& operator=(FFunctionData&& Other) = default; - - /** @name getters */ - //@{ - const FFuncInfo& GetFunctionData() const { return FunctionData; } - const FToken& GetReturnData() const { return ReturnTypeData.Token; } - const FPropertyData& GetParameterData() const { return ParameterData; } - FPropertyData& GetParameterData() { return ParameterData; } - FTokenData* GetReturnTokenData() { return &ReturnTypeData; } - //@} - - void UpdateFunctionData(FFuncInfo& UpdatedFuncData) - { - //@TODO: UCREMOVAL: Some more thorough evaluation should be done here - FunctionData.FunctionFlags |= UpdatedFuncData.FunctionFlags; - FunctionData.FunctionExportFlags |= UpdatedFuncData.FunctionExportFlags; - } - - /** - * Adds a new function property to be tracked. Determines whether the property is a - * function parameter, local property, or return value, and adds it to the appropriate - * list - * - * @param PropertyToken the property to add - */ - void AddProperty(FToken&& PropertyToken, FUnrealSourceFile* UnrealSourceFile) - { - const FProperty* Prop = PropertyToken.TokenProperty; - check(Prop); - check( (Prop->PropertyFlags&CPF_Parm) != 0 ); - - if ( (Prop->PropertyFlags&CPF_ReturnParm) != 0 ) - { - SetReturnData(MoveTemp(PropertyToken)); - } - else - { - AddParameter(MoveTemp(PropertyToken), UnrealSourceFile); - } - } - - /** - * (debug) Dumps the values of this FFunctionData to the log file - * - * @param Indent number of spaces to insert at the beginning of each line - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - void Dump( int32 Indent ) - { - UE_LOG(LogCompile, Log, TEXT("%sparameters:"), FCString::Spc(Indent)); - ParameterData.Dump(Indent + 4); - - UE_LOG(LogCompile, Log, TEXT("%sreturn prop:"), FCString::Spc(Indent)); - if ( ReturnTypeData.Token.Type != CPT_None ) - { - UE_LOG(LogCompile, Log, TEXT("%s%s"), FCString::Spc(Indent + 4), *ReturnTypeData.Token.Describe()); - } - } - */ - - /** - * Sets the specified function export flags - */ - void SetFunctionExportFlag( uint32 NewFlags ) - { - FunctionData.FunctionExportFlags |= NewFlags; - } - - /** - * Clears the specified function export flags - */ - void ClearFunctionExportFlags( uint32 ClearFlags ) - { - FunctionData.FunctionExportFlags &= ~ClearFlags; - } - - /** - * Finds function data for given function object. - */ - static FFunctionData* FindForFunction(UFunction* Function); - - /** - * Adds function data object for given function object. - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - static FFunctionData* Add(UFunction* Function); - */ - - /** - * Adds function data object for given function object. - */ - static FFunctionData* Add(FFuncInfo&& FunctionInfo); - - /** - * Tries to find function data for given function object. - */ - static bool TryFindForFunction(UFunction* Function, FFunctionData*& OutData); - -private: - static TMap > FunctionDataMap; -}; - -/** - * Tracks information about a multiple inheritance parent declaration for native script classes. - */ -struct FMultipleInheritanceBaseClass -{ - /** - * The name to use for the base class when exporting the script class to header file. - */ - FString ClassName; - - /** - * For multiple inheritance parents declared using 'Implements', corresponds to the UClass for the interface. For multiple inheritance parents declared - * using 'Inherits', this value will be NULL. - */ - UClass* InterfaceClass = nullptr; - - /** - * Constructors - */ - explicit FMultipleInheritanceBaseClass(FString&& BaseClassName) - : ClassName(MoveTemp(BaseClassName)) - { - } - - explicit FMultipleInheritanceBaseClass(UClass* ImplementedInterfaceClass) - : InterfaceClass(ImplementedInterfaceClass) - { - ClassName = FString::Printf(TEXT("I%s"), *ImplementedInterfaceClass->GetName()); - } -}; - -enum class EParsedInterface -{ - NotAnInterface, - ParsedUInterface, - ParsedIInterface -}; - -/** - * Class for storing compiler metadata about a class's properties. - */ -class FClassMetaData -{ - /** member properties for this class */ - FPropertyData GlobalPropertyData; - - /** base classes to multiply inherit from (other than the main base class */ - TArray MultipleInheritanceParents; - - /** whether this class declares delegate functions or properties */ - bool bContainsDelegates; - - /** The line of UCLASS/UINTERFACE macro in this class. */ - int32 PrologLine; - - /** The line of GENERATED_BODY/GENERATED_UCLASS_BODY macro in this class. */ - int32 GeneratedBodyLine; - - /** Same as above, but for interface class associated with this class. */ - int32 InterfaceGeneratedBodyLine; - -public: - /** Default constructor */ - FClassMetaData() - : bContainsDelegates(false) - , PrologLine(-1) - , GeneratedBodyLine(-1) - , InterfaceGeneratedBodyLine(-1) - , bConstructorDeclared(false) - , bDefaultConstructorDeclared(false) - , bObjectInitializerConstructorDeclared(false) - , bCustomVTableHelperConstructorDeclared(false) - , GeneratedBodyMacroAccessSpecifier(ACCESS_NotAnAccessSpecifier) - { - } - - /** - * Gets prolog line number for this class. - */ - int32 GetPrologLine() const - { - check(PrologLine > 0); - return PrologLine; - } - - /** - * Gets generated body line number for this class. - */ - int32 GetGeneratedBodyLine() const - { - check(GeneratedBodyLine > 0); - return GeneratedBodyLine; - } - - /** - * Gets interface generated body line number for this class. - */ - int32 GetInterfaceGeneratedBodyLine() const - { - check(InterfaceGeneratedBodyLine > 0); - return InterfaceGeneratedBodyLine; - } - - /** - * Sets prolog line number for this class. - */ - void SetPrologLine(int32 Line) - { - check(Line > 0); - PrologLine = Line; - } - - /** - * Sets generated body line number for this class. - */ - void SetGeneratedBodyLine(int32 Line) - { - check(Line > 0); - GeneratedBodyLine = Line; - } - - /** - * Sets interface generated body line number for this class. - */ - void SetInterfaceGeneratedBodyLine(int32 Line) - { - check(Line > 0); - InterfaceGeneratedBodyLine = Line; - } - - /** - * Sets contains delegates flag for this class. - */ - void MarkContainsDelegate() - { - bContainsDelegates = true; - } - - /** - * Adds a new property to be tracked. Determines the correct list for the property based on - * its owner (function, struct, etc). - * - * @param PropertyToken the property to add - */ - void AddProperty(FToken&& PropertyToken, FUnrealSourceFile* UnrealSourceFile) - { - FProperty* Prop = PropertyToken.TokenProperty; - check(Prop); - - UObject* Outer = Prop->GetOwner(); - check(Outer); - UStruct* OuterClass = Cast(Outer); - if ( OuterClass != NULL ) - { - // global property - GlobalPropertyData.Set(Prop, MoveTemp(PropertyToken), UnrealSourceFile); - } - else - { - checkNoEntry(); - UFunction* OuterFunction = Cast(Outer); - if ( OuterFunction != NULL ) - { - // function parameter, return, or local property - FFunctionData::FindForFunction(OuterFunction)->AddProperty(MoveTemp(PropertyToken), UnrealSourceFile); - } - } - - // update the optimization flags - if ( !bContainsDelegates ) - { - if( Prop->IsA( FDelegateProperty::StaticClass() ) || Prop->IsA( FMulticastDelegateProperty::StaticClass() ) ) - { - bContainsDelegates = true; - } - else - { - FArrayProperty* ArrayProp = CastField(Prop); - if ( ArrayProp != NULL ) - { - if( ArrayProp->Inner->IsA( FDelegateProperty::StaticClass() ) || ArrayProp->Inner->IsA( FMulticastDelegateProperty::StaticClass() ) ) - { - bContainsDelegates = true; - } - } - } - } - } - - /** - * Adds new editor-only metadata (key/value pairs) to the class or struct that - * owns this property or function. - * - * @param Field the property or function to add to - * @param InMetaData the metadata to add - */ - static void AddMetaData(UField* Field, TMap&& InMetaData) - { - // only add if we have some! - if (InMetaData.Num()) - { - check(Field); - - // get (or create) a metadata object for this package - UMetaData* MetaData = Field->GetOutermost()->GetMetaData(); - TMap* ExistingMetaData = MetaData->GetMapForObject(Field); - if (ExistingMetaData && ExistingMetaData->Num()) - { - // Merge the existing metadata - TMap MergedMetaData; - MergedMetaData.Reserve(InMetaData.Num() + ExistingMetaData->Num()); - MergedMetaData.Append(*ExistingMetaData); - MergedMetaData.Append(InMetaData); - MetaData->SetObjectValues(Field, MoveTemp(MergedMetaData)); - } - else - { - // set the metadata for this field - MetaData->SetObjectValues(Field, MoveTemp(InMetaData)); - } - } - } - static void AddMetaData(FField* Field, TMap&& InMetaData) - { - // only add if we have some! - if (InMetaData.Num()) - { - check(Field); - - UPackage* Package = Field->GetOutermost(); - // get (or create) a metadata object for this package - UMetaData* MetaData = Package->GetMetaData(); - - for (TPair& MetaKeyValue : InMetaData) - { - Field->SetMetaData(MetaKeyValue.Key, MoveTemp(MetaKeyValue.Value)); - } - } - } - - /** - * Finds the metadata for the function specified - * - * @param Func the function to search for - * - * @return pointer to the metadata for the function specified, or NULL - * if the function doesn't exist in the list (for example, if it - * is declared in a package that is already compiled and has had its - * source stripped) - */ - FFunctionData* FindFunctionData( UFunction* Func ); - - /** - * Finds the metadata for the property specified - * - * @param Prop the property to search for - * - * @return pointer to the metadata for the property specified, or NULL - * if the property doesn't exist in the list (for example, if it - * is declared in a package that is already compiled and has had its - * source stripped) - */ - FTokenData* FindTokenData( FProperty* Prop ); - - /** - * (debug) Dumps the values of this FFunctionData to the log file - * - * @param Indent number of spaces to insert at the beginning of each line - */ - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - void Dump( int32 Indent ); - */ - - /** - * Add a string to the list of inheritance parents for this class. - * - * @param Inparent The C++ class name to add to the multiple inheritance list - * @param UnrealSourceFile Currently parsed source file. - */ - void AddInheritanceParent(FString&& InParent, FUnrealSourceFile* UnrealSourceFile); - - /** - * Add a string to the list of inheritance parents for this class. - * - * @param Inparent The C++ class name to add to the multiple inheritance list - * @param UnrealSourceFile Currently parsed source file. - */ - void AddInheritanceParent(UClass* ImplementedInterfaceClass, FUnrealSourceFile* UnrealSourceFile); - - /** - * Return the list of inheritance parents - */ - const TArray& GetInheritanceParents() const - { - return MultipleInheritanceParents; - } - - /** - * Returns whether this class contains any delegate properties which need to be fixed up. - */ - bool ContainsDelegates() const - { - return bContainsDelegates; - } - - /** - * Shrink TMaps to avoid slack in Pairs array. - */ - void Shrink() - { - GlobalPropertyData.Shrink(); - MultipleInheritanceParents.Shrink(); - } - - // Is constructor declared? - bool bConstructorDeclared; - - // Is default constructor declared? - bool bDefaultConstructorDeclared; - - // Is ObjectInitializer constructor (i.e. a constructor with only one parameter of type FObjectInitializer) declared? - bool bObjectInitializerConstructorDeclared; - - // Is custom VTable helper constructor declared? - bool bCustomVTableHelperConstructorDeclared; - - // GENERATED_BODY access specifier to preserve. - EAccessSpecifier GeneratedBodyMacroAccessSpecifier; - - /** Parsed interface state */ - EParsedInterface ParsedInterface = EParsedInterface::NotAnInterface; - - friend struct FClassMetaDataArchiveProxy; -}; - -/** - * Class for storing and linking data about properties and functions that is only required by the compiler. - * The type of data tracked by this class is data that would otherwise only be accessible by adding a - * member property to UFunction/FProperty. - */ -class FCompilerMetadataManager : protected TMap > -{ - using Super = TMap>; - -public: - /** - * Adds a new class to be tracked - * - * @param Struct the UStruct to add - * - * @return a pointer to the newly added metadata for the class specified - */ - FClassMetaData* AddClassData(UStruct* Struct, FUnrealSourceFile* UnrealSourceFile); - - FClassMetaData* AddInterfaceClassData(UStruct* Struct, FUnrealSourceFile* UnrealSourceFile); - - /** - * Find the metadata associated with the class specified - * - * @param Struct the UStruct to add - * - * @return a pointer to the newly added metadata for the class specified - */ - FClassMetaData* FindClassData(UStruct* Struct) - { - FClassMetaData* Result = nullptr; - - TUniquePtr* pClassData = Find(Struct); - if (pClassData) - { - Result = pClassData->Get(); - } - - return Result; - } - - /** - * Shrink TMaps to avoid slack in Pairs array. - */ - void Shrink() - { - TMap >::Shrink(); - for (TMap >::TIterator It(*this); It; ++It) - { - FClassMetaData* MetaData = It->Value.Get(); - MetaData->Shrink(); - } - } - - /** - * Throws an exception if a UInterface was parsed but not the corresponding IInterface. - */ - void CheckForNoIInterfaces(); - - friend struct FCompilerMetadataManagerArchiveProxy; - -private: - TArray> InterfacesToVerify; -}; - -/*----------------------------------------------------------------------------- - Retry points. ------------------------------------------------------------------------------*/ - -/** - * A point in the header parsing state that can be set and returned to - * using InitScriptLocation() and ReturnToLocation(). This is used in cases such as testing - * to see which overridden operator should be used, where code must be compiled - * and then "undone" if it was found not to match. - *

- * Retries are not allowed to cross command boundaries (and thus nesting - * boundaries). Retries can occur across a single command or expressions and - * subexpressions within a command. - */ -struct FScriptLocation -{ - static class FHeaderParser* Compiler; - - /** the text buffer for the class associated with this retry point */ - const TCHAR* Input; - - /** the position into the Input buffer where this retry point is located */ - int32 InputPos; - - /** the LineNumber of the compiler when this retry point was created */ - int32 InputLine; - - /** Constructor */ - FScriptLocation(); -}; - -///////////////////////////////////////////////////// -// FNameLookupCPP - -/** - * Helper class used to cache UClass* -> TCHAR* name lookup for finding the named used for C++ declaration. - */ -struct FNameLookupCPP -{ - /** - * Returns the name used for declaring the passed in struct in C++ - * - * @param Struct UStruct to obtain C++ name for - * @return Name used for C++ declaration - */ - static FString GetNameCPP(UStruct* Struct, bool bForceInterface = false) - { - return FString::Printf(TEXT("%s%s"), (bForceInterface ? TEXT("I") : Struct->GetPrefixCPP()), *Struct->GetName()); - } -}; - -///////////////////////////////////////////////////// -// FAdvancedDisplayParameterHandler - used by FHeaderParser::ParseParameterList, to check if a property if a function parameter has 'AdvancedDisplay' flag - -/** - * AdvancedDisplay can be used in two ways: - * 1. 'AdvancedDisplay = "3"' - the number tells how many parameters (from beginning) should NOT BE marked - * 2. 'AdvancedDisplay = "AttachPointName, Location, LocationType"' - list the parameters, that should BE marked - */ -class FAdvancedDisplayParameterHandler -{ - TArray ParametersNames; - - int32 NumberLeaveUnmarked; - int32 AlreadyLeft; - - bool bUseNumber; -public: - FAdvancedDisplayParameterHandler(const TMap* MetaData); - - /** - * return if given parameter should be marked as Advance View, - * the function should be called only once for any parameter - */ - bool ShouldMarkParameter(const FString& ParameterName); - - /** return if more parameters can be marked */ - bool CanMarkMore() const; -}; diff --git a/Engine/Source/Programs/UHTLite/Private/Scope.cpp b/Engine/Source/Programs/UHTLite/Private/Scope.cpp deleted file mode 100644 index 0bbb2a6674eb..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Scope.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "Scope.h" -#include "UnrealHeaderTool.h" -#include "UObject/ErrorException.h" -#include "ParserHelper.h" - -extern FCompilerMetadataManager GScriptHelper; - -FScope::FScope(FScope* InParent) - : Parent(InParent) -{ } - -FScope::FScope() - : Parent(nullptr) -{ - -} - -void FScope::AddType(UField* Type) -{ - TypeMap.Add(Type->GetFName(), Type); -} - -/** - * Dispatch type to one of three arrays Enums, Structs and DelegateFunctions. - * - * @param Type Input type. - * @param Enums (Output parameter) Array to fill with enums. - * @param Structs (Output parameter) Array to fill with structs. - * @param DelegateFunctions (Output parameter) Array to fill with delegate functions. - */ -void DispatchType(UField* Type, TArray &Enums, TArray &Structs, TArray &DelegateFunctions) -{ - UClass* TypeClass = Type->GetClass(); - - if (TypeClass == UClass::StaticClass() || TypeClass == UStruct::StaticClass()) - { - // Inner scopes. - FScope::GetTypeScope((UStruct*)Type)->SplitTypesIntoArrays(Enums, Structs, DelegateFunctions); - } - else if (TypeClass == UEnum::StaticClass()) - { - UEnum* Enum = (UEnum*)Type; - Enums.Add(Enum); - } - else if (TypeClass == UScriptStruct::StaticClass()) - { - UScriptStruct* Struct = (UScriptStruct*)Type; - Structs.Add(Struct); - } - else if (TypeClass == UDelegateFunction::StaticClass() || TypeClass == USparseDelegateFunction::StaticClass()) - { - bool bAdded = false; - UDelegateFunction* Function = (UDelegateFunction*)Type; - - if (Function->GetSuperFunction() == NULL) - { - DelegateFunctions.Add(Function); - bAdded = true; - } - - check(bAdded); - } -} - -void FScope::SplitTypesIntoArrays(TArray& Enums, TArray& Structs, TArray& DelegateFunctions) -{ - for (TPair& TypePair : TypeMap) - { - UField* Type = TypePair.Value; - DispatchType(Type, Enums, Structs, DelegateFunctions); - } -} - -TSharedRef FScope::GetTypeScope(UStruct* Type) -{ - auto* ScopeRefPtr = ScopeMap.Find(Type); - if (!ScopeRefPtr) - { - FError::Throwf(TEXT("Couldn't find scope for the type %s."), *Type->GetName()); - } - - return *ScopeRefPtr; -} - -TSharedRef FScope::AddTypeScope(UStruct* Type, FScope* ParentScope) -{ - FStructScope* ScopePtr = new FStructScope(Type, ParentScope); - TSharedRef Scope = MakeShareable(ScopePtr); - - ScopeMap.Add(Type, Scope); - - return Scope; -} - -UField* FScope::FindTypeByName(FName Name) -{ - if (!Name.IsNone()) - { - TDeepScopeTypeIterator TypeIterator(this); - - while (TypeIterator.MoveNext()) - { - UField* Type = *TypeIterator; - if (Type->GetFName() == Name) - { - return Type; - } - } - } - - return nullptr; -} - -const UField* FScope::FindTypeByName(FName Name) const -{ - if (!Name.IsNone()) - { - TScopeTypeIterator TypeIterator = GetTypeIterator(); - - while (TypeIterator.MoveNext()) - { - UField* Type = *TypeIterator; - if (Type->GetFName() == Name) - { - return Type; - } - } - } - - return nullptr; -} - -bool FScope::ContainsType(UField* Type) -{ - return FindTypeByName(Type->GetFName()) != nullptr; -} - -bool FScope::IsFileScope() const -{ - return Parent == nullptr; -} - -bool FScope::ContainsTypes() const -{ - return TypeMap.Num() > 0; -} - -FFileScope* FScope::GetFileScope() -{ - FScope* CurrentScope = this; - while (!CurrentScope->IsFileScope()) - { - CurrentScope = const_cast(CurrentScope->GetParent()); - } - - return CurrentScope->AsFileScope(); -} - -TMap > FScope::ScopeMap; - -FFileScope::FFileScope(FName InName, FUnrealSourceFile* InSourceFile) - : SourceFile(InSourceFile), Name(InName) -{ } - -void FFileScope::IncludeScope(FFileScope* IncludedScope) -{ - IncludedScopes.Add(IncludedScope); -} - -FUnrealSourceFile* FFileScope::GetSourceFile() const -{ - return SourceFile; -} - -FName FFileScope::GetName() const -{ - return Name; -} - -UStruct* FStructScope::GetStruct() const -{ - return Struct; -} - -FName FStructScope::GetName() const -{ - return Struct->GetFName(); -} - -FStructScope::FStructScope(UStruct* InStruct, FScope* InParent) - : FScope(InParent), Struct(InStruct) -{ - -} diff --git a/Engine/Source/Programs/UHTLite/Private/Scope.h b/Engine/Source/Programs/UHTLite/Private/Scope.h deleted file mode 100644 index c2aa3c6654fb..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Scope.h +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/Class.h" - -class UEnum; -class UScriptStruct; -class UDelegateFunction; - -// Forward declarations. -class UStruct; -class FClassMetaData; -class FUnrealSourceFile; -class FFileScope; -class FStructScope; - -// Traits to achieve conditional types for const/non-const iterators. -template -class TConditionalType -{ -public: - typedef TypeIfFalse Type; -}; - -template -class TConditionalType -{ -public: - typedef TypeIfTrue Type; -}; - -// Base class representing type scope. -class FScope : public TSharedFromThis -{ -public: - // Default constructor i.e. Parent == nullptr - FScope(); - - // Constructor that's providing parent scope. - FScope(FScope* Parent); - - // Virtual destructor. - virtual ~FScope() - { }; - - virtual FFileScope* AsFileScope() { return nullptr; } - virtual FStructScope* AsStructScope() { return nullptr; } - - /** - * Adds type to the scope. - * - * @param Type Type to add. - */ - void AddType(UField* Type); - - /** - * Finds type by its name. - * - * @param Name Name to look for. - * - * @returns Found type or nullptr on failure. - */ - UField* FindTypeByName(FName Name); - - /** - * Finds type by its name. - * - * @param Name Name to look for. - * - * @returns Found type or nullptr on failure. - */ - template - UField* FindTypeByName(const CharType* Name) - { - return FindTypeByName(FName(Name, FNAME_Find)); - } - - /** - * Finds type by its name. - * - * Const version. - * - * @param Name Name to look for. - * - * @returns Found type or nullptr on failure. - */ - const UField* FindTypeByName(FName Name) const; - - /** - * Finds type by its name. - * - * Const version. - * - * @param Name Name to look for. - * - * @returns Found type or nullptr on failure. - */ - template - const UField* FindTypeByName(const CharType* Name) const - { - return FindTypeByName(FName(Name, FNAME_Find)); - } - /** - * Checks if scope contains this type. - * - * @param Type Type to look for. - */ - bool ContainsType(UField* Type); - - /** - * Checks if scope contains type that satisfies a predicate. - * - * @param Predicate Predicate to satisfy. - */ - template - bool Contains(TPredicateType Predicate) - { - for (const auto& NameTypePair : TypeMap) - { - if (Predicate.operator()(NameTypePair.Value)) - { - return true; - } - } - - return false; - } - - /** - * Gets struct's or class's scope. - * - * @param Type A struct or class for which to return the scope. - * - * @returns Struct's or class's scope. - */ - static TSharedRef GetTypeScope(UStruct* Type); - - /** - * Adds struct's or class's scope. - * - * @param Type A struct or class for which to add the scope. - * @param ParentScope A scope in which this scope is contained. - * - * @returns Newly added scope. - */ - static TSharedRef AddTypeScope(UStruct* Type, FScope* ParentScope); - - /** - * Gets structs, enums and delegate functions from this scope. - * - * @param Enums (Output parameter) List of enums from this scope. - * @param Structs (Output parameter) List of structs from this scope. - * @param DelegateFunctions (Output parameter) List of delegate properties from this scope. - */ - void SplitTypesIntoArrays(TArray& Enums, TArray& Structs, TArray& DelegateFunctions); - - /** - * Gets scope name. - */ - virtual FName GetName() const = 0; - - /** - * Gets scope types filtered by type. - * - * @param OutArray (Output parameter) Array to fill with types from this scope. - */ - template - void GetTypes(TArray OutArray) - { - for (const auto& NameTypePair : TypeMap) - { - if (NameTypePair.Value->IsA()) - { - OutArray.Add((TTypeFilter*)NameTypePair.Value); - } - } - } - - /** - * Gets scope's parent. - */ - const FScope* GetParent() const - { - return Parent; - } - - /** - * Scope type iterator. - */ - template - class TScopeTypeIterator - { - public: - // Conditional typedefs. - typedef typename TConditionalType::TConstIterator, TMap::TIterator>::Type MapIteratorType; - typedef typename TConditionalType::Type ScopeType; - - // Constructor - TScopeTypeIterator(ScopeType* Scope) - : TypeIterator(Scope->TypeMap) - { - bBeforeStart = true; - } - - /** - * Indirection operator. - * - * @returns Type this iterator currently point to. - */ - TType* operator*() - { - return bBeforeStart ? nullptr : (TType*)(*TypeIterator).Value; - } - - /** - * Moves this iterator to next type. - * - * @returns True on success. False otherwise. - */ - bool MoveNext() - { - if (!bBeforeStart) - { - ++TypeIterator; - } - - bBeforeStart = false; - return TypeIterator.operator bool(); - } - - private: - - // Current type. - MapIteratorType TypeIterator; - - // Tells if this iterator is in initial state. - bool bBeforeStart; - }; - - /** - * Gets type iterator. - * - * @return Type iterator. - */ - template - TScopeTypeIterator GetTypeIterator() - { - return TScopeTypeIterator(this); - } - - /** - * Gets const type iterator. - * - * @return Const type iterator. - */ - template - TScopeTypeIterator GetTypeIterator() const - { - return TScopeTypeIterator(this); - } - - /** - * Tells if this scope is a file scope. - * - * @returns True if this is a file scope. False otherwise. - */ - bool IsFileScope() const; - - /** - * Tells if this scope contains any type. - * - * @returns True if this scope contains type. False otherwise. - */ - bool ContainsTypes() const; - - FFileScope* GetFileScope(); -private: - // This scopes parent. - const FScope* Parent; - - // Map of types in this scope. - TMap TypeMap; - - // Global map type <-> scope. - static TMap > ScopeMap; - - friend struct FScopeArchiveProxy; - friend struct FStructScopeArchiveProxy; -}; - -using FScopeSet = TSet, TInlineSetAllocator<1024>>; - -/** - * Represents a scope associated with source file. - */ -class FFileScope : public FScope -{ -public: - FFileScope() - : SourceFile(nullptr) - , Name(NAME_None) - { } - // Constructor. - FFileScope(FName Name, FUnrealSourceFile* SourceFile); - - /** - * Includes other file scopes into this scope (#include). - * - * @param IncludedScope File scope to include. - */ - void IncludeScope(FFileScope* IncludedScope); - - virtual FFileScope* AsFileScope() override { return this; } - - /** - * Gets scope name. - */ - FName GetName() const override; - - /** - * Gets source file associated with this scope. - */ - FUnrealSourceFile* GetSourceFile() const; - - /** - * Appends included file scopes into given array. - * - * @param Out (Output parameter) Array to append scopes. - */ - void AppendIncludedFileScopes(FScopeSet& Out) - { - bool bAlreadyAdded = false; - Out.Add(this, &bAlreadyAdded); - - if (!bAlreadyAdded) - { - for (FFileScope* IncludedScope : IncludedScopes) - { - IncludedScope->AppendIncludedFileScopes(Out); - } - } - } - - - const TArray& GetIncludedScopes() const - { - return IncludedScopes; - } - - void SetSourceFile(FUnrealSourceFile* InSourceFile) - { - SourceFile = InSourceFile; - } -private: - // Source file. - FUnrealSourceFile* SourceFile; - - // Scope name. - FName Name; - - // Included scopes list. - TArray IncludedScopes; - - friend struct FFileScopeArchiveProxy; -}; - - -/** - * Data structure representing scope of a struct or class. - */ -class FStructScope : public FScope -{ -public: - // Constructor. - FStructScope(UStruct* Struct, FScope* Parent); - - virtual FStructScope* AsStructScope() override { return this; } - /** - * Gets struct associated with this scope. - */ - UStruct* GetStruct() const; - - /** - * Gets scope name. - */ - FName GetName() const override; - -private: - // Struct associated with this scope. - UStruct* Struct; - - friend struct FStructScopeArchiveProxy; -}; - -/** - * Deep scope type iterator. It looks for type in the whole scope hierarchy. - * First going up inheritance from inner structs to outer. Then through all - * included scopes. - */ -template -class TDeepScopeTypeIterator -{ -public: - // Conditional typedefs. - typedef typename TConditionalType::TConstIterator, TMap::TIterator>::Type MapIteratorType; - typedef typename TConditionalType::Type ScopeType; - typedef typename TConditionalType::Type FileScopeType; - typedef typename TConditionalType::Type ScopeArrayIteratorType; - - // Constructor. - TDeepScopeTypeIterator(ScopeType* Scope) - { - const ScopeType* CurrentScope = Scope; - - while (!CurrentScope->IsFileScope()) - { - ScopesToTraverse.Add(Scope); - - UStruct* Struct = ((FStructScope*)CurrentScope)->GetStruct(); - - if (Struct->IsA()) - { - UClass* Class = ((UClass*)Struct)->GetSuperClass(); - - while (Class && !(Class->ClassFlags & EClassFlags::CLASS_Intrinsic)) - { - ScopesToTraverse.Add(&FScope::GetTypeScope(Class).Get()); - Class = Class->GetSuperClass(); - } - } - - CurrentScope = CurrentScope->GetParent(); - } - - ((FileScopeType*)CurrentScope)->AppendIncludedFileScopes(ScopesToTraverse); - } - - /** - * Iterator increment. - * - * @returns True if moved iterator to another position. False otherwise. - */ - bool MoveNext() - { - if (!ScopeIterator.IsSet() && !MoveToNextScope()) - { - return false; - } - - if (ScopeIterator->MoveNext()) - { - return true; - } - else - { - do - { - ScopeIterator.Reset(); - if (!MoveToNextScope()) - { - return false; - } - } - while(!ScopeIterator->MoveNext()); - - return true; - } - } - - /** - * Current type getter. - * - * @returns Current type. - */ - TType* operator*() - { - return ScopeIterator->operator*(); - } - -private: - /** - * Moves the iterator to the next scope. - * - * @returns True if succeeded. False otherwise. - */ - bool MoveToNextScope() - { - if (!ScopesIterator.IsSet()) - { - ScopesIterator.Emplace(ScopesToTraverse); - } - else - { - ScopesIterator->operator++(); - } - - if (!ScopesIterator->operator bool()) - { - return false; - } - - ScopeIterator.Emplace(ScopesIterator->operator*()); - return true; - } - - // Current scope iterator. - TOptional > ScopeIterator; - - // Scopes list iterator. - TOptional ScopesIterator; - - // List of scopes to traverse. - FScopeSet ScopesToTraverse; -}; - diff --git a/Engine/Source/Programs/UHTLite/Private/SimplifiedParsingClassInfo.h b/Engine/Source/Programs/UHTLite/Private/SimplifiedParsingClassInfo.h deleted file mode 100644 index efde6d4c0435..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/SimplifiedParsingClassInfo.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Containers/UnrealString.h" - -class FArchive; - -/** - * Simplified parsing information about UClasses. - */ -class FSimplifiedParsingClassInfo -{ -public: - // Constructor. - FSimplifiedParsingClassInfo(FString&& InClassName, FString&& InBaseClassName, int32 InClassDefLine, bool bInClassIsAnInterface) - : ClassName (MoveTemp(InClassName)) - , BaseClassName (MoveTemp(InBaseClassName)) - , ClassDefLine (InClassDefLine) - , bClassIsAnInterface(bInClassIsAnInterface) - {} - - FSimplifiedParsingClassInfo() { } - - /** - * Gets class name. - */ - const FString& GetClassName() const - { - return ClassName; - } - - /** - * Gets base class name. - */ - const FString& GetBaseClassName() const - { - return BaseClassName; - } - - /** - * Gets class definition line number. - */ - int32 GetClassDefLine() const - { - return ClassDefLine; - } - - /** - * Tells if this class is an interface. - */ - bool IsInterface() const - { - return bClassIsAnInterface; - } - - friend FArchive& operator<<(FArchive& Ar, FSimplifiedParsingClassInfo& SimplifiedParsingClassInfo) - { - Ar << SimplifiedParsingClassInfo.ClassName; - Ar << SimplifiedParsingClassInfo.BaseClassName; - Ar << SimplifiedParsingClassInfo.ClassDefLine; - Ar << SimplifiedParsingClassInfo.bClassIsAnInterface; - - return Ar; - } - -private: - // Name. - FString ClassName; - - // Super-class name. - FString BaseClassName; - - // Class definition line. - int32 ClassDefLine; - - // Is this an interface class? - bool bClassIsAnInterface; -}; diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/CheckedMetadataSpecifiers.def b/Engine/Source/Programs/UHTLite/Private/Specifiers/CheckedMetadataSpecifiers.def deleted file mode 100644 index cbfdcf2c5a42..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/CheckedMetadataSpecifiers.def +++ /dev/null @@ -1,17 +0,0 @@ -// CHECKED_METADATA_SPECIFIER() -// -// Must be listed such that the strings are in lexicographical order! - -// [[ IncludeTool: Inline ]] // Markup to tell IncludeTool that this file is state changing and cannot be optimized out. - -CHECKED_METADATA_SPECIFIER(BlueprintProtected) -CHECKED_METADATA_SPECIFIER(ClampMax) -CHECKED_METADATA_SPECIFIER(ClampMin) -CHECKED_METADATA_SPECIFIER(CommutativeAssociativeBinaryOperator) -CHECKED_METADATA_SPECIFIER(DevelopmentStatus) -CHECKED_METADATA_SPECIFIER(DocumentationPolicy) -CHECKED_METADATA_SPECIFIER(ExpandBoolAsExecs) -CHECKED_METADATA_SPECIFIER(ExpandEnumAsExecs) -CHECKED_METADATA_SPECIFIER(UIMax) -CHECKED_METADATA_SPECIFIER(UIMin) -CHECKED_METADATA_SPECIFIER(Units) diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/CheckedMetadataSpecifiers.h b/Engine/Source/Programs/UHTLite/Private/Specifiers/CheckedMetadataSpecifiers.h deleted file mode 100644 index 5ddff936be34..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/CheckedMetadataSpecifiers.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -enum class ECheckedMetadataSpecifier -{ - None = -1 - - #define CHECKED_METADATA_SPECIFIER(SpecifierName) , SpecifierName - #include "CheckedMetadataSpecifiers.def" - #undef CHECKED_METADATA_SPECIFIER - - , Max -}; - -extern const TMap GCheckedMetadataSpecifiers; - -ECheckedMetadataSpecifier GetCheckedMetadataSpecifier(FName Key); \ No newline at end of file diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/ClassMetadataSpecifiers.def b/Engine/Source/Programs/UHTLite/Private/Specifiers/ClassMetadataSpecifiers.def deleted file mode 100644 index 671fe75f93d9..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/ClassMetadataSpecifiers.def +++ /dev/null @@ -1,42 +0,0 @@ -// CLASS_METADATA_SPECIFIER() -// -// Must be listed such that the strings are in lexicographical order! - -// [[ IncludeTool: Inline ]] // Markup to tell IncludeTool that this file is state changing and cannot be optimized out. - -CLASS_METADATA_SPECIFIER(Abstract) -CLASS_METADATA_SPECIFIER(AdvancedClassDisplay) -CLASS_METADATA_SPECIFIER(AutoCollapseCategories) -CLASS_METADATA_SPECIFIER(AutoExpandCategories) -CLASS_METADATA_SPECIFIER(ClassGroup) -CLASS_METADATA_SPECIFIER(CollapseCategories) -CLASS_METADATA_SPECIFIER(ComponentWrapperClass) -CLASS_METADATA_SPECIFIER(Config) -CLASS_METADATA_SPECIFIER(ConfigDoNotCheckDefaults) -CLASS_METADATA_SPECIFIER(Const) -CLASS_METADATA_SPECIFIER(ConversionRoot) -CLASS_METADATA_SPECIFIER(CustomConstructor) -CLASS_METADATA_SPECIFIER(DefaultConfig) -CLASS_METADATA_SPECIFIER(DefaultToInstanced) -CLASS_METADATA_SPECIFIER(DependsOn) -CLASS_METADATA_SPECIFIER(Deprecated) -CLASS_METADATA_SPECIFIER(DontAutoCollapseCategories) -CLASS_METADATA_SPECIFIER(DontCollapseCategories) -CLASS_METADATA_SPECIFIER(EditInlineNew) -CLASS_METADATA_SPECIFIER(GlobalUserConfig) -CLASS_METADATA_SPECIFIER(HideCategories) -CLASS_METADATA_SPECIFIER(HideDropdown) -CLASS_METADATA_SPECIFIER(HideFunctions) -CLASS_METADATA_SPECIFIER(Intrinsic) -CLASS_METADATA_SPECIFIER(MinimalAPI) -CLASS_METADATA_SPECIFIER(NoExport) -CLASS_METADATA_SPECIFIER(NonTransient) -CLASS_METADATA_SPECIFIER(NotEditInlineNew) -CLASS_METADATA_SPECIFIER(NotPlaceable) -CLASS_METADATA_SPECIFIER(PerObjectConfig) -CLASS_METADATA_SPECIFIER(Placeable) -CLASS_METADATA_SPECIFIER(ShowCategories) -CLASS_METADATA_SPECIFIER(ShowFunctions) -CLASS_METADATA_SPECIFIER(SparseClassDataTypes) -CLASS_METADATA_SPECIFIER(Transient) -CLASS_METADATA_SPECIFIER(Within) diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/ClassMetadataSpecifiers.h b/Engine/Source/Programs/UHTLite/Private/Specifiers/ClassMetadataSpecifiers.h deleted file mode 100644 index d23f02bd8dc0..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/ClassMetadataSpecifiers.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -enum class EClassMetadataSpecifier -{ - None = -1 - - #define CLASS_METADATA_SPECIFIER(SpecifierName) , SpecifierName - #include "ClassMetadataSpecifiers.def" - #undef CLASS_METADATA_SPECIFIER - - , Max -}; - -extern const TCHAR* GClassMetadataSpecifierStrings[(int32)EClassMetadataSpecifier::Max]; diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/FunctionSpecifiers.def b/Engine/Source/Programs/UHTLite/Private/Specifiers/FunctionSpecifiers.def deleted file mode 100644 index 35fa0054c716..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/FunctionSpecifiers.def +++ /dev/null @@ -1,25 +0,0 @@ -// FUNCTION_SPECIFIER() -// -// Must be listed such that the strings are in lexicographical order! - -// [[ IncludeTool: Inline ]] // Markup to tell IncludeTool that this file is state changing and cannot be optimized out. - -FUNCTION_SPECIFIER(BlueprintAuthorityOnly) -FUNCTION_SPECIFIER(BlueprintCallable) -FUNCTION_SPECIFIER(BlueprintCosmetic) -FUNCTION_SPECIFIER(BlueprintGetter) -FUNCTION_SPECIFIER(BlueprintImplementableEvent) -FUNCTION_SPECIFIER(BlueprintNativeEvent) -FUNCTION_SPECIFIER(BlueprintPure) -FUNCTION_SPECIFIER(BlueprintSetter) -FUNCTION_SPECIFIER(Client) -FUNCTION_SPECIFIER(CustomThunk) -FUNCTION_SPECIFIER(Exec) -FUNCTION_SPECIFIER(NetMulticast) -FUNCTION_SPECIFIER(Reliable) -FUNCTION_SPECIFIER(SealedEvent) -FUNCTION_SPECIFIER(Server) -FUNCTION_SPECIFIER(ServiceRequest) -FUNCTION_SPECIFIER(ServiceResponse) -FUNCTION_SPECIFIER(Unreliable) -FUNCTION_SPECIFIER(WithValidation) diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/FunctionSpecifiers.h b/Engine/Source/Programs/UHTLite/Private/Specifiers/FunctionSpecifiers.h deleted file mode 100644 index 2ca7cfe444bb..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/FunctionSpecifiers.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -enum class EFunctionSpecifier -{ - None = -1 - - #define FUNCTION_SPECIFIER(SpecifierName) , SpecifierName - #include "FunctionSpecifiers.def" - #undef FUNCTION_SPECIFIER - - , Max -}; - -extern const TCHAR* GFunctionSpecifierStrings[(int32)EFunctionSpecifier::Max]; diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/InterfaceSpecifiers.def b/Engine/Source/Programs/UHTLite/Private/Specifiers/InterfaceSpecifiers.def deleted file mode 100644 index 322dc0b0e1b4..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/InterfaceSpecifiers.def +++ /dev/null @@ -1,9 +0,0 @@ -// INTERFACE_SPECIFIER() -// -// Must be listed such that the strings are in lexicographical order! - -// [[ IncludeTool: Inline ]] // Markup to tell IncludeTool that this file is state changing and cannot be optimized out. - -INTERFACE_SPECIFIER(ConversionRoot) -INTERFACE_SPECIFIER(DependsOn) -INTERFACE_SPECIFIER(MinimalAPI) diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/InterfaceSpecifiers.h b/Engine/Source/Programs/UHTLite/Private/Specifiers/InterfaceSpecifiers.h deleted file mode 100644 index 3599d7b8813c..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/InterfaceSpecifiers.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -enum class EInterfaceSpecifier -{ - None = -1 - - #define INTERFACE_SPECIFIER(SpecifierName) , SpecifierName - #include "InterfaceSpecifiers.def" - #undef INTERFACE_SPECIFIER - - , Max -}; - -extern const TCHAR* GInterfaceSpecifierStrings[(int32)EInterfaceSpecifier::Max]; diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/SpecifierArrays.cpp b/Engine/Source/Programs/UHTLite/Private/Specifiers/SpecifierArrays.cpp deleted file mode 100644 index 62fc3646b244..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/SpecifierArrays.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "CoreMinimal.h" -#include "UnrealHeaderTool.h" -#include "Misc/AssertionMacros.h" -#include "Misc/CString.h" - -#include "CheckedMetadataSpecifiers.h" -#include "ClassMetadataSpecifiers.h" -#include "FunctionSpecifiers.h" -#include "InterfaceSpecifiers.h" -#include "StructSpecifiers.h" -#include "VariableSpecifiers.h" -#include "Algo/IsSorted.h" - -const TCHAR* GFunctionSpecifierStrings[(int32)EFunctionSpecifier::Max] = -{ - #define FUNCTION_SPECIFIER(SpecifierName) TEXT(#SpecifierName), - #include "FunctionSpecifiers.def" - #undef FUNCTION_SPECIFIER -}; - -const TCHAR* GStructSpecifierStrings[(int32)EStructSpecifier::Max] = -{ - #define STRUCT_SPECIFIER(SpecifierName) TEXT(#SpecifierName), - #include "StructSpecifiers.def" - #undef STRUCT_SPECIFIER -}; - -const TCHAR* GInterfaceSpecifierStrings[(int32)EInterfaceSpecifier::Max] = -{ - #define INTERFACE_SPECIFIER(SpecifierName) TEXT(#SpecifierName), - #include "InterfaceSpecifiers.def" - #undef INTERFACE_SPECIFIER -}; - -const TCHAR* GVariableSpecifierStrings[(int32)EVariableSpecifier::Max] = -{ - #define VARIABLE_SPECIFIER(SpecifierName) TEXT(#SpecifierName), - #include "VariableSpecifiers.def" - #undef VARIABLE_SPECIFIER -}; - -const TMap GCheckedMetadataSpecifiers( -{ - #define CHECKED_METADATA_SPECIFIER(SpecifierName) { FName(#SpecifierName), ECheckedMetadataSpecifier::SpecifierName }, - #include "CheckedMetadataSpecifiers.def" - #undef CHECKED_METADATA_SPECIFIER -}); - -const TCHAR* GClassMetadataSpecifierStrings[(int32)EClassMetadataSpecifier::Max] = -{ - #define CLASS_METADATA_SPECIFIER(SpecifierName) TEXT(#SpecifierName), - #include "ClassMetadataSpecifiers.def" - #undef CLASS_METADATA_SPECIFIER -}; - -ECheckedMetadataSpecifier GetCheckedMetadataSpecifier(FName Key) -{ - if (const ECheckedMetadataSpecifier* Specifier = GCheckedMetadataSpecifiers.Find(Key)) - { - return *Specifier; - } - return ECheckedMetadataSpecifier::Max; -} - -struct FCStringsLessThanCaseInsensitive -{ - FORCEINLINE bool operator()(const TCHAR* Lhs, const TCHAR* Rhs) - { - return FCString::Stricmp(Lhs, Rhs) < 0; - } -}; - -const bool GIsGFunctionSpecifierStringsSorted = ensure(Algo::IsSorted(GFunctionSpecifierStrings, FCStringsLessThanCaseInsensitive())); -const bool GIsGStructSpecifierStringsSorted = ensure(Algo::IsSorted(GStructSpecifierStrings, FCStringsLessThanCaseInsensitive())); -const bool GIsGInterfaceSpecifierStringsSorted = ensure(Algo::IsSorted(GInterfaceSpecifierStrings, FCStringsLessThanCaseInsensitive())); -const bool GIsGVariableSpecifierStringsSorted = ensure(Algo::IsSorted(GVariableSpecifierStrings, FCStringsLessThanCaseInsensitive())); -const bool GIsGClassMetadataSpecifierStringsSorted = ensure(Algo::IsSorted(GClassMetadataSpecifierStrings, FCStringsLessThanCaseInsensitive())); \ No newline at end of file diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/StructSpecifiers.def b/Engine/Source/Programs/UHTLite/Private/Specifiers/StructSpecifiers.def deleted file mode 100644 index dd548de36d97..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/StructSpecifiers.def +++ /dev/null @@ -1,9 +0,0 @@ -// STRUCT_SPECIFIER() -// -// Must be listed such that the strings are in lexicographical order! - -// [[ IncludeTool: Inline ]] // Markup to tell IncludeTool that this file is state changing and cannot be optimized out. - -STRUCT_SPECIFIER(Atomic) -STRUCT_SPECIFIER(Immutable) -STRUCT_SPECIFIER(NoExport) diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/StructSpecifiers.h b/Engine/Source/Programs/UHTLite/Private/Specifiers/StructSpecifiers.h deleted file mode 100644 index b16b19bd3f6b..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/StructSpecifiers.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -enum class EStructSpecifier -{ - None = -1 - - #define STRUCT_SPECIFIER(SpecifierName) , SpecifierName - #include "StructSpecifiers.def" - #undef STRUCT_SPECIFIER - - , Max -}; - -extern const TCHAR* GStructSpecifierStrings[(int32)EStructSpecifier::Max]; diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/VariableSpecifiers.def b/Engine/Source/Programs/UHTLite/Private/Specifiers/VariableSpecifiers.def deleted file mode 100644 index b241571a1614..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/VariableSpecifiers.def +++ /dev/null @@ -1,45 +0,0 @@ -// VARIABLE_SPECIFIER() -// -// Must be listed such that the strings are in lexicographical order! - -// [[ IncludeTool: Inline ]] // Markup to tell IncludeTool that this file is state changing and cannot be optimized out. - -VARIABLE_SPECIFIER(AdvancedDisplay) -VARIABLE_SPECIFIER(AssetRegistrySearchable) -VARIABLE_SPECIFIER(BlueprintAssignable) -VARIABLE_SPECIFIER(BlueprintAuthorityOnly) -VARIABLE_SPECIFIER(BlueprintCallable) -VARIABLE_SPECIFIER(BlueprintGetter) -VARIABLE_SPECIFIER(BlueprintReadOnly) -VARIABLE_SPECIFIER(BlueprintReadWrite) -VARIABLE_SPECIFIER(BlueprintSetter) -VARIABLE_SPECIFIER(Config) -VARIABLE_SPECIFIER(Const) -VARIABLE_SPECIFIER(DuplicateTransient) -VARIABLE_SPECIFIER(EditAnywhere) -VARIABLE_SPECIFIER(EditDefaultsOnly) -VARIABLE_SPECIFIER(EditFixedSize) -VARIABLE_SPECIFIER(EditInline) -VARIABLE_SPECIFIER(EditInstanceOnly) -VARIABLE_SPECIFIER(Export) -VARIABLE_SPECIFIER(GlobalConfig) -VARIABLE_SPECIFIER(Instanced) -VARIABLE_SPECIFIER(Interp) -VARIABLE_SPECIFIER(Localized) -VARIABLE_SPECIFIER(NoClear) -VARIABLE_SPECIFIER(NonPIEDuplicateTransient) -VARIABLE_SPECIFIER(NonPIETransient) -VARIABLE_SPECIFIER(NonTransactional) -VARIABLE_SPECIFIER(NotReplicated) -VARIABLE_SPECIFIER(Ref) -VARIABLE_SPECIFIER(Replicated) -VARIABLE_SPECIFIER(ReplicatedUsing) -VARIABLE_SPECIFIER(RepRetry) -VARIABLE_SPECIFIER(SaveGame) -VARIABLE_SPECIFIER(SimpleDisplay) -VARIABLE_SPECIFIER(SkipSerialization) -VARIABLE_SPECIFIER(TextExportTransient) -VARIABLE_SPECIFIER(Transient) -VARIABLE_SPECIFIER(VisibleAnywhere) -VARIABLE_SPECIFIER(VisibleDefaultsOnly) -VARIABLE_SPECIFIER(VisibleInstanceOnly) diff --git a/Engine/Source/Programs/UHTLite/Private/Specifiers/VariableSpecifiers.h b/Engine/Source/Programs/UHTLite/Private/Specifiers/VariableSpecifiers.h deleted file mode 100644 index b006bb7aa8a4..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/Specifiers/VariableSpecifiers.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -enum class EVariableSpecifier -{ - None = -1 - - #define VARIABLE_SPECIFIER(SpecifierName) , SpecifierName - #include "VariableSpecifiers.def" - #undef VARIABLE_SPECIFIER - - , Max -}; - -extern const TCHAR* GVariableSpecifierStrings[(int32)EVariableSpecifier::Max]; diff --git a/Engine/Source/Programs/UHTLite/Private/StringUtils.cpp b/Engine/Source/Programs/UHTLite/Private/StringUtils.cpp deleted file mode 100644 index 0d07dc64c4c1..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/StringUtils.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "StringUtils.h" -#include "UnrealHeaderTool.h" -#include "Containers/UnrealString.h" -#include "Hash/CityHash.h" - -FString GetClassNameWithPrefixRemoved(const FString& InClassName) -{ - FString Result; - const FString ClassPrefix = GetClassPrefix( InClassName ); - if( !ClassPrefix.IsEmpty() ) - { - Result = InClassName.Right(InClassName.Len() - ClassPrefix.Len()); - } - return Result; -} - -FString GetClassNameWithoutPrefix(FString InClassNameOrFilename) -{ - FString ClassNameWithoutPrefix = MoveTemp(InClassNameOrFilename); - - // Check for header names (they don't come with a full path so we only search for the first dot) - int32 DotIndex; - if (ClassNameWithoutPrefix.FindChar(TEXT('.'), DotIndex)) - { - ClassNameWithoutPrefix.MidInline(0, DotIndex, false); - } - else - { - const FString ClassPrefix = GetClassPrefix(ClassNameWithoutPrefix); - ClassNameWithoutPrefix.RightInline(ClassNameWithoutPrefix.Len() - ClassPrefix.Len(), false); - } - return ClassNameWithoutPrefix; -} - -FString GetClassPrefix( const FString& InClassName ) -{ - bool bIsLabledDeprecated; - return GetClassPrefix(InClassName, /*out*/ bIsLabledDeprecated); -} - -FString GetClassPrefix(const FString& InClassName, bool& bIsLabeledDeprecated ) -{ - FString ClassPrefix = InClassName.Left(1); - - bIsLabeledDeprecated = false; - - if (!ClassPrefix.IsEmpty()) - { - const TCHAR ClassPrefixChar = ClassPrefix[0]; - switch (ClassPrefixChar) - { - case TEXT('I'): - case TEXT('A'): - case TEXT('U'): - // If it is a class prefix, check for deprecated class prefix also - if (InClassName.Len() > 12 && FCString::Strncmp(&(InClassName[1]), TEXT("DEPRECATED_"), 11) == 0) - { - bIsLabeledDeprecated = true; - ClassPrefix = InClassName.Left(12); - } - break; - - case TEXT('F'): - case TEXT('T'): - // Struct prefixes are also fine. - break; - - default: - // If it's not a class or struct prefix, it's invalid - ClassPrefix.Reset(); - break; - } - } - return ClassPrefix; -} - -// Returns zero only for '\r' and '\0' -FORCEINLINE uint64 FindCrOrNulHelper(TCHAR C) -{ - uint64 LoPassMask = ~((uint64(1) << '\0') | (uint64(1) << '\r')); - uint64 LoBit = uint64(1) << (C & 63); - uint64 LoPass = LoBit & LoPassMask; - uint64 HiPass = C & ~63; - - return LoPass | HiPass; -} - -// Finds next CR or null term with a single branch -// to help treat CR-LF and LF line endings identically -FORCEINLINE const TCHAR* FindCrOrNul(const TCHAR* Str) -{ - while (FindCrOrNulHelper(*Str)) - { - ++Str; - } - - return Str; -} - -FORCEINLINE uint64 GenerateTextHash64(const TCHAR* Str) -{ - uint64 Hash = 0; - - while (true) - { - const TCHAR* End = FindCrOrNul(Str); - - if (int32 Len = UE_PTRDIFF_TO_INT32(End - Str)) - { - Hash = CityHash64WithSeed(reinterpret_cast(Str), Len * sizeof(TCHAR), Hash); - } - - if (*End == '\0') - { - return Hash; - } - - Str = End + 1; - } -} - -uint32 GenerateTextHash(const TCHAR* Data) -{ - uint64 Hash = GenerateTextHash64(Data); - return static_cast(Hash) + static_cast(Hash >> 32); -} \ No newline at end of file diff --git a/Engine/Source/Programs/UHTLite/Private/StringUtils.h b/Engine/Source/Programs/UHTLite/Private/StringUtils.h deleted file mode 100644 index 579bc27b61ad..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/StringUtils.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Misc/Timespan.h" - -/** - * Attempts to strip the given class name of its affixed prefix. If no prefix exists, will return a blank string. - * - * @param InClassName Class Name with a prefix to be stripped - */ -FString GetClassNameWithPrefixRemoved(const FString& InClassName); - -/** - * Attempts to strip the given class name of its affixed prefix. If no prefix exists, will return unchanged string. - * - * @param InClassName Class Name with a prefix to be stripped - */ -FString GetClassNameWithoutPrefix(FString InClassNameOrFilename); - -/** - * Attempts to get class prefix. If the given class name does not start with a valid Unreal Prefix, it will return an empty string. - * - * @param InClassName Name w/ potential prefix to check - */ -FString GetClassPrefix(const FString& InClassName); - -/** - * Attempts to get class prefix. If the given class name does not start with a valid Unreal Prefix, it will return an empty string. - * - * @param InClassName Name w/ potential prefix to check - * @param out bIsLabeledDeprecated Reference param set to True if the class name is marked as deprecated - */ -FString GetClassPrefix(const FString& InClassName, bool& bIsLabeledDeprecated); - -/** -* Generates hash for text, ignoring characters such as \r and the terminating \0 -* -* @param InText Text to digest -* @return Hash generated for the specified text buffer -*/ -uint32 GenerateTextHash(const TCHAR* InText); diff --git a/Engine/Source/Programs/UHTLite/Private/UnderlyingEnumType.h b/Engine/Source/Programs/UHTLite/Private/UnderlyingEnumType.h deleted file mode 100644 index 290123f76ea7..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnderlyingEnumType.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -enum class EUnderlyingEnumType -{ - Unspecified, - uint8, - uint16, - uint32, - uint64, - int8, - int16, - int32, - int64 -}; diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderTool.h b/Engine/Source/Programs/UHTLite/Private/UnrealHeaderTool.h deleted file mode 100644 index 36a6aa029d1f..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderTool.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "SimplifiedParsingClassInfo.h" -#include "UnrealSourceFile.h" - -// UHTLite NOTE: Provided to ease debugging during refactoring. -#define UHT_USE_PARALLEL_FOR 0 - -struct FHeaderParserNames -{ - static const FName NAME_IsConversionRoot; - static const FName NAME_HideCategories; - static const FName NAME_ShowCategories; - static const FName NAME_SparseClassDataTypes; - static const FName NAME_AdvancedClassDisplay; -}; - -struct FPerHeaderData -{ - TSharedPtr UnrealSourceFile; - TArray DependsOn; - TArray ParsedClassArray; -}; diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolGlobals.h b/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolGlobals.h deleted file mode 100644 index d8846b5e607d..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolGlobals.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Logging/LogCategory.h" -#include "Logging/LogMacros.h" -#include "UnrealHeaderToolStats.h" - -DECLARE_LOG_CATEGORY_EXTERN(LogCompile, Log, All); - -extern bool GUHTWarningLogged; -extern bool GUHTErrorLogged; - -#define UE_LOG_WARNING_UHT(Format, ...) { GUHTWarningLogged = true; UE_LOG(LogCompile, Warning, Format, ##__VA_ARGS__); } -#define UE_LOG_ERROR_UHT(Format, ...) { GUHTErrorLogged = true; UE_LOG(LogCompile, Error, Format, ##__VA_ARGS__); } diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolMain.cpp b/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolMain.cpp deleted file mode 100644 index f95d268a788c..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolMain.cpp +++ /dev/null @@ -1,937 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - - -#include "UnrealHeaderTool.h" -#include "CoreMinimal.h" -#include "Containers/UnrealString.h" -#include "Logging/LogMacros.h" -#include "CoreGlobals.h" -#include "Misc/Parse.h" -#include "Misc/CommandLine.h" -#include "Misc/Paths.h" -#include "Misc/ScopeExit.h" -#include "Modules/ModuleManager.h" -#include "Misc/CompilationResult.h" -#include "UnrealHeaderToolGlobals.h" -#include "RequiredProgramMainCPPInclude.h" -#include "ClassMaps.h" -#include "IScriptGeneratorPluginInterface.h" -#include "ProfilingDebugging/ScopedTimers.h" -#include "HeaderParser.h" -#include "Features/IModularFeatures.h" -#include "Manifest.h" -#include "UnrealTypeDefinitionInfo.h" -#include "StringUtils.h" -#include "FileLineException.h" -#include "NativeClassExporter.h" - -IMPLEMENT_APPLICATION(UnrealHeaderTool, "UnrealHeaderTool"); - -DEFINE_LOG_CATEGORY(LogCompile); - -bool GUHTWarningLogged = false; -bool GUHTErrorLogged = false; - -// UHTLite TODO: Refactor globals! -extern FManifest GManifest; -extern ECompilationResult::Type GCompilationResult; -extern FGraphEventArray GAsyncFileTasks; -extern double GMacroizeTime; -extern TArray ChangeMessages; -extern bool bWriteContents; -extern bool bVerifyContents; - -static void PerformSimplifiedClassParse(UPackage* InParent, const TCHAR* FileName, const TCHAR* Buffer, struct FPerHeaderData& PerHeaderData, FClassDeclarations& ClassDeclarations); -static void ProcessInitialClassParse(FPerHeaderData& PerHeaderData, FTypeDefinitionInfoMap& TypeDefinitionInfoMap); - -#pragma region Plugins -/** Get all script plugins based on ini setting */ -void GetScriptPlugins(TArray& ScriptPlugins) -{ - FScopedDurationTimer PluginTimeTracker(GPluginOverheadTime); - - ScriptPlugins = IModularFeatures::Get().GetModularFeatureImplementations(TEXT("ScriptGenerator")); - UE_LOG(LogCompile, Log, TEXT("Found %d script generator plugins."), ScriptPlugins.Num()); - - // Check if we can use these plugins and initialize them - for (int32 PluginIndex = ScriptPlugins.Num() - 1; PluginIndex >= 0; --PluginIndex) - { - IScriptGeneratorPluginInterface* ScriptGenerator = ScriptPlugins[PluginIndex]; - bool bSupportedPlugin = ScriptGenerator->SupportsTarget(GManifest.TargetName); - if (bSupportedPlugin) - { - // Find the right output directory for this plugin base on its target (Engine-side) plugin name. - FString GeneratedCodeModuleName = ScriptGenerator->GetGeneratedCodeModuleName(); - const FManifestModule* GeneratedCodeModule = NULL; - FString OutputDirectory; - FString IncludeBase; - for (const FManifestModule& Module : GManifest.Modules) - { - if (Module.Name == GeneratedCodeModuleName) - { - GeneratedCodeModule = &Module; - } - } - if (GeneratedCodeModule) - { - UE_LOG(LogCompile, Log, TEXT("Initializing script generator \'%s\'"), *ScriptGenerator->GetGeneratorName()); - ScriptGenerator->Initialize(GManifest.RootLocalPath, GManifest.RootBuildPath, GeneratedCodeModule->GeneratedIncludeDirectory, GeneratedCodeModule->IncludeBase); - } - else - { - // Can't use this plugin - UE_LOG(LogCompile, Log, TEXT("Unable to determine output directory for %s. Cannot export script glue with \'%s\'"), *GeneratedCodeModuleName, *ScriptGenerator->GetGeneratorName()); - bSupportedPlugin = false; - } - } - if (!bSupportedPlugin) - { - UE_LOG(LogCompile, Log, TEXT("Script generator \'%s\' not supported for target: %s"), *ScriptGenerator->GetGeneratorName(), *GManifest.TargetName); - ScriptPlugins.RemoveAt(PluginIndex); - } - } -} -#pragma endregion Plugins - -/** - * Tries to resolve super classes for classes defined in the given - * module. - * - * @param Package Modules package. - */ -void ResolveSuperClasses(UPackage* Package, const FTypeDefinitionInfoMap& TypeDefinitionInfoMap) -{ - TArray Objects; - GetObjectsWithPackage(Package, Objects); - - for (UObject* Object : Objects) - { - if (!Object->IsA() || Object->HasAnyFlags(RF_ClassDefaultObject)) - { - continue; - } - - UClass* DefinedClass = Cast(Object); - - if (DefinedClass->HasAnyClassFlags(CLASS_Intrinsic | CLASS_NoExport)) - { - continue; - } - - const FSimplifiedParsingClassInfo& ParsingInfo = TypeDefinitionInfoMap[DefinedClass]->GetUnrealSourceFile().GetDefinedClassParsingInfo(DefinedClass); - - const FString& BaseClassName = ParsingInfo.GetBaseClassName(); - const FString& BaseClassNameStripped = GetClassNameWithPrefixRemoved(BaseClassName); - - if (!BaseClassNameStripped.IsEmpty() && !DefinedClass->GetSuperClass()) - { - UClass* FoundBaseClass = FindObject(Package, *BaseClassNameStripped); - - if (FoundBaseClass == nullptr) - { - FoundBaseClass = FindObject(ANY_PACKAGE, *BaseClassNameStripped); - } - - if (FoundBaseClass == nullptr) - { - // Don't know its parent class. Raise error. - FError::Throwf(TEXT("Couldn't find parent type for '%s' named '%s' in current module (Package: %s) or any other module parsed so far."), *DefinedClass->GetName(), *BaseClassName, *GetNameSafe(Package)); - } - - DefinedClass->SetSuperStruct(FoundBaseClass); - DefinedClass->ClassCastFlags |= FoundBaseClass->ClassCastFlags; - } - } -} - -ECompilationResult::Type PreparseModules(const FString& ModuleInfoPath, int32& NumFailures, FUnrealSourceFiles& UnrealSourceFilesMap, FTypeDefinitionInfoMap& TypeDefinitionInfoMap, FPublicSourceFileSet& PublicSourceFileSet, TMap& PackageToManifestModuleMap, FClassDeclarations& ClassDeclarations) -{ - // Three passes. 1) Public 'Classes' headers (legacy) 2) Public headers 3) Private headers - enum EHeaderFolderTypes - { - PublicClassesHeaders = 0, - PublicHeaders = 1, - PrivateHeaders, - - FolderType_Count - }; - - ECompilationResult::Type Result = ECompilationResult::Succeeded; - -#if !PLATFORM_EXCEPTIONS_DISABLED - FGraphEventArray ExceptionTasks; - - auto LogException = [&Result, &NumFailures, &ExceptionTasks](FString&& Filename, int32 Line, const FString& Message) - { - auto LogExceptionTask = [&Result, &NumFailures, Filename = MoveTemp(Filename), Line, Message]() - { - TGuardValue DisableLogTimes(GPrintLogTimes, ELogTimes::None); - - FString FormattedErrorMessage = FString::Printf(TEXT("%s(%d): Error: %s\r\n"), *Filename, Line, *Message); - Result = ECompilationResult::OtherCompilationError; - - UE_LOG(LogCompile, Log, TEXT("%s"), *FormattedErrorMessage); - GWarn->Log(ELogVerbosity::Error, FormattedErrorMessage); - - ++NumFailures; - }; - - if (IsInGameThread()) - { - LogExceptionTask(); - } - else - { - FGraphEventRef EventRef = FFunctionGraphTask::CreateAndDispatchWhenReady(MoveTemp(LogExceptionTask), TStatId(), nullptr, ENamedThreads::GameThread); - - static FCriticalSection ExceptionCS; - FScopeLock Lock(&ExceptionCS); - ExceptionTasks.Add(EventRef); - } - }; -#endif - - for (FManifestModule& Module : GManifest.Modules) - { - if (Result != ECompilationResult::Succeeded) - { - break; - } - - // Force regeneration of all subsequent modules, otherwise data will get corrupted. - Module.ForceRegeneration(); - - UPackage* Package = Cast(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(*Module.LongPackageName), false, false)); - if (Package == NULL) - { - Package = CreatePackage(NULL, *Module.LongPackageName); - } - // Set some package flags for indicating that this package contains script - // NOTE: We do this even if we didn't have to create the package, because CoreUObject is compiled into UnrealHeaderTool and we still - // want to make sure our flags get set - Package->SetPackageFlags(PKG_ContainsScript | PKG_Compiling); - Package->ClearPackageFlags(PKG_ClientOptional | PKG_ServerSideOnly); - - if (Module.OverrideModuleType == EPackageOverrideType::None) - { - switch (Module.ModuleType) - { - case EBuildModuleType::GameEditor: - case EBuildModuleType::EngineEditor: - Package->SetPackageFlags(PKG_EditorOnly); - break; - - case EBuildModuleType::GameDeveloper: - case EBuildModuleType::EngineDeveloper: - Package->SetPackageFlags(PKG_Developer); - break; - - case EBuildModuleType::GameUncooked: - case EBuildModuleType::EngineUncooked: - Package->SetPackageFlags(PKG_UncookedOnly); - break; - } - } - else - { - // If the user has specified this module to have another package flag, then OR it on - switch (Module.OverrideModuleType) - { - case EPackageOverrideType::EditorOnly: - Package->SetPackageFlags(PKG_EditorOnly); - break; - - case EPackageOverrideType::EngineDeveloper: - case EPackageOverrideType::GameDeveloper: - Package->SetPackageFlags(PKG_Developer); - break; - - case EPackageOverrideType::EngineUncookedOnly: - case EPackageOverrideType::GameUncookedOnly: - Package->SetPackageFlags(PKG_UncookedOnly); - break; - } - } - - // Add new module or overwrite whatever we had loaded, that data is obsolete. - PackageToManifestModuleMap.Add(Package, &Module); - - double ThisModulePreparseTime = 0.0; - int32 NumHeadersPreparsed = 0; - FDurationTimer ThisModuleTimer(ThisModulePreparseTime); - ThisModuleTimer.Start(); - - // Pre-parse the headers - for (int32 PassIndex = 0; PassIndex < FolderType_Count && Result == ECompilationResult::Succeeded; ++PassIndex) - { - EHeaderFolderTypes CurrentlyProcessing = (EHeaderFolderTypes)PassIndex; - - // We'll make an ordered list of all UObject headers we care about. - // @todo uht: Ideally 'dependson' would not be allowed from public -> private, or NOT at all for new style headers - const TArray& UObjectHeaders = - (CurrentlyProcessing == PublicClassesHeaders) ? Module.PublicUObjectClassesHeaders : - (CurrentlyProcessing == PublicHeaders) ? Module.PublicUObjectHeaders : - Module.PrivateUObjectHeaders; - if (!UObjectHeaders.Num()) - { - continue; - } - - NumHeadersPreparsed += UObjectHeaders.Num(); - - TArray HeaderFiles; - HeaderFiles.SetNum(UObjectHeaders.Num()); - - { - SCOPE_SECONDS_COUNTER_UHT(LoadHeaderContentFromFile); -#if UHT_USE_PARALLEL_FOR - ParallelFor(UObjectHeaders.Num(), [&](int32 Index) -#else - for (int Index = 0; Index < UObjectHeaders.Num(); Index++) -#endif - { - const FString& RawFilename = UObjectHeaders[Index]; - -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - const FString FullFilename = FPaths::ConvertRelativePathToFull(ModuleInfoPath, RawFilename); - - if (!FFileHelper::LoadFileToString(HeaderFiles[Index], *FullFilename)) - { - FError::Throwf(TEXT("UnrealHeaderTool was unable to load source file '%s'"), *FullFilename); - } - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch (TCHAR* ErrorMsg) - { - FString AbsFilename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*RawFilename); - LogException(MoveTemp(AbsFilename), 1, ErrorMsg); - } -#endif - } -#if UHT_USE_PARALLEL_FOR - ); -#endif - } - -#if !PLATFORM_EXCEPTIONS_DISABLED - FTaskGraphInterface::Get().WaitUntilTasksComplete(ExceptionTasks); -#endif - - if (Result != ECompilationResult::Succeeded) - { - continue; - } - - TArray PerHeaderData; - PerHeaderData.SetNum(UObjectHeaders.Num()); - -#if UHT_USE_PARALLEL_FOR - ParallelFor(UObjectHeaders.Num(), [&](int32 Index) -#else - for (int Index = 0; Index < UObjectHeaders.Num(); Index++) -#endif - { - const FString& RawFilename = UObjectHeaders[Index]; - -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - PerformSimplifiedClassParse(Package, *RawFilename, *HeaderFiles[Index], PerHeaderData[Index], ClassDeclarations); - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch (const FFileLineException& Ex) - { - FString AbsFilename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*Ex.Filename); - LogException(MoveTemp(AbsFilename), Ex.Line, Ex.Message); - } - catch (TCHAR* ErrorMsg) - { - FString AbsFilename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*RawFilename); - LogException(MoveTemp(AbsFilename), 1, ErrorMsg); - } -#endif - } -#if UHT_USE_PARALLEL_FOR - ); -#endif - -#if !PLATFORM_EXCEPTIONS_DISABLED - FTaskGraphInterface::Get().WaitUntilTasksComplete(ExceptionTasks); -#endif - - if (Result != ECompilationResult::Succeeded) - { - continue; - } - - for (int32 Index = 0; Index < UObjectHeaders.Num(); ++Index) - { - const FString& RawFilename = UObjectHeaders[Index]; - -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - // Import class. - const FString FullFilename = FPaths::ConvertRelativePathToFull(ModuleInfoPath, RawFilename); - - ProcessInitialClassParse(PerHeaderData[Index], TypeDefinitionInfoMap); - TSharedRef UnrealSourceFile = PerHeaderData[Index].UnrealSourceFile.ToSharedRef(); - FUnrealSourceFile* UnrealSourceFilePtr = &UnrealSourceFile.Get(); - UnrealSourceFilesMap.Add(FPaths::GetCleanFilename(RawFilename), UnrealSourceFile); - - if (CurrentlyProcessing == PublicClassesHeaders) - { - PublicSourceFileSet.Add(UnrealSourceFilePtr); - } - - // Save metadata for the class path, both for it's include path and relative to the module base directory - if (FullFilename.StartsWith(Module.BaseDirectory)) - { - // Get the path relative to the module directory - const TCHAR* ModuleRelativePath = *FullFilename + Module.BaseDirectory.Len(); - - UnrealSourceFile->SetModuleRelativePath(ModuleRelativePath); - - // Calculate the include path - const TCHAR* IncludePath = ModuleRelativePath; - - // Walk over the first potential slash - if (*IncludePath == TEXT('/')) - { - IncludePath++; - } - - // Does this module path start with a known include path location? If so, we can cut that part out of the include path - static const TCHAR PublicFolderName[] = TEXT("Public/"); - static const TCHAR PrivateFolderName[] = TEXT("Private/"); - static const TCHAR ClassesFolderName[] = TEXT("Classes/"); - if (FCString::Strnicmp(IncludePath, PublicFolderName, UE_ARRAY_COUNT(PublicFolderName) - 1) == 0) - { - IncludePath += (UE_ARRAY_COUNT(PublicFolderName) - 1); - } - else if (FCString::Strnicmp(IncludePath, PrivateFolderName, UE_ARRAY_COUNT(PrivateFolderName) - 1) == 0) - { - IncludePath += (UE_ARRAY_COUNT(PrivateFolderName) - 1); - } - else if (FCString::Strnicmp(IncludePath, ClassesFolderName, UE_ARRAY_COUNT(ClassesFolderName) - 1) == 0) - { - IncludePath += (UE_ARRAY_COUNT(ClassesFolderName) - 1); - } - - // Add the include path - if (*IncludePath != 0) - { - UnrealSourceFile->SetIncludePath(MoveTemp(IncludePath)); - } - } - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch (const FFileLineException& Ex) - { - FString AbsFilename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*Ex.Filename); - LogException(MoveTemp(AbsFilename), Ex.Line, Ex.Message); - } - catch (TCHAR* ErrorMsg) - { - FString AbsFilename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*RawFilename); - LogException(MoveTemp(AbsFilename), 1, ErrorMsg); - } -#endif - } - if (Result == ECompilationResult::Succeeded && NumFailures != 0) - { - Result = ECompilationResult::OtherCompilationError; - } - } - - // Don't resolve superclasses for module when loading from makefile. - // Data is only partially loaded at this point. -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - ResolveSuperClasses(Package, TypeDefinitionInfoMap); - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch (TCHAR* ErrorMsg) - { - TGuardValue DisableLogTimes(GPrintLogTimes, ELogTimes::None); - - FString FormattedErrorMessage = FString::Printf(TEXT("Error: %s\r\n"), ErrorMsg); - - Result = GCompilationResult; - - UE_LOG(LogCompile, Log, TEXT("%s"), *FormattedErrorMessage); - GWarn->Log(ELogVerbosity::Error, FormattedErrorMessage); - - ++NumFailures; - } -#endif - - ThisModuleTimer.Stop(); - UE_LOG(LogCompile, Log, TEXT("Preparsed module %s containing %i files(s) in %.2f secs."), *Module.LongPackageName, NumHeadersPreparsed, ThisModulePreparseTime); - } - - return Result; -} - -ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename) -{ - double MainTime = 0.0; - FDurationTimer MainTimer(MainTime); - MainTimer.Start(); - - check(GIsUCCMakeStandaloneHeaderGenerator); - ECompilationResult::Type Result = ECompilationResult::Succeeded; - - FString ModuleInfoPath = FPaths::GetPath(ModuleInfoFilename); - - // Load the manifest file, giving a list of all modules to be processed, pre-sorted by dependency ordering -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - GManifest = FManifest::LoadFromFile(ModuleInfoFilename); - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch (const TCHAR* Ex) - { - UE_LOG(LogCompile, Error, TEXT("Failed to load manifest file '%s': %s"), *ModuleInfoFilename, Ex); - return GCompilationResult; - } -#endif - - // Counters. - int32 NumFailures = 0; - double TotalModulePreparseTime = 0.0; - double TotalParseAndCodegenTime = 0.0; - - // UHTLite NOTE: Used in PreparseModules() and UnrealHeaderTool_Main(). - // UHTLite NOTE: Written to in FHeaderParser::CompileStructDeclaration() and FHeaderParser::CompileEnum(). - // UHTLite NOTE: Needs a mutex? - FTypeDefinitionInfoMap TypeDefinitionInfoMap; - - // UHTLite NOTE: Data is written once in PreparseModules() and is then read-only. - FUnrealSourceFiles UnrealSourceFilesMap; - FPublicSourceFileSet PublicSourceFileSet; - TMap PackageToManifestModuleMap; - - // UHTLite NOTE: Classes are added in PreparseModules(), but class flags are modified during header parsing! - FClassDeclarations ClassDeclarations; - - // UHTLite NOTE: This data is cross-module! It is read-only during code generation. - TMap EnumUnderlyingTypes; - FRWLock EnumUnderlyingTypesLock; - TMap ClassSerializerMap; - FRWLock ClassSerializerMapLock; - - { - FDurationTimer TotalModulePreparseTimer(TotalModulePreparseTime); - TotalModulePreparseTimer.Start(); - Result = PreparseModules(ModuleInfoPath, NumFailures, UnrealSourceFilesMap, TypeDefinitionInfoMap, PublicSourceFileSet, PackageToManifestModuleMap, ClassDeclarations); - TotalModulePreparseTimer.Stop(); - } - // Do the actual parse of the headers and generate for them - if (Result == ECompilationResult::Succeeded) - { - FScopedDurationTimer ParseAndCodeGenTimer(TotalParseAndCodegenTime); - - TMap> ClassesByPackageMap; - ClassesByPackageMap.Reserve(GManifest.Modules.Num()); - - // Verify that all script declared superclasses exist. - for (UClass* ScriptClass : TObjectRange()) - { - ClassesByPackageMap.FindOrAdd(ScriptClass->GetOutermost()).Add(ScriptClass); - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - const UClass* ScriptSuperClass = ScriptClass->GetSuperClass(); - - if (ScriptSuperClass && !ScriptSuperClass->HasAnyClassFlags(CLASS_Intrinsic) && TypeDefinitionInfoMap.Contains(ScriptClass) && !TypeDefinitionInfoMap.Contains(ScriptSuperClass)) - { - class FSuperClassContextSupplier : public FContextSupplier - { - public: - FSuperClassContextSupplier(const UClass* Class) - : DefinitionInfo(TypeDefinitionInfoMap[Class]) - { } - - virtual FString GetContext() override - { - FString Filename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*DefinitionInfo->GetUnrealSourceFile().GetFilename()); - int32 LineNumber = DefinitionInfo->GetLineNumber(); - return FString::Printf(TEXT("%s(%i)"), *Filename, LineNumber); - } - private: - TSharedRef DefinitionInfo; - } ContextSupplier(ScriptClass); - - FContextSupplier* OldContext = GWarn->GetContext(); - - TGuardValue DisableLogTimes(GPrintLogTimes, ELogTimes::None); - - GWarn->SetContext(&ContextSupplier); - GWarn->Log(ELogVerbosity::Error, FString::Printf(TEXT("Error: Superclass %s of class %s not found"), *ScriptSuperClass->GetName(), *ScriptClass->GetName())); - GWarn->SetContext(OldContext); - - Result = ECompilationResult::OtherCompilationError; - ++NumFailures; - } - */ - } - - if (Result == ECompilationResult::Succeeded) - { -#pragma region Plugins - TArray ScriptPlugins; - // Can only export scripts for game targets - if (GManifest.IsGameTarget) - { - GetScriptPlugins(ScriptPlugins); - } -#pragma endregion Plugins - - // UHTLite NOTE: TypeDefinitionInfoMap needs a mutex before this loop can be threaded! - for (const FManifestModule& Module : GManifest.Modules) - { - if (UPackage* Package = Cast(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(*Module.LongPackageName), false, false))) - { - FClasses AllClasses(ClassesByPackageMap.Find(Package)); - AllClasses.Validate(); - - Result = FHeaderParser::ParseAllHeadersInside(AllClasses, - GWarn, - Package, - Module, -#pragma region Plugins - ScriptPlugins, -#pragma endregion Plugins - UnrealSourceFilesMap, - TypeDefinitionInfoMap, - PublicSourceFileSet, - PackageToManifestModuleMap, - ClassDeclarations, - EnumUnderlyingTypes, - EnumUnderlyingTypesLock, - ClassSerializerMap, - ClassSerializerMapLock); - - if (Result != ECompilationResult::Succeeded) - { - ++NumFailures; - break; - } - } - } - -#pragma region Plugins - { - FScopedDurationTimer PluginTimeTracker(GPluginOverheadTime); - for (IScriptGeneratorPluginInterface* ScriptGenerator : ScriptPlugins) - { - ScriptGenerator->FinishExport(); - } - } - - // Get a list of external dependencies from each enabled plugin - FString ExternalDependencies; - for (IScriptGeneratorPluginInterface* ScriptPlugin : ScriptPlugins) - { - TArray PluginExternalDependencies; - ScriptPlugin->GetExternalDependencies(PluginExternalDependencies); - - for (const FString& PluginExternalDependency : PluginExternalDependencies) - { - ExternalDependencies += PluginExternalDependency + LINE_TERMINATOR; - } - } - - FFileHelper::SaveStringToFile(ExternalDependencies, *GManifest.ExternalDependenciesFile); -#pragma endregion Plugins - } - } - - // Avoid TArray slack for meta data. - GScriptHelper.Shrink(); - - // Finish all async file tasks before stopping the clock - FTaskGraphInterface::Get().WaitUntilTasksComplete(GAsyncFileTasks); - - MainTimer.Stop(); - - UE_LOG(LogCompile, Log, TEXT("Preparsing %i modules took %.2f seconds"), GManifest.Modules.Num(), TotalModulePreparseTime); - UE_LOG(LogCompile, Log, TEXT("Parsing took %.2f seconds"), TotalParseAndCodegenTime - GHeaderCodeGenTime); - UE_LOG(LogCompile, Log, TEXT("Code generation took %.2f seconds"), GHeaderCodeGenTime); - UE_LOG(LogCompile, Log, TEXT("ScriptPlugin overhead was %.2f seconds"), GPluginOverheadTime); - UE_LOG(LogCompile, Log, TEXT("Macroize time was %.2f seconds"), GMacroizeTime); - - FUnrealHeaderToolStats& Stats = FUnrealHeaderToolStats::Get(); - for (const TPair& Pair : Stats.Counters) - { - FString CounterName = Pair.Key.ToString(); - UE_LOG(LogCompile, Log, TEXT("%s timer was %.3f seconds"), *CounterName, Pair.Value); - } - - UE_LOG(LogCompile, Log, TEXT("Total time was %.2f seconds"), MainTime); - - if (bWriteContents) - { - UE_LOG(LogCompile, Log, TEXT("********************************* Wrote reference generated code to ReferenceGeneratedCode.")); - } - else if (bVerifyContents) - { - UE_LOG(LogCompile, Log, TEXT("********************************* Wrote generated code to VerifyGeneratedCode and compared to ReferenceGeneratedCode")); - for (FString& Msg : ChangeMessages) - { - UE_LOG(LogCompile, Error, TEXT("%s"), *Msg); - } - TArray RefFileNames; - IFileManager::Get().FindFiles(RefFileNames, *(FPaths::ProjectSavedDir() / TEXT("ReferenceGeneratedCode/*.*")), true, false); - TArray VerFileNames; - IFileManager::Get().FindFiles(VerFileNames, *(FPaths::ProjectSavedDir() / TEXT("VerifyGeneratedCode/*.*")), true, false); - if (RefFileNames.Num() != VerFileNames.Num()) - { - UE_LOG(LogCompile, Error, TEXT("Number of generated files mismatch ref=%d, ver=%d"), RefFileNames.Num(), VerFileNames.Num()); - } - } - - RequestEngineExit(TEXT("UnrealHeaderTool finished")); - - if (Result != ECompilationResult::Succeeded || NumFailures > 0) - { - return ECompilationResult::OtherCompilationError; - } - - return Result; -} - -UClass* ProcessParsedClass(bool bClassIsAnInterface, TArray& DependentOn, const FString& ClassName, const FString& BaseClassName, UObject* InParent, EObjectFlags Flags) -{ - FString ClassNameStripped = GetClassNameWithPrefixRemoved(*ClassName); - - // All classes must start with a valid unreal prefix - if (!FHeaderParser::ClassNameHasValidPrefix(ClassName, ClassNameStripped)) - { - FError::Throwf(TEXT("Invalid class name '%s'. The class name must have an appropriate prefix added (A for Actors, U for other classes)."), *ClassName); - } - - if (FHeaderParser::IsReservedTypeName(ClassNameStripped)) - { - FError::Throwf(TEXT("Invalid class name '%s'. Cannot use a reserved name ('%s')."), *ClassName, *ClassNameStripped); - } - - // Ensure the base class has any valid prefix and exists as a valid class. Checking for the 'correct' prefix will occur during compilation - FString BaseClassNameStripped; - if (!BaseClassName.IsEmpty()) - { - BaseClassNameStripped = GetClassNameWithPrefixRemoved(BaseClassName); - if (!FHeaderParser::ClassNameHasValidPrefix(BaseClassName, BaseClassNameStripped)) - { - FError::Throwf(TEXT("No prefix or invalid identifier for base class %s.\nClass names must match Unreal prefix specifications (e.g., \"UObject\" or \"AActor\")"), *BaseClassName); - } - } - - //UE_LOG(LogCompile, Log, TEXT("Class: %s extends %s"),*ClassName,*BaseClassName); - // Handle failure and non-class headers. - if (BaseClassName.IsEmpty() && (ClassName != TEXT("UObject"))) - { - FError::Throwf(TEXT("Class '%s' must inherit UObject or a UObject-derived class"), *ClassName); - } - - if (ClassName == BaseClassName) - { - FError::Throwf(TEXT("Class '%s' cannot inherit from itself"), *ClassName); - } - - // In case the file system and the class disagree on the case of the - // class name replace the fname with the one from the script class file - // This is needed because not all source control systems respect the - // original filename's case - FName ClassNameReplace(*ClassName, FNAME_Replace_Not_Safe_For_Threading); - - // Use stripped class name for processing and replace as we did above - FName ClassNameStrippedReplace(*ClassNameStripped, FNAME_Replace_Not_Safe_For_Threading); - - UClass* ResultClass = FindObject(InParent, *ClassNameStripped); - - // if we aren't generating headers, then we shouldn't set misaligned object, since it won't get cleared - - const static bool bVerboseOutput = FParse::Param(FCommandLine::Get(), TEXT("VERBOSE")); - - if (ResultClass == nullptr || !ResultClass->IsNative()) - { - // detect if the same class name is used in multiple packages - if (ResultClass == nullptr) - { - UClass* ConflictingClass = FindObject(ANY_PACKAGE, *ClassNameStripped, true); - if (ConflictingClass != nullptr) - { - UE_LOG_WARNING_UHT(TEXT("Duplicate class name: %s also exists in file %s"), *ClassName, *ConflictingClass->GetOutermost()->GetName()); - } - } - - // Create new class. - ResultClass = new(EC_InternalUseOnlyConstructor, InParent, *ClassNameStripped, Flags) UClass(FObjectInitializer(), nullptr); - - // add CLASS_Interface flag if the class is an interface - // NOTE: at this pre-parsing/importing stage, we cannot know if our super class is an interface or not, - // we leave the validation to the main header parser - if (bClassIsAnInterface) - { - ResultClass->ClassFlags |= CLASS_Interface; - } - - if (bVerboseOutput) - { - UE_LOG(LogCompile, Log, TEXT("Imported: %s"), *ResultClass->GetFullName()); - } - } - - if (bVerboseOutput) - { - for (const FHeaderProvider& Dependency : DependentOn) - { - UE_LOG(LogCompile, Log, TEXT("\tAdding %s as a dependency"), *Dependency.ToString()); - } - } - - return ResultClass; -} - -void PerformSimplifiedClassParse(UPackage* InParent, const TCHAR* FileName, const TCHAR* Buffer, FPerHeaderData& PerHeaderData, FClassDeclarations& ClassDeclarations) -{ - // Parse the header to extract the information needed - FUHTStringBuilder ClassHeaderTextStrippedOfCppText; - - FHeaderParser::SimplifiedClassParse(FileName, Buffer, /*out*/ PerHeaderData.ParsedClassArray, /*out*/ PerHeaderData.DependsOn, ClassHeaderTextStrippedOfCppText, ClassDeclarations); - - FUnrealSourceFile* UnrealSourceFilePtr = new FUnrealSourceFile(InParent, FileName, MoveTemp(ClassHeaderTextStrippedOfCppText)); - PerHeaderData.UnrealSourceFile = MakeShareable(UnrealSourceFilePtr); -} - -void ProcessInitialClassParse(FPerHeaderData& PerHeaderData, FTypeDefinitionInfoMap& TypeDefinitionInfoMap) -{ - TSharedRef UnrealSourceFile = PerHeaderData.UnrealSourceFile.ToSharedRef(); - UPackage* InParent = UnrealSourceFile->GetPackage(); - for (FSimplifiedParsingClassInfo& ParsedClassInfo : PerHeaderData.ParsedClassArray) - { - UClass* ResultClass = ProcessParsedClass(ParsedClassInfo.IsInterface(), PerHeaderData.DependsOn, ParsedClassInfo.GetClassName(), ParsedClassInfo.GetBaseClassName(), InParent, RF_Public | RF_Standalone); - - // UHTLite NOTE: Not required for code-gen. Will be refactored later. - /* - GStructToSourceLine.Add(ResultClass, MakeTuple(UnrealSourceFile, ParsedClassInfo.GetClassDefLine())); - */ - - FScope::AddTypeScope(ResultClass, &UnrealSourceFile->GetScope().Get()); - - TypeDefinitionInfoMap.Add(ResultClass, MakeShared(UnrealSourceFile.Get(), ParsedClassInfo.GetClassDefLine())); - UnrealSourceFile->AddDefinedClass(ResultClass, MoveTemp(ParsedClassInfo)); - } - - for (FHeaderProvider& DependsOnElement : PerHeaderData.DependsOn) - { - UnrealSourceFile->GetIncludes().AddUnique(DependsOnElement); - } -} - -/** - * Application entry point - * - * @param ArgC Command-line argument count - * @param ArgV Argument strings - */ -INT32_MAIN_INT32_ARGC_TCHAR_ARGV() -{ - FTaskTagScope Scope(ETaskTag::EGameThread); - FString CmdLine; - - for (int32 Arg = 0; Arg < ArgC; Arg++) - { - FString LocalArg = ArgV[Arg]; - if (LocalArg.Contains(TEXT(" "), ESearchCase::CaseSensitive)) - { - CmdLine += TEXT("\""); - CmdLine += LocalArg; - CmdLine += TEXT("\""); - } - else - { - CmdLine += LocalArg; - } - - if (Arg + 1 < ArgC) - { - CmdLine += TEXT(" "); - } - } - - FString ShortCmdLine = FCommandLine::RemoveExeName(*CmdLine); - ShortCmdLine.TrimStartInline(); - - // Get game name from the commandline. It will later be used to load the correct ini files. - FString ModuleInfoFilename; - if (ShortCmdLine.Len() && **ShortCmdLine != TEXT('-')) - { - const TCHAR* CmdLinePtr = *ShortCmdLine; - - // Parse the game name or project filename. UHT reads the list of plugins from there in case one of the plugins is UHT plugin. - FString GameName = FParse::Token(CmdLinePtr, false); - - // This parameter is the absolute path to the file which contains information about the modules - // that UHT needs to generate code for. - ModuleInfoFilename = FParse::Token(CmdLinePtr, false ); - } - -#if !NO_LOGGING - const static bool bVerbose = FParse::Param(*CmdLine,TEXT("VERBOSE")); - if (bVerbose) - { - UE_SET_LOG_VERBOSITY(LogCompile, Verbose); - } -#endif - - // Make sure the engine is properly cleaned up whenever we exit this function - ON_SCOPE_EXIT - { - FEngineLoop::AppPreExit(); - FEngineLoop::AppExit(); - }; - - GIsUCCMakeStandaloneHeaderGenerator = true; - if (GEngineLoop.PreInit(*ShortCmdLine) != 0) - { - UE_LOG(LogCompile, Error, TEXT("Failed to initialize the engine (PreInit failed).")); - return ECompilationResult::CrashOrAssert; - } - - // Log full command line for UHT as UBT overrides LogInit verbosity settings - UE_LOG(LogCompile, Log, TEXT("UHT Command Line: %s"), *CmdLine); - - if (ModuleInfoFilename.IsEmpty()) - { - if (!FPlatformMisc::IsDebuggerPresent()) - { - UE_LOG(LogCompile, Error, TEXT( "Missing module info filename on command line" )); - return ECompilationResult::OtherCompilationError; - } - - // If we have a debugger, let's use a pre-existing manifest file to streamline debugging - // without the user having to faff around trying to get a UBT-generated manifest - ModuleInfoFilename = FPaths::ConvertRelativePathToFull(FPlatformProcess::BaseDir(), TEXT("../../Source/Programs/UnrealHeaderTool/Resources/UHTDebugging.manifest")); - } - - ECompilationResult::Type Result = UnrealHeaderTool_Main(ModuleInfoFilename); - - if (Result == ECompilationResult::Succeeded && (GUHTErrorLogged || (GUHTWarningLogged && GWarn->TreatWarningsAsErrors))) - { - Result = ECompilationResult::OtherCompilationError; - } - - return Result; -} diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolStats.cpp b/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolStats.cpp deleted file mode 100644 index 3ed583c76766..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolStats.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "UnrealHeaderToolStats.h" - -FUnrealHeaderToolStats GUnrealHeaderToolStats; - -FUnrealHeaderToolStats& FUnrealHeaderToolStats::Get() -{ - return GUnrealHeaderToolStats; -} - -double& FUnrealHeaderToolStats::GetCounter(const FName& Key) -{ - return GUnrealHeaderToolStats.Counters.FindOrAdd(Key); -} diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolStats.h b/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolStats.h deleted file mode 100644 index ad6185f696a7..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealHeaderToolStats.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Stats/StatsMisc.h" - -struct FUnrealHeaderToolStats -{ - TMap Counters; - - static FUnrealHeaderToolStats& Get(); - static double& GetCounter(const FName& Key); -}; - -#if STATS -#define SCOPE_SECONDS_COUNTER_UHT(Name) \ - FSimpleScopeSecondsCounter ANONYMOUS_VARIABLE(Name)(FUnrealHeaderToolStats::GetCounter(TEXT(#Name))) -#else -#define SCOPE_SECONDS_COUNTER_UHT(Name) -#endif diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealSourceFile.cpp b/Engine/Source/Programs/UHTLite/Private/UnrealSourceFile.cpp deleted file mode 100644 index bddc3a8f6911..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealSourceFile.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "UnrealSourceFile.h" -#include "UnrealHeaderTool.h" -#include "Misc/PackageName.h" -#include "HeaderParser.h" - -void FUnrealSourceFile::AddDefinedClass(UClass* Class, FSimplifiedParsingClassInfo&& ParsingInfo) -{ - DefinedClasses.Add(Class, MoveTemp(ParsingInfo)); -} - -const FString& FUnrealSourceFile::GetFileId() const -{ - if (FileId.Len() == 0) - { - FString StdFilename = Filename; - - FPaths::MakeStandardFilename(StdFilename); - - bool bRelativePath = FPaths::IsRelative(StdFilename); - - if (!bRelativePath) - { - // If path is still absolute that means MakeStandardFilename has failed - // In this case make it relative to the current project. - bRelativePath = FPaths::MakePathRelativeTo(StdFilename, *FPaths::GetPath(FPaths::GetProjectFilePath())); - } - - // If the path has passed either MakeStandardFilename or MakePathRelativeTo it should be using internal path seperators - if (bRelativePath) - { - // Remove any preceding parent directory paths - while (StdFilename.RemoveFromStart(TEXT("../"))); - } - - FStringOutputDevice Out; - - for (TCHAR Char : StdFilename) - { - if (FChar::IsAlnum(Char)) - { - Out.AppendChar(Char); - } - else - { - Out.AppendChar('_'); - } - } - - FileId = MoveTemp(Out); - } - - return FileId; -} - -const FString& FUnrealSourceFile::GetStrippedFilename() const -{ - if (StrippedFilename.Len() == 0) - { - StrippedFilename = FPaths::GetBaseFilename(Filename); - } - - return StrippedFilename; -} - -FString FUnrealSourceFile::GetGeneratedMacroName(FClassMetaData* ClassData, const TCHAR* Suffix) const -{ - return GetGeneratedMacroName(ClassData->GetGeneratedBodyLine(), Suffix); -} - -FString FUnrealSourceFile::GetGeneratedMacroName(int32 LineNumber, const TCHAR* Suffix) const -{ - if (Suffix != nullptr) - { - return FString::Printf(TEXT("%s_%d%s"), *GetFileId(), LineNumber, Suffix); - } - - return FString::Printf(TEXT("%s_%d"), *GetFileId(), LineNumber); -} - -FString FUnrealSourceFile::GetGeneratedBodyMacroName(int32 LineNumber, bool bLegacy) const -{ - return GetGeneratedMacroName(LineNumber, *FString::Printf(TEXT("%s%s"), TEXT("_GENERATED_BODY"), bLegacy ? TEXT("_LEGACY") : TEXT(""))); -} - -void FUnrealSourceFile::SetGeneratedFilename(FString&& InGeneratedFilename) -{ - GeneratedFilename = MoveTemp(InGeneratedFilename); -} - -void FUnrealSourceFile::SetHasChanged(bool bInHasChanged) -{ - bHasChanged = bInHasChanged; -} - -void FUnrealSourceFile::SetModuleRelativePath(FString&& InModuleRelativePath) -{ - ModuleRelativePath = MoveTemp(InModuleRelativePath); -} - -void FUnrealSourceFile::SetIncludePath(FString&& InIncludePath) -{ - IncludePath = MoveTemp(InIncludePath); -} - -const FString& FUnrealSourceFile::GetContent() const -{ - return Content; -} - -EGeneratedCodeVersion FUnrealSourceFile::GetGeneratedCodeVersionForStruct(const FHeaderParser& HeaderParser, UStruct* Struct) const -{ - if (const EGeneratedCodeVersion* Version = GeneratedCodeVersions.Find(Struct)) - { - return *Version; - } - - return HeaderParser.DefaultGeneratedCodeVersion; -} - -void FUnrealSourceFile::MarkDependenciesResolved() -{ - bDependenciesResolved = true; -} - -bool FUnrealSourceFile::AreDependenciesResolved() const -{ - return bDependenciesResolved; -} - -void FUnrealSourceFile::SetScope(FFileScope* InScope) -{ - if (&Scope.Get() != InScope) - { - Scope = TSharedRef(InScope); - } -} - -void FUnrealSourceFile::SetScope(TSharedRef InScope) -{ - Scope = InScope; -} - -void FUnrealSourceFile::MarkAsParsed() -{ - bParsed = true; -} - -bool FUnrealSourceFile::IsParsed() const -{ - return bParsed; -} - -bool FUnrealSourceFile::HasChanged() const -{ - return bHasChanged; -} - -FString FUnrealSourceFile::GetFileDefineName() const -{ - const FString API = FPackageName::GetShortName(Package).ToUpper(); - return FString::Printf(TEXT("%s_%s_generated_h"), *API, *GetStrippedFilename()); -} diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealSourceFile.h b/Engine/Source/Programs/UHTLite/Private/UnrealSourceFile.h deleted file mode 100644 index df02f998ad26..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealSourceFile.h +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Misc/Paths.h" -#include "Scope.h" -#include "HeaderProvider.h" -#include "SimplifiedParsingClassInfo.h" -#include "GeneratedCodeVersion.h" - -class UPackage; -class UClass; -class UStruct; -class FArchive; -class FClassMetaData; - -/** - * Contains information about source file that defines various UHT aware types. - */ -class FUnrealSourceFile : public TSharedFromThis -{ -public: - // Constructor. - FUnrealSourceFile(UPackage* InPackage, FString&& InFilename, FString&& InContent) - : Scope (MakeShareable(new FFileScope(*(FString(TEXT("__")) + FPaths::GetBaseFilename(InFilename) + FString(TEXT("__File"))), this))) - , Filename (MoveTemp(InFilename)) - , Package (InPackage) - , Content (MoveTemp(InContent)) - , bHasChanged (false) - , bParsed (false) - , bDependenciesResolved(false) - { - if (GetStrippedFilename() != "NoExportTypes") - { - Includes.Emplace(FHeaderProvider(EHeaderProviderSourceType::FileName, "NoExportTypes.h")); - } - } - - /** - * Gets parsing info for class defined in this source file. - * - * @returns Parsing info for class defined in this source file. - */ - const FSimplifiedParsingClassInfo& GetDefinedClassParsingInfo(UClass* DefinedClass) const - { - return GetDefinedClassesWithParsingInfo()[DefinedClass]; - } - - /** - * Gets map with classes defined in this source file with parsing info. - * - * @returns Map with classes defined in this source file with parsing info. - */ - const TMap& GetDefinedClassesWithParsingInfo() const - { - return DefinedClasses; - } - - /** - * Gets number of types defined in this source file. - */ - int32 GetDefinedClassesCount() const - { - return DefinedClasses.Num(); - } - - /** - * Gets generated header filename. - */ - const FString& GetGeneratedHeaderFilename() const - { - if (GeneratedHeaderFilename.Len() == 0) - { - GeneratedHeaderFilename = FString::Printf(TEXT("%s.generated.h"), *FPaths::GetBaseFilename(Filename)); - } - - return GeneratedHeaderFilename; - } - - /** - * Gets module relative path. - */ - const FString& GetModuleRelativePath() const - { - return ModuleRelativePath; - } - - /** - * Gets stripped filename. - */ - const FString& GetStrippedFilename() const; - - /** - * Gets unique file id. - */ - const FString& GetFileId() const; - - /** - * Gets define name of this source file. - */ - FString GetFileDefineName() const; - - /** - * Gets file-wise generated body macro name. - * - * @param LineNumber Number at which generated body macro is. - * @param bLegacy Tells if method should get legacy generated body macro. - */ - FString GetGeneratedBodyMacroName(int32 LineNumber, bool bLegacy = false) const; - - /** - * Gets file-wise generated body macro name. - * - * @param LineNumber Number at which generated body macro is. - * @param Suffix Suffix to add to generated body macro name. - */ - FString GetGeneratedMacroName(int32 LineNumber, const TCHAR* Suffix) const; - - /** - * Gets file-wise generated body macro name. - * - * @param ClassData Class metadata for which to get generated body macro name. - * @param Suffix Suffix to add to generated body macro name. - */ - FString GetGeneratedMacroName(FClassMetaData* ClassData, const TCHAR* Suffix = nullptr) const; - - /** - * Adds given class to class definition list for this source file. - * - * @param Class Class to add to list. - * @param ParsingInfo Data from simplified parsing step. - */ - void AddDefinedClass(UClass* Class, FSimplifiedParsingClassInfo&& ParsingInfo); - - /** - * Gets scope for this file. - */ - TSharedRef GetScope() const - { - return Scope; - } - - /** - * Gets package this file is in. - */ - UPackage* GetPackage() const - { - return Package; - } - - /** - * Gets filename. - */ - const FString& GetFilename() const - { - return Filename; - } - - /** - * Gets generated filename. - */ - const FString& GetGeneratedFilename() const - { - return GeneratedFilename; - } - - /** - * Gets include path. - */ - const FString& GetIncludePath() const - { - return IncludePath; - } - - /** - * Gets content. - */ - const FString& GetContent() const; - - /** - * Gets includes. - */ - TArray& GetIncludes() - { - return Includes; - } - - /** - * Gets includes. Const version. - */ - const TArray& GetIncludes() const - { - return Includes; - } - - /** - * Gets generated code version for given UStruct. - */ - EGeneratedCodeVersion GetGeneratedCodeVersionForStruct(const class FHeaderParser& HeaderParser, UStruct* Struct) const; - - /** - * Gets generated code versions. - */ - TMap& GetGeneratedCodeVersions() - { - return GeneratedCodeVersions; - } - - /** - * Gets generated code versions. Const version. - */ - const TMap& GetGeneratedCodeVersions() const - { - return GeneratedCodeVersions; - } - - /** - * Sets generated filename. - */ - void SetGeneratedFilename(FString&& GeneratedFilename); - - /** - * Sets has changed flag. - */ - void SetHasChanged(bool bHasChanged); - - /** - * Sets module relative path. - */ - void SetModuleRelativePath(FString&& ModuleRelativePath); - - /** - * Sets include path. - */ - void SetIncludePath(FString&& IncludePath); - - /** - * Mark this file as parsed. - */ - void MarkAsParsed(); - - /** - * Checks if this file is parsed. - */ - bool IsParsed() const; - - /** - * Checks if generated file has been changed. - */ - bool HasChanged() const; - - /** - * Mark that this file has resolved dependencies. - */ - void MarkDependenciesResolved(); - - /** - * Checks if dependencies has been resolved. - */ - bool AreDependenciesResolved() const; - friend FArchive& operator<<(FArchive& Ar, FUnrealSourceFile& UHTMakefile); - void SetScope(FFileScope* Scope); - void SetScope(TSharedRef Scope); -private: - - // File scope. - TSharedRef Scope; - - // Path of this file. - FString Filename; - - // Stripped path of this file - mutable FString StrippedFilename; - - // Cached FileId for this file - mutable FString FileId; - - // Package of this file. - UPackage* Package; - - // File name of the generated header file associated with this file. - FString GeneratedFilename; - - // File name of the generated header file associated with this file. - mutable FString GeneratedHeaderFilename; - - // Module relative path. - FString ModuleRelativePath; - - // Include path. - FString IncludePath; - - // Source file content. - FString Content; - - // Tells if generated header file was changed. - bool bHasChanged; - - // Tells if this file was parsed. - bool bParsed; - - // Tells if dependencies has been resolved already. - bool bDependenciesResolved; - - // This source file includes. - TArray Includes; - - // List of classes defined in this source file along with parsing info. - TMap DefinedClasses; - - // Mapping of UStructs to versions, according to which their code should be generated. - TMap GeneratedCodeVersions; -}; diff --git a/Engine/Source/Programs/UHTLite/Private/UnrealTypeDefinitionInfo.h b/Engine/Source/Programs/UHTLite/Private/UnrealTypeDefinitionInfo.h deleted file mode 100644 index 4b1c7c5d6160..000000000000 --- a/Engine/Source/Programs/UHTLite/Private/UnrealTypeDefinitionInfo.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Templates/SharedPointer.h" - -// Forward declarations. -class FUnrealSourceFile; - -/** - * Class that stores information about type (USTRUCT/UCLASS) definition. - */ -class FUnrealTypeDefinitionInfo : public TSharedFromThis -{ -public: - // Constructor - FUnrealTypeDefinitionInfo(FUnrealSourceFile& InSourceFile, int32 InLineNumber) - : SourceFile(InSourceFile) - , LineNumber(InLineNumber) - { } - - /** - * Gets the line number in source file this type was defined in. - */ - int32 GetLineNumber() const - { - return LineNumber; - } - - /** - * Gets the reference to FUnrealSourceFile object that stores information about - * source file this type was defined in. - */ - FUnrealSourceFile& GetUnrealSourceFile() - { - return SourceFile; - } - - void SetLineNumber(int32 InLineNumber) - { - LineNumber = InLineNumber; - } - - const FUnrealSourceFile& GetUnrealSourceFile() const - { - return SourceFile; - } - -private: - FUnrealSourceFile& SourceFile; - int32 LineNumber; - - friend struct FUnrealTypeDefinitionInfoArchiveProxy; -}; diff --git a/Engine/Source/Programs/UHTLite/Public/IScriptGeneratorPluginInterface.h b/Engine/Source/Programs/UHTLite/Public/IScriptGeneratorPluginInterface.h deleted file mode 100644 index 09dd02b9c66b..000000000000 --- a/Engine/Source/Programs/UHTLite/Public/IScriptGeneratorPluginInterface.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "Serialization/Archive.h" -#include "Features/IModularFeature.h" -#include "Modules/ModuleInterface.h" - -class FArchive; -class UClass; - -/** Build module type, mirrored in ExternalExecution.cs, enum UHTModuleType */ -struct EBuildModuleType -{ - enum Type - { - Program, - EngineRuntime, - EngineUncooked, - EngineDeveloper, - EngineEditor, - EngineThirdParty, - GameRuntime, - GameUncooked, - GameDeveloper, - GameEditor, - GameThirdParty, - // NOTE: If you add a new value, make sure to update the ToString() method below! - - Max - }; - - friend FArchive& operator<<(FArchive& Ar, EBuildModuleType::Type& Type) - { - if (Ar.IsLoading()) - { - uint8 Value; - Ar << Value; - Type = (EBuildModuleType::Type)Value; - } - else if (Ar.IsSaving()) - { - uint8 Value = (uint8)Type; - Ar << Value; - } - return Ar; - } - /** - * Converts a string literal into EModuleType::Type value - * - * @param The string to convert to EModuleType::Type - * - * @return The enum value corresponding to the name - */ - static EBuildModuleType::Type Parse(const TCHAR* Value); -}; - -/** Build module override type to add additional PKG flags if necessary, mirrored in ModuleRules.cs, enum PackageOverrideType */ -struct EPackageOverrideType -{ - enum Type - { - None, - EditorOnly, - EngineDeveloper, - GameDeveloper, - EngineUncookedOnly, - GameUncookedOnly - }; - - friend FArchive& operator<<(FArchive& Ar, EPackageOverrideType::Type& Type) - { - if (Ar.IsLoading()) - { - uint8 Value; - Ar << Value; - Type = (EPackageOverrideType::Type)Value; - } - else if (Ar.IsSaving()) - { - uint8 Value = (uint8)Type; - Ar << Value; - } - return Ar; - } - /** - * Converts a string literal into EPackageOverrideType::Type value - * - * @param The string to convert to EPackageOverrideType::Type - * - * @return The enum value corresponding to the name - */ - static EPackageOverrideType::Type Parse(const TCHAR* Value); -}; - -/** - * The public interface to script generator plugins. - */ -class IScriptGeneratorPluginInterface : public IModuleInterface, public IModularFeature -{ -public: - - /** Name of module that is going to be compiling generated script glue */ - virtual FString GetGeneratedCodeModuleName() const = 0; - /** Returns true if this plugin supports exporting scripts for the specified target. This should handle game as well as editor target names */ - virtual bool SupportsTarget(const FString& TargetName) const = 0; - /** Returns true if this plugin supports exporting scripts for the specified module */ - virtual bool ShouldExportClassesForModule(const FString& ModuleName, EBuildModuleType::Type ModuleType, const FString& ModuleGeneratedIncludeDirectory) const = 0; - /** Initializes this plugin with build information */ - virtual void Initialize(const FString& RootLocalPath, const FString& RootBuildPath, const FString& OutputDirectory, const FString& IncludeBase) = 0; - /** Exports a single class. May be called multiple times for the same class (as UHT processes the entire hierarchy inside modules. */ - virtual void ExportClass(class UClass* Class, const FString& SourceHeaderFilename, const FString& GeneratedHeaderFilename, bool bHasChanged) = 0; - /** Called once all classes have been exported */ - virtual void FinishExport() = 0; - /** Name of the generator plugin, mostly for debuggind purposes */ - virtual FString GetGeneratorName() const = 0; - /** Finds a list of external dependencies which require UHT to be re-run */ - virtual void GetExternalDependencies(TArray& Dependencies) const { } -}; - diff --git a/Engine/Source/Programs/UHTLite/UHTLite.Build.cs b/Engine/Source/Programs/UHTLite/UHTLite.Build.cs deleted file mode 100644 index e57b1562c7bc..000000000000 --- a/Engine/Source/Programs/UHTLite/UHTLite.Build.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -using UnrealBuildTool; - -public class UHTLite : ModuleRules -{ - public UHTLite(ReadOnlyTargetRules Target) : base(Target) - { - PublicIncludePaths.Add("Runtime/Launch/Public"); - - PrivateDependencyModuleNames.AddRange( - new string[] - { - "Core", - "CoreUObject", - "Json", - "Projects" - } - ); - - PrivateIncludePaths.AddRange( - new string[] - { - // For LaunchEngineLoop.cpp include - "Runtime/Launch/Private", - "Programs/UHTLite/Private", - }); - - bEnableExceptions = true; - - UnsafeTypeCastWarningLevel = WarningLevel.Warning; - } -} diff --git a/Engine/Source/Programs/UHTLite/UHTLite.Target.cs b/Engine/Source/Programs/UHTLite/UHTLite.Target.cs deleted file mode 100644 index 12aeae2541f5..000000000000 --- a/Engine/Source/Programs/UHTLite/UHTLite.Target.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class UHTLiteTarget : TargetRules -{ - public UHTLiteTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Program; - LinkType = TargetLinkType.Modular; - LaunchModuleName = "UHTLite"; - - // Never use malloc profiling in Unreal Header Tool. We set this because often UHT is compiled right before the engine - // automatically by Unreal Build Tool, but if bUseMallocProfiler is defined, UHT can operate incorrectly. - bUseMallocProfiler = false; - - bCompileICU = false; - // Editor-only data, however, is needed - bBuildWithEditorOnlyData = true; - - // Currently this app is not linking against the engine, so we'll compile out references from Core to the rest of the engine - bCompileAgainstEngine = false; - bCompileAgainstApplicationCore = false; - - // Force exception handling across all modules. - bForceEnableExceptions = true; - - // Plugin support - bCompileWithPluginSupport = true; - bBuildDeveloperTools = true; - - // UnrealHeaderTool is a console application, not a Windows app (sets entry point to main(), instead of WinMain()) - bIsBuildingConsoleApplication = true; - - GlobalDefinitions.Add("HACK_HEADER_GENERATOR=1"); - GlobalDefinitions.Add("FNAME_WRITE_PROTECT_PAGES=0"); - GlobalDefinitions.Add("USE_LOCALIZED_PACKAGE_CACHE=0"); - GlobalDefinitions.Add("STATS=0"); - - // We disable compilation of GC related code since it takes a very long time (up to 30 seconds on typical hardware at the time of writing) - GlobalDefinitions.Add("UE_WITH_GC=0"); - GlobalDefinitions.Add("UE_WITH_SAVEPACKAGE=0"); - GlobalDefinitions.Add("UE_WITH_CORE_REDIRECTS=0"); - } -}