Lua向C#逻辑迁移 一期 #13

将整个插件代码上传
This commit is contained in:
2025-10-26 21:48:39 +08:00
parent 56994b3927
commit 648386cd73
785 changed files with 53683 additions and 2 deletions

View File

@ -0,0 +1,62 @@
using Mono.Cecil;
using Mono.Cecil.Rocks;
namespace UnrealSharpWeaver.Utilities;
public static class AssemblyUtilities
{
public static TypeReference? FindGenericType(this AssemblyDefinition assembly, string typeNamespace, string typeName, TypeReference[] typeParameters, bool bThrowOnException = true)
{
TypeReference? typeRef = FindType(assembly, typeName, typeNamespace, bThrowOnException);
return typeRef == null ? null : typeRef.Resolve().MakeGenericInstanceType(typeParameters).ImportType();
}
public static TypeReference? FindType(this AssemblyDefinition assembly, string typeName, string typeNamespace = "", bool throwOnException = true)
{
foreach (var module in assembly.Modules)
{
foreach (var type in module.GetAllTypes())
{
if ((typeNamespace.Length > 0 && type.Namespace != typeNamespace) || type.Name != typeName)
{
continue;
}
return type.ImportType();
}
}
if (throwOnException)
{
throw new TypeAccessException($"Type \"{typeNamespace}.{typeName}\" not found in userAssembly {assembly.Name}");
}
return null;
}
public static TypeDefinition CreateNewClass(this AssemblyDefinition assembly, string classNamespace, string className, TypeAttributes attributes, TypeReference? parentClass = null)
{
if (parentClass == null)
{
parentClass = assembly.MainModule.TypeSystem.Object;
}
TypeDefinition newType = new TypeDefinition(classNamespace, className, attributes, parentClass);
assembly.MainModule.Types.Add(newType);
return newType;
}
public static void ForEachAssembly(Func<AssemblyDefinition, bool> action)
{
List<AssemblyDefinition> assemblies = [WeaverImporter.Instance.UnrealSharpAssembly, WeaverImporter.Instance.UnrealSharpCoreAssembly];
assemblies.AddRange(WeaverImporter.Instance.AllProjectAssemblies);
foreach (AssemblyDefinition assembly in assemblies)
{
if (!action(assembly))
{
return;
}
}
}
}

View File

@ -0,0 +1,63 @@
using Mono.Cecil;
namespace UnrealSharpWeaver.Utilities;
public static class AttributeUtilities
{
public static readonly string UMetaDataAttribute = "UMetaDataAttribute";
public static readonly string MetaTagsNamespace = WeaverImporter.AttributeNamespace + ".MetaTags";
public static List<CustomAttribute> FindMetaDataAttributes(this IEnumerable<CustomAttribute> customAttributes)
{
return FindAttributesByType(customAttributes, WeaverImporter.AttributeNamespace, UMetaDataAttribute);
}
public static List<CustomAttribute> FindMetaDataAttributesByNamespace(this IEnumerable<CustomAttribute> customAttributes)
{
return FindAttributesByNamespace(customAttributes, MetaTagsNamespace);
}
public static CustomAttributeArgument? FindAttributeField(this CustomAttribute attribute, string fieldName)
{
foreach (var field in attribute.Fields)
{
if (field.Name == fieldName)
{
return field.Argument;
}
}
return null;
}
public static CustomAttribute? FindAttributeByType(this IEnumerable<CustomAttribute> customAttributes, string typeNamespace, string typeName)
{
List<CustomAttribute> attribs = FindAttributesByType(customAttributes, typeNamespace, typeName);
return attribs.Count == 0 ? null : attribs[0];
}
public static List<CustomAttribute> FindAttributesByType(this IEnumerable<CustomAttribute> customAttributes, string typeNamespace, string typeName)
{
List<CustomAttribute> attribs = new List<CustomAttribute>();
foreach (CustomAttribute attrib in customAttributes)
{
if (attrib.AttributeType.Namespace == typeNamespace && attrib.AttributeType.Name == typeName)
{
attribs.Add(attrib);
}
}
return attribs;
}
public static List<CustomAttribute> FindAttributesByNamespace(this IEnumerable<CustomAttribute> customAttributes, string typeNamespace)
{
List<CustomAttribute> attribs = new List<CustomAttribute>();
foreach (var attrib in customAttributes)
{
if (attrib.AttributeType.Namespace == typeNamespace)
{
attribs.Add(attrib);
}
}
return attribs;
}
}

View File

@ -0,0 +1,34 @@
using Mono.Cecil;
namespace UnrealSharpWeaver.Utilities;
public static class DelegateUtilities
{
public static MethodDefinition GetDelegateInvokeMethod(TypeDefinition typeDefinition)
{
foreach (MethodDefinition method in typeDefinition.Methods)
{
if (method.Name != "Invoke")
{
continue;
}
if (!method.ReturnsVoid())
{
throw new Exception($"{typeDefinition.FullName} is exposed to Unreal Engine, and must have a void return type.");
}
return method;
}
throw new Exception($"Delegate type {typeDefinition.FullName} does not have an Invoke method.");
}
public static string GetUnrealDelegateName(TypeReference typeDefinition)
{
string functionName = typeDefinition.FullName;
functionName = functionName.Replace(".", "_");
functionName += "__DelegateSignature";
return functionName;
}
}

View File

@ -0,0 +1,9 @@
namespace UnrealSharpWeaver.Utilities;
public static class EnumUtilities
{
public static bool HasAnyFlags(this Enum flags, Enum testFlags)
{
return (Convert.ToUInt64(flags) & Convert.ToUInt64(testFlags)) != 0;
}
}

View File

@ -0,0 +1,146 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using UnrealSharpWeaver.MetaData;
namespace UnrealSharpWeaver.Utilities;
public static class MethodUtilities
{
public static readonly EFunctionFlags RpcFlags = EFunctionFlags.NetServer | EFunctionFlags.NetClient | EFunctionFlags.NetMulticast;
public static readonly string UFunctionAttribute = "UFunctionAttribute";
/// <param name="name">name the method copy will have</param>
/// <param name="method">original method</param>
/// <param name="addMethod">Add the method copy to the declaring type. this allows to use the original sources to be matched to the copy.</param>
/// <param name="copyMetadataToken"></param>
/// <returns>new instance of as copy of the original</returns>
public static MethodDefinition CopyMethod(string name, MethodDefinition method, bool addMethod = true, bool copyMetadataToken = true)
{
MethodDefinition newMethod = new MethodDefinition(name, method.Attributes, method.ReturnType)
{
HasThis = true,
ExplicitThis = method.ExplicitThis,
CallingConvention = method.CallingConvention,
Body = method.Body
};
if (copyMetadataToken)
{
newMethod.MetadataToken = method.MetadataToken;
}
foreach (ParameterDefinition parameter in method.Parameters)
{
TypeReference importedType = parameter.ParameterType.ImportType();
newMethod.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, importedType));
}
if (addMethod)
{
method.DeclaringType.Methods.Add(newMethod);
}
return newMethod;
}
public static VariableDefinition AddLocalVariable(this MethodDefinition method, TypeReference typeReference)
{
var variable = new VariableDefinition(typeReference);
method.Body.Variables.Add(variable);
method.Body.InitLocals = true;
return variable;
}
public static void FinalizeMethod(this MethodDefinition method)
{
method.Body.GetILProcessor().Emit(OpCodes.Ret);
OptimizeMethod(method);
}
public static bool MethodIsCompilerGenerated(this ICustomAttributeProvider method)
{
return method.CustomAttributes.FindAttributeByType("System.Runtime.CompilerServices", "CompilerGeneratedAttribute") != null;
}
public static EFunctionFlags GetFunctionFlags(this MethodDefinition method)
{
EFunctionFlags flags = (EFunctionFlags) BaseMetaData.GetFlags(method, "FunctionFlagsMapAttribute");
if (method.IsPublic)
{
flags |= EFunctionFlags.Public;
}
else if (method.IsFamily)
{
flags |= EFunctionFlags.Protected;
}
else
{
flags |= EFunctionFlags.Private;
}
if (method.IsStatic)
{
flags |= EFunctionFlags.Static;
}
if (flags.HasAnyFlags(RpcFlags))
{
flags |= EFunctionFlags.Net;
if (!method.ReturnsVoid())
{
throw new InvalidUnrealFunctionException(method, "RPCs can't have return values.");
}
if (flags.HasFlag(EFunctionFlags.BlueprintNativeEvent))
{
throw new InvalidUnrealFunctionException(method, "BlueprintEvents methods cannot be replicated!");
}
}
// This represents both BlueprintNativeEvent and BlueprintImplementableEvent
if (flags.HasFlag(EFunctionFlags.BlueprintNativeEvent))
{
flags |= EFunctionFlags.Event;
}
// Native is needed to bind the function pointer of the UFunction to our own invoke in UE.
return flags | EFunctionFlags.Native;
}
public static void OptimizeMethod(this MethodDefinition method)
{
if (method.Body.CodeSize == 0)
{
return;
}
method.Body.Optimize();
method.Body.SimplifyMacros();
}
public static void RemoveReturnInstruction(this MethodDefinition method)
{
if (method.Body.Instructions.Count > 0 && method.Body.Instructions[^1].OpCode == OpCodes.Ret)
{
method.Body.Instructions.RemoveAt(method.Body.Instructions.Count - 1);
}
}
public static CustomAttribute? GetUFunction(this MethodDefinition function)
{
return function.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UFunctionAttribute);
}
public static bool IsUFunction(this MethodDefinition method)
{
return GetUFunction(method) != null;
}
public static MethodReference ImportMethod(this MethodReference method)
{
return WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference(method);
}
}

View File

@ -0,0 +1,156 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
namespace UnrealSharpWeaver.Utilities;
public static class ParameterUtilities
{
public static Instruction CreateLoadInstructionOutParam(this ParameterDefinition param, PropertyType paramTypeCode)
{
while (true)
{
switch (paramTypeCode)
{
case PropertyType.Enum:
var param1 = param;
param = null!;
paramTypeCode = param1.ParameterType.Resolve().GetEnumUnderlyingType().GetPrimitiveTypeCode();
continue;
case PropertyType.Bool:
case PropertyType.Int8:
case PropertyType.Byte:
return Instruction.Create(OpCodes.Ldind_I1);
case PropertyType.Int16:
case PropertyType.UInt16:
return Instruction.Create(OpCodes.Ldind_I2);
case PropertyType.Int:
case PropertyType.UInt32:
return Instruction.Create(OpCodes.Ldind_I4);
case PropertyType.Int64:
case PropertyType.UInt64:
return Instruction.Create(OpCodes.Ldind_I8);
case PropertyType.Float:
return Instruction.Create(OpCodes.Ldind_R4);
case PropertyType.Double:
return Instruction.Create(OpCodes.Ldind_R8);
case PropertyType.Struct:
return Instruction.Create(OpCodes.Ldobj, param.ParameterType.GetElementType());
case PropertyType.LazyObject:
case PropertyType.WeakObject:
case PropertyType.SoftClass:
case PropertyType.SoftObject:
case PropertyType.Class:
return Instruction.Create(OpCodes.Ldobj, param.ParameterType.GetElementType());
case PropertyType.Delegate:
case PropertyType.MulticastInlineDelegate:
case PropertyType.MulticastSparseDelegate:
// Delegate/multicast delegates in C# are implemented as classes, use Ldind_Ref
return Instruction.Create(OpCodes.Ldind_Ref);
case PropertyType.InternalManagedFixedSizeArray:
case PropertyType.InternalNativeFixedSizeArray:
throw new NotImplementedException(); // Fixed size arrays not supported as args
case PropertyType.Array:
case PropertyType.Set:
case PropertyType.Map:
// Assumes this will be always be an object (IList, List, ISet, HashSet, IDictionary, Dictionary)
return Instruction.Create(OpCodes.Ldind_Ref);
case PropertyType.Unknown:
case PropertyType.Interface:
case PropertyType.Object:
case PropertyType.ObjectPtr:
case PropertyType.String:
case PropertyType.Name:
case PropertyType.Text:
case PropertyType.DefaultComponent:
default:
return Instruction.Create(OpCodes.Ldind_Ref);
}
}
}
public static Instruction CreateSetInstructionOutParam(this ParameterDefinition param, PropertyType paramTypeCode)
{
while (true)
{
switch (paramTypeCode)
{
case PropertyType.Enum:
paramTypeCode = param.ParameterType.Resolve().GetEnumUnderlyingType().GetPrimitiveTypeCode();
continue;
case PropertyType.Bool:
case PropertyType.Int8:
case PropertyType.Byte:
return Instruction.Create(OpCodes.Stind_I1);
case PropertyType.Int16:
case PropertyType.UInt16:
return Instruction.Create(OpCodes.Stind_I2);
case PropertyType.Int:
case PropertyType.UInt32:
return Instruction.Create(OpCodes.Stind_I4);
case PropertyType.Int64:
case PropertyType.UInt64:
return Instruction.Create(OpCodes.Stind_I8);
case PropertyType.Float:
return Instruction.Create(OpCodes.Stind_R4);
case PropertyType.Double:
return Instruction.Create(OpCodes.Stind_R8);
case PropertyType.Struct:
return Instruction.Create(OpCodes.Stobj, param.ParameterType.GetElementType());
case PropertyType.LazyObject:
case PropertyType.WeakObject:
case PropertyType.SoftClass:
case PropertyType.SoftObject:
case PropertyType.Class:
case PropertyType.Name:
case PropertyType.Text:
return Instruction.Create(OpCodes.Stobj, param.ParameterType.GetElementType());
case PropertyType.Delegate:
case PropertyType.MulticastSparseDelegate:
case PropertyType.MulticastInlineDelegate:
// Delegate/multicast delegates in C# are implemented as classes, use Stind_Ref
return Instruction.Create(OpCodes.Stind_Ref);
case PropertyType.InternalManagedFixedSizeArray:
case PropertyType.InternalNativeFixedSizeArray:
throw new NotImplementedException(); // Fixed size arrays not supported as args
case PropertyType.Array:
case PropertyType.Set:
case PropertyType.Map:
// Assumes this will be always be an object (IList, List, ISet, HashSet, IDictionary, Dictionary)
return Instruction.Create(OpCodes.Stind_Ref);
case PropertyType.Unknown:
case PropertyType.Interface:
case PropertyType.Object:
case PropertyType.ObjectPtr:
case PropertyType.String:
case PropertyType.DefaultComponent:
default:
return Instruction.Create(OpCodes.Stind_Ref);
}
}
}
}

View File

@ -0,0 +1,24 @@
using Mono.Cecil;
using Mono.Collections.Generic;
namespace UnrealSharpWeaver.Utilities;
public static class PropertyUtilities
{
public static readonly string UPropertyAttribute = "UPropertyAttribute";
public static CustomAttribute? GetUProperty(Collection<CustomAttribute> attributes)
{
return attributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UPropertyAttribute);
}
public static CustomAttribute? GetUProperty(this IMemberDefinition typeDefinition)
{
return typeDefinition.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UPropertyAttribute);
}
public static bool IsUProperty(this IMemberDefinition property)
{
return GetUProperty(property.CustomAttributes) != null;
}
}

View File

@ -0,0 +1,656 @@
using System.Reflection.Metadata;
using Mono.Cecil;
using Mono.Cecil.Rocks;
using Mono.Collections.Generic;
using UnrealSharpWeaver.NativeTypes;
using CustomAttribute = Mono.Cecil.CustomAttribute;
using FieldDefinition = Mono.Cecil.FieldDefinition;
using InterfaceImplementation = Mono.Cecil.InterfaceImplementation;
using MethodDefinition = Mono.Cecil.MethodDefinition;
using PropertyDefinition = Mono.Cecil.PropertyDefinition;
using SequencePoint = Mono.Cecil.Cil.SequencePoint;
using TypeDefinition = Mono.Cecil.TypeDefinition;
using TypeReference = Mono.Cecil.TypeReference;
namespace UnrealSharpWeaver.Utilities;
public static class TypeDefinitionUtilities
{
public static readonly string UClassCallbacks = "UClassExporter";
public static readonly string UClassAttribute = "UClassAttribute";
public static readonly string UEnumAttribute = "UEnumAttribute";
public static readonly string UStructAttribute = "UStructAttribute";
public static readonly string UInterfaceAttribute = "UInterfaceAttribute";
public static readonly string BlittableTypeAttribute = "BlittableTypeAttribute";
public static CustomAttribute? GetUClass(this IMemberDefinition definition)
{
return definition.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UClassAttribute);
}
public static bool IsUClass(this IMemberDefinition definition)
{
return GetUClass(definition) != null;
}
public static bool IsUInterface(this TypeDefinition typeDefinition)
{
return GetUInterface(typeDefinition) != null;
}
public static bool IsUEnum(this TypeDefinition typeDefinition)
{
return GetUEnum(typeDefinition) != null;
}
public static CustomAttribute? GetUStruct(this IMemberDefinition type)
{
return type.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UStructAttribute);
}
public static bool IsUStruct(this IMemberDefinition definition)
{
return GetUStruct(definition) != null;
}
public static string GetEngineName(this IMemberDefinition memberDefinition)
{
IMemberDefinition currentMemberIteration = memberDefinition;
while (currentMemberIteration != null)
{
CustomAttribute? genTypeAttribute = currentMemberIteration.CustomAttributes
.FirstOrDefault(x => x.AttributeType.Name == WeaverImporter.GeneratedTypeAttribute);
if (genTypeAttribute is not null)
{
return (string) genTypeAttribute.ConstructorArguments[0].Value;
}
if (memberDefinition.IsUClass() && memberDefinition.Name.StartsWith('U') ||
memberDefinition.IsUStruct() && memberDefinition.Name.StartsWith('F'))
{
return memberDefinition.Name[1..];
}
if (currentMemberIteration is MethodDefinition { IsVirtual: true } virtualMethodDefinition)
{
if (currentMemberIteration == virtualMethodDefinition.GetBaseMethod())
{
break;
}
currentMemberIteration = virtualMethodDefinition.GetBaseMethod();
}
else
{
break;
}
}
// Same name in engine as in managed code
return memberDefinition.Name;
}
public static CustomAttribute? GetUEnum(this TypeDefinition type)
{
return type.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UEnumAttribute);
}
public static CustomAttribute? GetBlittableType(this TypeDefinition type)
{
return type.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpCoreAttributesNamespace, BlittableTypeAttribute);
}
public static bool IsUnmanagedType(this TypeReference typeRef)
{
var typeDef = typeRef.Resolve();
// Must be a value type
if (!typeDef.IsValueType)
return false;
// Primitive types and enums are unmanaged
if (typeDef.IsPrimitive || typeDef.IsEnum)
return true;
// For structs, recursively check all fields
return typeDef.Fields
.Where(f => !f.IsStatic)
.Select(f => f.FieldType.Resolve())
.All(IsUnmanagedType);
}
public static CustomAttribute? GetUInterface(this TypeDefinition type)
{
return type.CustomAttributes.FindAttributeByType(WeaverImporter.UnrealSharpAttributesNamespace, UInterfaceAttribute);
}
public static void AddGeneratedTypeAttribute(this TypeDefinition type)
{
CustomAttribute attribute = new CustomAttribute(WeaverImporter.Instance.GeneratedTypeCtor);
string typeName = type.Name.Substring(1);
string fullTypeName = type.Namespace + "." + typeName;
attribute.ConstructorArguments.Add(new CustomAttributeArgument(WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.TypeSystem.String, typeName));
attribute.ConstructorArguments.Add(new CustomAttributeArgument(WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.TypeSystem.String, fullTypeName));
type.CustomAttributes.Add(attribute);
}
public static PropertyDefinition? FindPropertyByName(this TypeDefinition classOuter, string propertyName)
{
foreach (var property in classOuter.Properties)
{
if (property.Name == propertyName)
{
return property;
}
}
return default;
}
public static bool IsChildOf(this TypeDefinition type, TypeDefinition parentType)
{
TypeDefinition? currentType = type;
while (currentType != null)
{
if (currentType == parentType)
{
return true;
}
currentType = currentType.BaseType?.Resolve();
}
return false;
}
public static TypeReference FindNestedType(this TypeDefinition typeDef, string typeName)
{
foreach (var nestedType in typeDef.NestedTypes)
{
if (nestedType.Name != typeName)
{
continue;
}
return WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference(nestedType);
}
throw new Exception($"{typeName} not found in {typeDef}.");
}
public static MethodDefinition AddMethod(this TypeDefinition type, string name, TypeReference? returnType, MethodAttributes attributes = MethodAttributes.Private, params TypeReference[] parameterTypes)
{
returnType ??= WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.TypeSystem.Void;
var method = new MethodDefinition(name, attributes, returnType);
foreach (var parameterType in parameterTypes)
{
method.Parameters.Add(new ParameterDefinition(parameterType));
}
type.Methods.Add(method);
return method;
}
public static MethodDefinition GetOrAddMethod(this TypeDefinition type, string name, TypeReference? returnType, MethodAttributes attributes = MethodAttributes.Private, params TypeReference[] parameterTypes)
{
returnType ??= WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.TypeSystem.Void;
var existingMethod = FindMethod(type, name, throwIfNotFound: false, parameterTypes);
if (existingMethod is not null)
{
return existingMethod.Resolve();
}
var method = new MethodDefinition(name, attributes, returnType);
foreach (var parameterType in parameterTypes)
{
method.Parameters.Add(new ParameterDefinition(parameterType));
}
type.Methods.Add(method);
return method;
}
private static readonly MethodAttributes MethodAttributes = MethodAttributes.Public | MethodAttributes.Static;
public static MethodDefinition AddToNativeMethod(this TypeDefinition type, TypeDefinition valueType, TypeReference[]? parameters = null)
{
if (parameters == null)
{
parameters = [WeaverImporter.Instance.IntPtrType, WeaverImporter.Instance.Int32TypeRef, valueType];
}
MethodDefinition toNativeMethod = type.AddMethod("ToNative", WeaverImporter.Instance.VoidTypeRef, MethodAttributes, parameters);
return toNativeMethod;
}
public static MethodDefinition AddFromNativeMethod(this TypeDefinition type, TypeDefinition returnType, TypeReference[]? parameters = null)
{
if (parameters == null)
{
parameters = [WeaverImporter.Instance.IntPtrType, WeaverImporter.Instance.Int32TypeRef];
}
MethodDefinition fromNative = type.AddMethod("FromNative", returnType, MethodAttributes, parameters);
return fromNative;
}
public static FieldDefinition AddField(this TypeDefinition type, string name, TypeReference typeReference, FieldAttributes attributes = 0)
{
if (attributes == 0)
{
attributes = FieldAttributes.Static | FieldAttributes.Private;
}
var field = new FieldDefinition(name, attributes, typeReference);
type.Fields.Add(field);
return field;
}
public static FieldReference FindField(this TypeDefinition typeDef, string fieldName)
{
foreach (var field in typeDef.Fields)
{
if (field.Name != fieldName)
{
continue;
}
return WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference(field);
}
throw new Exception($"{fieldName} not found in {typeDef}.");
}
public static bool IsUObject(this TypeDefinition typeDefinition)
{
if (!typeDefinition.IsUClass())
{
return false;
}
while (typeDefinition != null)
{
if (typeDefinition.BaseType == null)
{
return false;
}
if (typeDefinition == WeaverImporter.Instance.UObjectDefinition)
{
return true;
}
typeDefinition = typeDefinition.BaseType.Resolve();
}
return false;
}
public static TypeReference ImportType(this TypeReference type)
{
return WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference(type);
}
public static bool HasMethod(this TypeDefinition typeDef, string methodName, bool throwIfNotFound = true, params TypeReference[] parameterTypes)
{
return FindMethod(typeDef, methodName, throwIfNotFound, parameterTypes) != null;
}
public static MethodReference? FindMethod(this TypeReference typeReference, string methodName,
bool throwIfNotFound = true, params TypeReference[] parameterTypes)
{
var method = FindMethod(typeReference.Resolve(), methodName, throwIfNotFound, parameterTypes);
if (method is null) return null;
// If the declaring type is generic instance, we need to create a new method reference
if (typeReference is GenericInstanceType genericInstance)
{
// Create new method reference on the generic instance
var newMethod = new MethodReference(method.Name, method.ReturnType, genericInstance)
{
HasThis = method.HasThis,
ExplicitThis = method.ExplicitThis,
CallingConvention = method.CallingConvention
};
// Copy the parameters
foreach (var parameter in method.Parameters)
{
newMethod.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
}
// If the method itself is generic, handle its generic parameters
if (method.HasGenericParameters)
{
var genericInstanceMethod = new GenericInstanceMethod(newMethod);
foreach (var genericParam in method.GenericParameters)
{
// Map the generic parameter to the concrete type from the declaring type
var concreteType = genericInstance.GenericArguments[genericParam.Position];
genericInstanceMethod.GenericArguments.Add(concreteType);
}
return genericInstanceMethod;
}
return newMethod;
}
return method;
}
public static MethodReference? FindMethod(this TypeDefinition typeDef, string methodName, bool throwIfNotFound = true, params TypeReference[] parameterTypes)
{
TypeDefinition? currentClass = typeDef;
while (currentClass != null)
{
MethodReference? method = FindOwnMethod(currentClass, methodName, throwIfNotFound: false, parameterTypes);
if (method != null)
{
return method;
}
currentClass = currentClass.BaseType?.Resolve();
}
if (throwIfNotFound)
{
throw new Exception("Couldn't find method " + methodName + " in " + typeDef + ".");
}
return default;
}
public static MethodReference? FindOwnMethod(TypeDefinition typeDef, string methodName, bool throwIfNotFound = true, params TypeReference[] parameterTypes)
{
foreach (var classMethod in typeDef.Methods)
{
if (classMethod.Name != methodName)
{
continue;
}
if (parameterTypes.Length > 0 && classMethod.Parameters.Count != parameterTypes.Length)
{
continue;
}
bool found = true;
for (int i = 0; i < parameterTypes.Length; i++)
{
if (classMethod.Parameters[i].ParameterType.FullName != parameterTypes[i].FullName)
{
found = false;
break;
}
}
if (found)
{
return classMethod.ImportMethod();
}
}
if (throwIfNotFound)
{
throw new Exception("Couldn't find method " + methodName + " in " + typeDef + ".");
}
return default;
}
public static NativeDataType GetDataType(this TypeReference typeRef, string propertyName, Collection<CustomAttribute>? customAttributes)
{
int arrayDim = 1;
TypeDefinition typeDef = typeRef.Resolve();
SequencePoint? sequencePoint = ErrorEmitter.GetSequencePointFromMemberDefinition(typeDef);
if (customAttributes != null)
{
CustomAttribute? propertyAttribute = typeDef.GetUProperty();
if (propertyAttribute != null)
{
CustomAttributeArgument? arrayDimArg = propertyAttribute.FindAttributeField("ArrayDim");
if (typeRef is GenericInstanceType genericType && genericType.GetElementType().FullName == "UnrealSharp.FixedSizeArrayReadWrite`1")
{
if (arrayDimArg.HasValue)
{
arrayDim = (int) arrayDimArg.Value.Value;
// Unreal doesn't have a separate type for fixed arrays, so we just want to generate the inner UProperty type with an arrayDim.
typeRef = genericType.GenericArguments[0];
typeDef = typeRef.Resolve();
}
else
{
throw new InvalidPropertyException(propertyName, sequencePoint, "Fixed array properties must specify an ArrayDim in their [UProperty] attribute");
}
}
else if (arrayDimArg.HasValue)
{
throw new InvalidPropertyException(propertyName, sequencePoint, "ArrayDim is only valid for FixedSizeArray properties.");
}
}
}
switch (typeDef.FullName)
{
case "System.Double":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.Double);
case "System.Single":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.Float);
case "System.SByte":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.Int8);
case "System.Int16":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.Int16);
case "System.Int32":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.Int);
case "System.Int64":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.Int64);
case "System.Byte":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.Byte);
case "System.UInt16":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.UInt16);
case "System.UInt32":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.UInt32);
case "System.UInt64":
return new NativeDataBuiltinType(typeRef, arrayDim, PropertyType.UInt64);
case "System.Boolean":
return new NativeDataBooleanType(typeRef, arrayDim);
case "System.String":
return new NativeDataStringType(typeRef, arrayDim);
default:
if (typeRef.IsGenericInstance || typeRef.IsByReference)
{
GenericInstanceType? instanceType = null;
if (typeRef is GenericInstanceType genericInstanceType)
{
instanceType = genericInstanceType;
}
if (typeRef is ByReferenceType byReferenceType)
{
instanceType = byReferenceType.ElementType as GenericInstanceType;
typeRef = byReferenceType.ElementType;
}
if (instanceType != null)
{
TypeReference[] genericArguments = instanceType.GenericArguments.ToArray();
string? genericTypeName = instanceType.ElementType.Name;
if (genericTypeName.Contains("TArray`1") || genericTypeName.Contains("List`1"))
{
return new NativeDataArrayType(typeRef, arrayDim, genericArguments[0]);
}
if (genericTypeName.Contains("TNativeArray`1") || genericTypeName.Contains("ReadOnlySpan`1"))
{
return new NativeDataNativeArrayType(typeRef, arrayDim, genericArguments[0]);
}
if (genericTypeName.Contains("TMap`2") || genericTypeName.Contains("Dictionary`2"))
{
return new NativeDataMapType(typeRef, arrayDim, genericArguments[0], genericArguments[1]);
}
if (genericTypeName.Contains("TSet`1") || genericTypeName.Contains("HashSet`1"))
{
return new NativeDataSetType(typeRef, arrayDim, genericArguments[0]);
}
if (genericTypeName.Contains("TSubclassOf`1"))
{
return new NativeDataClassType(typeRef, genericArguments[0], arrayDim);
}
if (genericTypeName.Contains("TWeakObjectPtr`1"))
{
return new NativeDataWeakObjectType(typeRef, genericArguments[0], arrayDim);
}
if (genericTypeName.Contains("TSoftObjectPtr`1"))
{
return new NativeDataSoftObjectType(typeRef, genericArguments[0], arrayDim);
}
if (genericTypeName.Contains("TSoftClassPtr`1"))
{
return new NativeDataSoftClassType(typeRef, genericArguments[0], arrayDim);
}
if (genericTypeName.Contains("TOptional`1"))
{
return new NativeDataOptionalType(typeRef, genericArguments[0], arrayDim);
}
}
}
if (typeDef.IsEnum && typeDef.IsUEnum())
{
CustomAttribute? enumAttribute = typeDef.GetUEnum();
if (enumAttribute == null)
{
throw new InvalidPropertyException(propertyName, sequencePoint, "Enum properties must use an UEnum enum: " + typeRef.FullName);
}
// TODO: This is just true for properties, not for function parameters they can be int. Need a good way to differentiate.
// if (typeDef.GetEnumUnderlyingType().Resolve() != ByteTypeRef.Resolve())
// {
// throw new InvalidPropertyException(propertyName, sequencePoint, "Enum's exposed to Blueprints must have an underlying type of System.Byte: " + typeRef.FullName);
// }
return new NativeDataEnumType(typeDef, arrayDim);
}
if (typeDef.IsInterface && typeDef.IsUInterface())
{
return new NativeDataInterfaceType(typeRef, typeDef.Name + "Marshaller");
}
if (typeDef.FullName == "UnrealSharp.FText")
{
return new NativeDataTextType(typeDef);
}
if (typeDef.FullName == "UnrealSharp.FName")
{
return new NativeDataNameType(typeDef, arrayDim);
}
if (typeDef.Name == "TMulticastDelegate`1")
{
return new NativeDataMulticastDelegate(typeRef);
}
if (typeDef.Name == "TDelegate`1")
{
return new NativeDataDelegateType(typeRef);
}
if (customAttributes != null && NativeDataDefaultComponent.IsDefaultComponent(customAttributes))
{
return new NativeDataDefaultComponent(customAttributes, typeDef, arrayDim);
}
TypeDefinition? superType = typeDef;
while (superType != null && superType.FullName != "UnrealSharp.Core.UnrealSharpObject")
{
TypeReference superTypeRef = superType.BaseType;
superType = superTypeRef != null ? superTypeRef.Resolve() : null;
}
if (superType != null)
{
return new NativeDataObjectType(typeRef, typeDef, arrayDim);
}
// See if this is a struct
CustomAttribute? structAttribute = typeDef.GetUStruct();
if (structAttribute == null)
{
return typeDef.IsUnmanagedType() ? new NativeDataUnmanagedType(typeDef, arrayDim) : new NativeDataManagedObjectType(typeRef, arrayDim);
}
return typeDef.GetBlittableType() != null ? new NativeDataBlittableStructType(typeDef, arrayDim) : new NativeDataStructType(typeDef, "StructMarshaller`1", arrayDim);
}
}
public static string GetWrapperClassName(this TypeReference typeRef)
{
return typeRef.Name + "Wrapper";
}
public static string GetMarshallerClassName(this TypeReference typeRef)
{
return typeRef.Name + "Marshaller";
}
public static PropertyType GetPrimitiveTypeCode(this TypeReference type)
{
// Is there a better way to do this? The private member e_type on TypeReference has what we want
return type.FullName switch
{
"System.Byte" => PropertyType.Byte,
"System.SByte" => PropertyType.Int8,
"System.Int16" => PropertyType.Int16,
"System.UInt16" => PropertyType.UInt16,
"System.Int32" => PropertyType.Int,
"System.UInt32" => PropertyType.UInt32,
"System.Int64" => PropertyType.Int64,
"System.UInt64" => PropertyType.UInt64,
"System.Float" => PropertyType.Float,
"System.Double" => PropertyType.Double,
_ => throw new NotImplementedException()
};
}
public static GenericInstanceType AddMarshalledStructInterface(this TypeDefinition typeDefinition)
{
var marshalledStructRef = WeaverImporter.Instance.CurrentWeavingAssembly.MainModule.ImportReference(
WeaverImporter.Instance.MarshalledStructReference);
var instancedInterface = marshalledStructRef.MakeGenericInstanceType(typeDefinition);
ArgumentNullException.ThrowIfNull(instancedInterface);
if (typeDefinition.Interfaces.All(i => i.InterfaceType.FullName != instancedInterface.FullName))
{
typeDefinition.Interfaces.Add(new InterfaceImplementation(instancedInterface));
}
return instancedInterface;
}
}