Files
UnrealEngineUWP/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.cpp
Tim Smith 808f81dd6c Performance improvements and clean up in preperation for a Live Coding fix.
1) Refactored preparsing to scan for @UCLASS, @USTRUCT, and @ENUM and define then.  This means that no new entries into the type definition info system will be added after pre-parsing and thus remove the need for any type of locking during code generation.
2) Parsing and code generation is now being done in a strict order based on dependencies and no longer by project.  This removes the need of explicitly parsing depedencies first or waiting on hashes to be computed.  (Provided a 50%-80% improvement in code generation time)
3) Changed HeaderParser to be specific to one source file.  Removed the need to pass the source file around since it is always known at construction time.  Simplified some error handling logic.
4) Changed parallel for loops to using event graph to provide more concurrency.
5) Refactored the main program flow into concise steps.

Testing:

Verified that the headers generated exactly the same for:

1) First person C++ Project
2) UnrealEditor
3) QAGame
4) ShooterGame
5) FortniteGame

FYI: Marc had reviewed this as part of a larger change but some parts were extract.  These are the remaining changes.

#rb marc.audy
#rnx
#preflight 6078666a02850d00010e22c5

[CL 16023636 by Tim Smith in ue5-main branch]
2021-04-15 13:44:34 -04:00

121 lines
3.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Classes.h"
#include "ParserClass.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;
}
}
FClass* FClasses::FindClass(const TCHAR* ClassName)
{
check(ClassName);
UObject* ClassPackage = ANY_PACKAGE;
if (UClass* Result = FindObject<UClass>(ClassPackage, ClassName))
{
return (FClass*)Result;
}
if (UObjectRedirector* RenamedClassRedirector = FindObject<UObjectRedirector>(ClassPackage, ClassName))
{
return (FClass*)CastChecked<UClass>(RenamedClassRedirector->DestinationObject);
}
return nullptr;
}
FClass* FClasses::FindScriptClassOrThrow(const FString& InClassName)
{
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)
{
// 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;
}