200 lines
6.6 KiB
C#
200 lines
6.6 KiB
C#
|
|
using Mono.Cecil;
|
|||
|
|
using Mono.Cecil.Rocks;
|
|||
|
|
using UnrealSharpWeaver.TypeProcessors;
|
|||
|
|
using UnrealSharpWeaver.Utilities;
|
|||
|
|
|
|||
|
|
namespace UnrealSharpWeaver.MetaData;
|
|||
|
|
|
|||
|
|
public class ClassMetaData : TypeReferenceMetadata
|
|||
|
|
{
|
|||
|
|
public TypeReferenceMetadata ParentClass { get; set; }
|
|||
|
|
public List<PropertyMetaData> Properties { get; set; }
|
|||
|
|
public List<FunctionMetaData> Functions { get; set; }
|
|||
|
|
public List<FunctionMetaData> VirtualFunctions { get; set; }
|
|||
|
|
public List<TypeReferenceMetadata> Interfaces { get; set; }
|
|||
|
|
public string ConfigCategory { get; set; }
|
|||
|
|
public ClassFlags ClassFlags { get; set; }
|
|||
|
|
|
|||
|
|
// Non-serialized for JSON
|
|||
|
|
public bool HasProperties => Properties.Count > 0;
|
|||
|
|
private readonly TypeDefinition _classDefinition;
|
|||
|
|
// End non-serialized
|
|||
|
|
|
|||
|
|
public ClassMetaData(TypeDefinition type) : base(type, TypeDefinitionUtilities.UClassAttribute)
|
|||
|
|
{
|
|||
|
|
_classDefinition = type;
|
|||
|
|
|
|||
|
|
Properties = [];
|
|||
|
|
Functions = [];
|
|||
|
|
VirtualFunctions = [];
|
|||
|
|
|
|||
|
|
ConfigCategory = string.Empty;
|
|||
|
|
Interfaces = [];
|
|||
|
|
|
|||
|
|
PopulateInterfaces();
|
|||
|
|
PopulateProperties();
|
|||
|
|
PopulateFunctions();
|
|||
|
|
|
|||
|
|
AddConfigCategory();
|
|||
|
|
|
|||
|
|
ParentClass = new TypeReferenceMetadata(type.BaseType.Resolve());
|
|||
|
|
ClassFlags |= GetClassFlags(type, AttributeName) | ClassFlags.CompiledFromBlueprint;
|
|||
|
|
|
|||
|
|
// Force DefaultConfig if Config is set and no other config flag is set
|
|||
|
|
if (ClassFlags.HasFlag(ClassFlags.Config) &&
|
|||
|
|
!ClassFlags.HasFlag(ClassFlags.GlobalUserConfig | ClassFlags.DefaultConfig | ClassFlags.ProjectUserConfig))
|
|||
|
|
{
|
|||
|
|
ClassFlags |= ClassFlags.DefaultConfig;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (type.IsChildOf(WeaverImporter.Instance.UActorComponentDefinition))
|
|||
|
|
{
|
|||
|
|
TryAddMetaData("BlueprintSpawnableComponent", true);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void AddConfigCategory()
|
|||
|
|
{
|
|||
|
|
CustomAttribute uClassAttribute = _classDefinition.GetUClass()!;
|
|||
|
|
CustomAttributeArgument? configCategoryProperty = uClassAttribute.FindAttributeField(nameof(ConfigCategory));
|
|||
|
|
if (configCategoryProperty != null)
|
|||
|
|
{
|
|||
|
|
ConfigCategory = (string) configCategoryProperty.Value.Value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void PopulateProperties()
|
|||
|
|
{
|
|||
|
|
if (_classDefinition.Properties.Count == 0)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Properties = [];
|
|||
|
|
|
|||
|
|
foreach (PropertyDefinition property in _classDefinition.Properties)
|
|||
|
|
{
|
|||
|
|
CustomAttribute? uPropertyAttribute = property.GetUProperty();
|
|||
|
|
|
|||
|
|
if (uPropertyAttribute == null)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PropertyMetaData propertyMetaData = new PropertyMetaData(property);
|
|||
|
|
Properties.Add(propertyMetaData);
|
|||
|
|
|
|||
|
|
if (propertyMetaData.IsInstancedReference)
|
|||
|
|
{
|
|||
|
|
ClassFlags |= ClassFlags.HasInstancedReference;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void PopulateFunctions()
|
|||
|
|
{
|
|||
|
|
if (_classDefinition.Methods.Count == 0)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Functions = [];
|
|||
|
|
VirtualFunctions = [];
|
|||
|
|
|
|||
|
|
for (var i = _classDefinition.Methods.Count - 1; i >= 0; i--)
|
|||
|
|
{
|
|||
|
|
MethodDefinition method = _classDefinition.Methods[i];
|
|||
|
|
|
|||
|
|
if (method.HasParameters)
|
|||
|
|
{
|
|||
|
|
var paramNameSet = new HashSet<string>();
|
|||
|
|
var uniqueNum = 0;
|
|||
|
|
foreach (var param in method.Parameters)
|
|||
|
|
{
|
|||
|
|
if (!paramNameSet.Add(param.Name))
|
|||
|
|
{
|
|||
|
|
param.Name = $"{param.Name}_{uniqueNum++}";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (FunctionMetaData.IsAsyncUFunction(method))
|
|||
|
|
{
|
|||
|
|
method.CustomAttributes.Clear();
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool isBlueprintOverride = FunctionMetaData.IsBlueprintEventOverride(method);
|
|||
|
|
bool isInterfaceFunction = FunctionMetaData.IsInterfaceFunction(method);
|
|||
|
|
|
|||
|
|
if (method.IsUFunction() && !isInterfaceFunction)
|
|||
|
|
{
|
|||
|
|
if (isBlueprintOverride)
|
|||
|
|
{
|
|||
|
|
throw new Exception($"{method.FullName} is a Blueprint override and cannot be marked as a UFunction again.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FunctionMetaData functionMetaData = new FunctionMetaData(method);
|
|||
|
|
|
|||
|
|
if (isInterfaceFunction && functionMetaData.FunctionFlags.HasFlag(EFunctionFlags.BlueprintNativeEvent))
|
|||
|
|
{
|
|||
|
|
throw new Exception("Interface functions cannot be marked as BlueprintEvent. Mark base declaration as BlueprintEvent instead.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Functions.Add(functionMetaData);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (isBlueprintOverride || isInterfaceFunction && method.GetBaseMethod().DeclaringType == _classDefinition)
|
|||
|
|
{
|
|||
|
|
EFunctionFlags functionFlags = EFunctionFlags.None;
|
|||
|
|
if (isInterfaceFunction)
|
|||
|
|
{
|
|||
|
|
MethodDefinition interfaceFunction = FunctionMetaData.TryGetInterfaceFunction(method)!;
|
|||
|
|
functionFlags = interfaceFunction.GetFunctionFlags();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
VirtualFunctions.Add(new FunctionMetaData(method, false, functionFlags));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static ClassFlags GetClassFlags(TypeReference classReference, string flagsAttributeName)
|
|||
|
|
{
|
|||
|
|
return (ClassFlags) GetFlags(classReference.Resolve().CustomAttributes, flagsAttributeName);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void PopulateInterfaces()
|
|||
|
|
{
|
|||
|
|
if (_classDefinition.Interfaces.Count == 0)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Interfaces = [];
|
|||
|
|
|
|||
|
|
foreach (InterfaceImplementation? typeInterface in _classDefinition.Interfaces)
|
|||
|
|
{
|
|||
|
|
TypeDefinition interfaceType = typeInterface.InterfaceType.Resolve();
|
|||
|
|
|
|||
|
|
if (interfaceType == WeaverImporter.Instance.IInterfaceType || !interfaceType.IsUInterface())
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Interfaces.Add(new TypeReferenceMetadata(interfaceType));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void PostWeaveCleanup()
|
|||
|
|
{
|
|||
|
|
foreach (FunctionMetaData function in Functions)
|
|||
|
|
{
|
|||
|
|
function.TryRemoveMethod();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (FunctionMetaData virtualFunction in VirtualFunctions)
|
|||
|
|
{
|
|||
|
|
virtualFunction.TryRemoveMethod();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|