Files
BusyRabbit/Plugins/UnrealSharp/Managed/UnrealSharpPrograms/UnrealSharpWeaver/MetaData/FunctionMetaData.cs
wyatt 648386cd73 Lua向C#逻辑迁移 一期 #13
将整个插件代码上传
2025-10-26 21:48:39 +08:00

293 lines
9.7 KiB
C#

using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using UnrealSharpWeaver.TypeProcessors;
using UnrealSharpWeaver.Utilities;
namespace UnrealSharpWeaver.MetaData;
public class FunctionMetaData : BaseMetaData
{
public PropertyMetaData[] Parameters { get; set; }
public PropertyMetaData? ReturnValue { get; set; }
public EFunctionFlags FunctionFlags { get; set; }
// Non-serialized for JSON
public readonly MethodDefinition MethodDef;
public FunctionRewriteInfo RewriteInfo;
public FieldDefinition? FunctionPointerField;
public bool IsBlueprintEvent => FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintNativeEvent);
public bool HasParameters => Parameters.Length > 0 || HasReturnValue;
public bool HasReturnValue => ReturnValue != null;
public bool IsRpc => FunctionFlags.HasAnyFlags(Utilities.MethodUtilities.RpcFlags);
public bool HasOutParams => FunctionFlags.HasAnyFlags(EFunctionFlags.HasOutParms);
private bool _shouldBeRemoved;
// End non-serialized
private const string CallInEditorName = "CallInEditor";
public FunctionMetaData(MethodDefinition method, bool onlyCollectMetaData = false, EFunctionFlags functionFlags = EFunctionFlags.None)
: base(method, Utilities.MethodUtilities.UFunctionAttribute)
{
MethodDef = method;
FunctionFlags = functionFlags;
bool hasOutParams = false;
if (!method.ReturnsVoid())
{
hasOutParams = true;
try
{
ReturnValue = PropertyMetaData.FromTypeReference(method.ReturnType, "ReturnValue", ParameterType.ReturnValue);
}
catch (InvalidPropertyException)
{
throw new InvalidUnrealFunctionException(method, $"'{method.ReturnType.FullName}' is invalid for unreal function return value.");
}
}
if (BaseAttribute != null)
{
CustomAttributeArgument? callInEditor = BaseAttribute.FindAttributeField(CallInEditorName);
if (callInEditor.HasValue)
{
TryAddMetaData(CallInEditorName, (bool) callInEditor.Value.Value);
}
}
Parameters = new PropertyMetaData[method.Parameters.Count];
for (int i = 0; i < method.Parameters.Count; ++i)
{
ParameterDefinition param = method.Parameters[i];
ParameterType modifier = ParameterType.Value;
TypeReference paramType = param.ParameterType;
if (param.IsOut)
{
hasOutParams = true;
modifier = ParameterType.Out;
}
else if (paramType.IsByReference)
{
hasOutParams = true;
modifier = ParameterType.Ref;
}
Parameters[i] = PropertyMetaData.FromTypeReference(paramType, param.Name, modifier, param);
if (param.HasConstant)
{
string? defaultValue = DefaultValueToString(param);
if (defaultValue != null)
{
TryAddMetaData($"CPP_Default_{param.Name}", defaultValue);
FunctionFlags |= EFunctionFlags.HasDefaults;
}
}
}
FunctionFlags |= MethodDef.GetFunctionFlags();
if (hasOutParams)
{
FunctionFlags |= EFunctionFlags.HasOutParms;
}
if (onlyCollectMetaData)
{
return;
}
RewriteFunction();
}
public void RewriteFunction(bool forceOverwriteBody = false)
{
TypeDefinition baseType = MethodDef.GetOriginalBaseMethod().DeclaringType;
if (baseType == MethodDef.DeclaringType)
{
RewriteInfo = new FunctionRewriteInfo(this);
FunctionProcessor.PrepareFunctionForRewrite(this, MethodDef.DeclaringType, forceOverwriteBody);
}
else
{
EFunctionFlags flags = GetFunctionFlags(MethodDef.GetOriginalBaseMethod());
if (flags.HasAnyFlags(EFunctionFlags.BlueprintCallable)
&& !flags.HasAnyFlags(EFunctionFlags.BlueprintNativeEvent))
{
return;
}
FunctionProcessor.MakeImplementationMethod(this);
// We don't need the override anymore. It's copied into the Implementation method.
// But we can't remove it here because it would mess up for child classes during weaving.
_shouldBeRemoved = true;
}
}
public void TryRemoveMethod()
{
if (!_shouldBeRemoved)
{
return;
}
MethodDef.DeclaringType.Methods.Remove(MethodDef);
}
public static bool IsAsyncUFunction(MethodDefinition method)
{
if (!method.HasCustomAttributes)
{
return false;
}
CustomAttribute? functionAttribute = method.GetUFunction();
if (functionAttribute == null)
{
return false;
}
if (!functionAttribute.HasConstructorArguments)
{
return false;
}
var flags = (EFunctionFlags) (ulong) functionAttribute.ConstructorArguments[0].Value;
return flags == EFunctionFlags.BlueprintCallable && method.ReturnType.FullName.StartsWith("System.Threading.Tasks.Task");
}
public static bool IsBlueprintEventOverride(MethodDefinition method)
{
if (!method.IsVirtual)
{
return false;
}
MethodDefinition baseMethod = method.GetOriginalBaseMethod();
if (baseMethod != method && baseMethod.HasCustomAttributes)
{
return baseMethod.IsUFunction();
}
return false;
}
public static string? DefaultValueToString(ParameterDefinition value)
{
// Can be null if the value is set to = default/null
if (value.Constant == null)
{
return null;
}
TypeDefinition typeDefinition = value.ParameterType.Resolve();
if (typeDefinition.IsEnum)
{
return typeDefinition.Fields[(byte) value.Constant].Name;
}
// Unreal doesn't support commas in default values
string defaultValue = value.Constant.ToString()!;
defaultValue = defaultValue.Replace(",", ".");
return defaultValue;
}
public static EFunctionFlags GetFunctionFlags(MethodDefinition method)
{
return (EFunctionFlags) GetFlags(method, "FunctionFlagsMapAttribute");
}
public static bool IsInterfaceFunction(MethodDefinition method)
{
return TryGetInterfaceFunction(method) != null;
}
public static MethodDefinition? TryGetInterfaceFunction(MethodDefinition method)
{
foreach (var typeInterface in method.DeclaringType.Interfaces)
{
var interfaceType = typeInterface.InterfaceType.Resolve();
if (!interfaceType.IsUInterface())
{
continue;
}
foreach (MethodDefinition? interfaceMethod in interfaceType.Methods)
{
if (interfaceMethod.Name == method.Name)
{
return interfaceMethod;
}
}
}
return null;
}
public static bool IsBlueprintCallable(MethodDefinition method)
{
EFunctionFlags flags = GetFunctionFlags(method);
return flags.HasAnyFlags(EFunctionFlags.BlueprintCallable);
}
public void EmitFunctionPointers(ILProcessor processor, Instruction loadTypeField, Instruction setFunctionPointer)
{
processor.Append(loadTypeField);
processor.Emit(OpCodes.Ldstr, Name);
processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetNativeFunctionFromClassAndNameMethod);
processor.Append(setFunctionPointer);
}
public void EmitFunctionParamOffsets(ILProcessor processor, Instruction loadFunctionPointer)
{
foreach (FunctionParamRewriteInfo paramRewriteInfo in RewriteInfo.FunctionParams)
{
FieldDefinition? offsetField = paramRewriteInfo.OffsetField;
if (offsetField == null)
{
continue;
}
PropertyMetaData param = paramRewriteInfo.PropertyMetaData;
processor.Append(loadFunctionPointer);
processor.Emit(OpCodes.Ldstr, param.Name);
processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetPropertyOffsetFromNameMethod);
processor.Emit(OpCodes.Stsfld, offsetField);
}
}
public void EmitFunctionParamSize(ILProcessor processor, Instruction loadFunctionPointer)
{
if (RewriteInfo.FunctionParamSizeField == null)
{
return;
}
processor.Append(loadFunctionPointer);
processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetNativeFunctionParamsSizeMethod);
processor.Emit(OpCodes.Stsfld, RewriteInfo.FunctionParamSizeField);
}
public void EmitParamNativeProperty(ILProcessor processor, Instruction? loadFunctionPointer)
{
foreach (var paramRewriteInfo in RewriteInfo.FunctionParams)
{
FieldDefinition? nativePropertyField = paramRewriteInfo.NativePropertyField;
if (nativePropertyField == null)
{
continue;
}
processor.Append(loadFunctionPointer);
processor.Emit(OpCodes.Ldstr, paramRewriteInfo.PropertyMetaData.Name);
processor.Emit(OpCodes.Call, WeaverImporter.Instance.GetNativePropertyFromNameMethod);
processor.Emit(OpCodes.Stsfld, nativePropertyField);
}
}
}