From 45749ffb5cc36550589bdd58bbc72ca71d3e7b87 Mon Sep 17 00:00:00 2001 From: walon Date: Thu, 2 Jan 2025 21:58:03 +0800 Subject: [PATCH] [new] add Managed2NativeFunctionPointer MethodBridge functions --- .../Commands/MethodBridgeGeneratorCommand.cs | 8 +- Editor/MethodBridge/CalliAnalyzer.cs | 70 ++++++++ Editor/MethodBridge/CalliAnalyzer.cs.meta | 11 ++ Editor/MethodBridge/Generator.cs | 158 ++++++++++++++++-- Editor/ReversePInvokeWrap/Analyzer.cs | 11 -- 5 files changed, 230 insertions(+), 28 deletions(-) create mode 100644 Editor/MethodBridge/CalliAnalyzer.cs create mode 100644 Editor/MethodBridge/CalliAnalyzer.cs.meta diff --git a/Editor/Commands/MethodBridgeGeneratorCommand.cs b/Editor/Commands/MethodBridgeGeneratorCommand.cs index 806f726..c5130a4 100644 --- a/Editor/Commands/MethodBridgeGeneratorCommand.cs +++ b/Editor/Commands/MethodBridgeGeneratorCommand.cs @@ -31,7 +31,7 @@ namespace HybridCLR.Editor.Commands Directory.Delete(il2cppBuildCachePath, true); } - private static void GenerateMethodBridgeCppFile(IReadOnlyCollection genericMethods, List reversePInvokeMethods, string outputFile) + private static void GenerateMethodBridgeCppFile(IReadOnlyCollection genericMethods, List reversePInvokeMethods, IReadOnlyCollection calliMethodSignatures, string outputFile) { string templateCode = File.ReadAllText(outputFile, Encoding.UTF8); var g = new Generator(new Generator.Options() @@ -40,6 +40,7 @@ namespace HybridCLR.Editor.Commands OutputFile = outputFile, GenericMethods = genericMethods, ReversePInvokeMethods = reversePInvokeMethods, + CalliMethodSignatures = calliMethodSignatures, Development = EditorUserBuildSettings.development, }); @@ -80,9 +81,12 @@ namespace HybridCLR.Editor.Commands var reversePInvokeAnalyzer = new ReversePInvokeWrap.Analyzer(cache, hotUpdateDlls); reversePInvokeAnalyzer.Run(); + var calliAnalyzer = new CalliAnalyzer(cache, hotUpdateDlls); + calliAnalyzer.Run(); + string outputFile = $"{SettingsUtil.GeneratedCppDir}/MethodBridge.cpp"; - GenerateMethodBridgeCppFile(methodBridgeAnalyzer.GenericMethods, reversePInvokeAnalyzer.ReversePInvokeMethods, outputFile); + GenerateMethodBridgeCppFile(methodBridgeAnalyzer.GenericMethods, reversePInvokeAnalyzer.ReversePInvokeMethods, calliAnalyzer.CalliMethodSignatures, outputFile); CleanIl2CppBuildCache(); } diff --git a/Editor/MethodBridge/CalliAnalyzer.cs b/Editor/MethodBridge/CalliAnalyzer.cs new file mode 100644 index 0000000..2027135 --- /dev/null +++ b/Editor/MethodBridge/CalliAnalyzer.cs @@ -0,0 +1,70 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridge +{ + + public class RawCalliMethodSignatureInfo + { + public MethodSig MethodSig { get; set; } + } + + public class CalliAnalyzer + { + private readonly List _rootModules = new List(); + + private readonly List _calliMethodSignatures = new List(); + + public List CalliMethodSignatures => _calliMethodSignatures; + + public CalliAnalyzer(AssemblyCache cache, List assemblyNames) + { + foreach (var assemblyName in assemblyNames) + { + _rootModules.Add(cache.LoadModule(assemblyName)); + } + } + + private void CollectCalli() + { + foreach (var mod in _rootModules) + { + Debug.Log($"ass:{mod.FullName} methodcount:{mod.Metadata.TablesStream.MethodTable.Rows}"); + for (uint rid = 1, n = mod.Metadata.TablesStream.MethodTable.Rows; rid <= n; rid++) + { + var method = mod.ResolveMethod(rid); + //Debug.Log($"method:{method}"); + if (!method.HasBody) + { + continue; + } + + foreach (var il in method.Body.Instructions) + { + if (il.OpCode.Code == dnlib.DotNet.Emit.Code.Calli) + { + MethodSig methodSig = (MethodSig)il.Operand; + + _calliMethodSignatures.Add(new RawCalliMethodSignatureInfo() + { + MethodSig = methodSig, + }); + Debug.Log($"method:{method} calli method signature:{methodSig}"); + } + } + } + } + } + + public void Run() + { + CollectCalli(); + } + } +} diff --git a/Editor/MethodBridge/CalliAnalyzer.cs.meta b/Editor/MethodBridge/CalliAnalyzer.cs.meta new file mode 100644 index 0000000..620c7c7 --- /dev/null +++ b/Editor/MethodBridge/CalliAnalyzer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ce33c8e48da5a649b261ba3a60fd3b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridge/Generator.cs b/Editor/MethodBridge/Generator.cs index 685be91..b2fccce 100644 --- a/Editor/MethodBridge/Generator.cs +++ b/Editor/MethodBridge/Generator.cs @@ -15,7 +15,6 @@ using UnityEditor; using UnityEngine; using TypeInfo = HybridCLR.Editor.ABI.TypeInfo; using CallingConvention = System.Runtime.InteropServices.CallingConvention; -using System.Security.Cryptography; using TypeAttributes = dnlib.DotNet.TypeAttributes; namespace HybridCLR.Editor.MethodBridge @@ -32,13 +31,37 @@ namespace HybridCLR.Editor.MethodBridge public List ReversePInvokeMethods { get; set; } + public IReadOnlyCollection CalliMethodSignatures { get; set; } + public bool Development { get; set; } } + private class ABIReversePInvokeMethodInfo + { + public MethodDesc Method { get; set; } + + public CallingConvention Callvention { get; set; } + + public int Count { get; set; } + + public string Signature { get; set; } + } + + private class CalliMethodInfo + { + public MethodDesc Method { get; set; } + + public CallingConvention Callvention { get; set; } + + public string Signature { get; set; } + } + private readonly List _genericMethods; private readonly List _originalReversePInvokeMethods; + private readonly List _originalCalliMethodSignatures; + private readonly string _templateCode; private readonly string _outputFile; @@ -55,13 +78,16 @@ namespace HybridCLR.Editor.MethodBridge private List _reversePInvokeMethods; + private List _callidMethods; + public Generator(Options options) { List<(GenericMethod, string)> genericMethodInfo = options.GenericMethods.Select(m => (m, m.ToString())).ToList(); genericMethodInfo.Sort((a, b) => string.CompareOrdinal(a.Item2, b.Item2)); _genericMethods = genericMethodInfo.Select(m => m.Item1).ToList(); _originalReversePInvokeMethods = options.ReversePInvokeMethods; - + _originalCalliMethodSignatures = options.CalliMethodSignatures.ToList(); + _templateCode = options.TemplateCode; _outputFile = options.OutputFile; _typeCreator = new TypeCreator(); @@ -114,6 +140,30 @@ namespace HybridCLR.Editor.MethodBridge return mbs; } + private MethodDesc CreateMethodDesc(TypeSig returnType, List parameters) + { + var paramInfos = new List(); + if (returnType.ContainsGenericParameter) + { + throw new Exception($"[PreservedMethod] method has generic parameters"); + } + foreach (var paramInfo in parameters) + { + if (paramInfo.ContainsGenericParameter) + { + throw new Exception($"[PreservedMethod] method has generic parameters"); + } + paramInfos.Add(new ParamInfo() { Type = GetSharedTypeInfo(paramInfo) }); + } + var mbs = new MethodDesc() + { + MethodDef = null, + ReturnInfo = new ReturnInfo() { Type = returnType != null ? GetSharedTypeInfo(returnType) : TypeInfo.s_void }, + ParamInfos = paramInfos, + }; + return mbs; + } + private void AddManaged2NativeMethod(MethodDesc method) { method.Init(); @@ -422,11 +472,16 @@ namespace HybridCLR.Editor.MethodBridge - private static string MakeSignature(MethodDesc desc, CallingConvention CallingConventionention) + private static string MakeReversePInvokeSignature(MethodDesc desc, CallingConvention CallingConventionention) { string convStr = ((char)('A' + (int)CallingConventionention - 1)).ToString(); return $"{convStr}{desc.Sig}"; } + private static string MakeCalliSignature(MethodDesc desc, CallingConvention CallingConventionention) + { + string convStr = ((char)('A' + Math.Max((int)CallingConventionention - 1, 0))).ToString(); + return $"{convStr}{desc.Sig}"; + } private static CallingConvention GetCallingConvention(MethodDef method) { @@ -479,7 +534,7 @@ namespace HybridCLR.Editor.MethodBridge sharedMethod = ToIsomorphicMethod(sharedMethod); CallingConvention callingConv = GetCallingConvention(method.Method); - string signature = MakeSignature(sharedMethod, callingConv); + string signature = MakeReversePInvokeSignature(sharedMethod, callingConv); if (!methodsBySig.TryGetValue(signature, out var arm)) { @@ -500,12 +555,46 @@ namespace HybridCLR.Editor.MethodBridge return newMethods; } + private List BuildCalliMethods(List rawMethods) + { + var methodsBySig = new Dictionary(); + foreach (var method in rawMethods) + { + var sharedMethod = new MethodDesc + { + MethodDef = null, + ReturnInfo = new ReturnInfo { Type = _typeCreator.CreateTypeInfo(method.MethodSig.RetType) }, + ParamInfos = method.MethodSig.Params.Select(p => new ParamInfo { Type = _typeCreator.CreateTypeInfo(p) }).ToList(), + }; + sharedMethod.Init(); + sharedMethod = ToIsomorphicMethod(sharedMethod); + + CallingConvention callingConv = (CallingConvention)(method.MethodSig.CallingConvention); + string signature = MakeCalliSignature(sharedMethod, callingConv); + + if (!methodsBySig.TryGetValue(signature, out var arm)) + { + arm = new CalliMethodInfo() + { + Method = sharedMethod, + Signature = signature, + Callvention = callingConv, + }; + methodsBySig.Add(signature, arm); + } + } + var newMethods = methodsBySig.Values.ToList(); + newMethods.Sort((a, b) => string.CompareOrdinal(a.Signature, b.Signature)); + return newMethods; + } + private void BuildOptimizedMethods() { _managed2NativeMethodList = ToUniqueOrderedList(_managed2NativeMethodList0); _native2ManagedMethodList = ToUniqueOrderedList(_native2ManagedMethodList0); _adjustThunkMethodList = ToUniqueOrderedList(_adjustThunkMethodList0); _reversePInvokeMethods = BuildABIMethods(_originalReversePInvokeMethods); + _callidMethods = BuildCalliMethods(_originalCalliMethodSignatures); } private void OptimizationTypesAndMethods() @@ -562,6 +651,13 @@ namespace HybridCLR.Editor.MethodBridge GenerateReversePInvokeWrappers(_reversePInvokeMethods, lines); + foreach (var method in _callidMethods) + { + GenerateManaged2NativeFunctionPointerMethod(method, lines); + } + GenerateManaged2NativeFunctionPointerMethodStub(_callidMethods, lines); + + frr.Replace("CODE", string.Join("\n", lines)); Directory.CreateDirectory(Path.GetDirectoryName(_outputFile)); @@ -778,9 +874,9 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu } } - public const string SigOfObj = "u"; + private const string SigOfObj = "u"; - public static string ToFullName(TypeSig type) + private static string ToFullName(TypeSig type) { type = type.RemovePinnedAndModifiers(); switch (type.ElementType) @@ -875,7 +971,7 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu return $"{Path.GetFileNameWithoutExtension(typeDef.Module.Name)}:{typeDef.FullName}"; } - public void GenerateStructureSignatureStub(List types, List lines) + private void GenerateStructureSignatureStub(List types, List lines) { lines.Add("const FullName2Signature hybridclr::interpreter::g_fullName2SignatureStub[] = {"); foreach (var type in types) @@ -887,7 +983,7 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu lines.Add("};"); } - public void GenerateManaged2NativeStub(List methods, List lines) + private void GenerateManaged2NativeStub(List methods, List lines) { lines.Add($@" const Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] = @@ -903,7 +999,7 @@ const Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] = lines.Add("};"); } - public void GenerateNative2ManagedStub(List methods, List lines) + private void GenerateNative2ManagedStub(List methods, List lines) { lines.Add($@" const Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] = @@ -919,7 +1015,7 @@ const Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] = lines.Add("};"); } - public void GenerateAdjustThunkStub(List methods, List lines) + private void GenerateAdjustThunkStub(List methods, List lines) { lines.Add($@" const NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] = @@ -945,7 +1041,7 @@ const NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] = return type.NeedExpandValue() ? $"(uint64_t)({varName})" : $"N2MAsUint64ValueOrAddress<{type.GetTypeName()}>({varName})"; } - public void GenerateManaged2NativeMethod(MethodDesc method, List lines) + private void GenerateManaged2NativeMethod(MethodDesc method, List lines) { string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => GetManaged2NativePassParam(p.Type, $"localVarBase+argVarIndexs[{p.Index}]")).Concat(new string[] { "method" })); @@ -959,7 +1055,7 @@ static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_ "); } - public string GenerateArgumentSizeAndOffset(List paramInfos) + private string GenerateArgumentSizeAndOffset(List paramInfos) { StringBuilder s = new StringBuilder(); int index = 0; @@ -973,7 +1069,7 @@ static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_ return s.ToString(); } - public string GenerateCopyArgumentToInterpreterStack(List paramInfos) + private string GenerateCopyArgumentToInterpreterStack(List paramInfos) { StringBuilder s = new StringBuilder(); int index = 0; @@ -1014,14 +1110,46 @@ static {method.ReturnInfo.Type.GetTypeName()} __N2M_{(adjustorThunk ? "AdjustorT "); } - public void GenerateNative2ManagedMethod(MethodDesc method, List lines) + private void GenerateNative2ManagedMethod(MethodDesc method, List lines) { GenerateNative2ManagedMethod0(method, false, lines); } - public void GenerateAdjustThunkMethod(MethodDesc method, List lines) + private void GenerateAdjustThunkMethod(MethodDesc method, List lines) { GenerateNative2ManagedMethod0(method, true, lines); } + + private void GenerateManaged2NativeFunctionPointerMethod(CalliMethodInfo methodInfo, List lines) + { + MethodDesc method = methodInfo.Method; + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}")); + string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => GetManaged2NativePassParam(p.Type, $"localVarBase+argVarIndexs[{p.Index}]"))); + string il2cppCallConventionName = GetIl2cppCallConventionName(methodInfo.Callvention); + lines.Add($@" +static void __M2NF_{methodInfo.Signature}(const void* methodPointer, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret) +{{ + typedef {method.ReturnInfo.Type.GetTypeName()} ({il2cppCallConventionName} *NativeMethod)({paramListStr}); + {(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(methodPointer))({paramNameListStr}); +}} +"); + } + + private void GenerateManaged2NativeFunctionPointerMethodStub(List calliMethodSignatures, List lines) + { + lines.Add(@" +const Managed2NativeFunctionPointerCallData hybridclr::interpreter::g_managed2NativeFunctionPointerCallStub[] +{ +"); + foreach (var method in calliMethodSignatures) + { + lines.Add($"\t{{\"{method.Signature}\", __M2NF_{method.Signature}}},"); + } + + lines.Add(@" + {nullptr, nullptr}, +}; +"); + } } } diff --git a/Editor/ReversePInvokeWrap/Analyzer.cs b/Editor/ReversePInvokeWrap/Analyzer.cs index 217b3c8..0004d18 100644 --- a/Editor/ReversePInvokeWrap/Analyzer.cs +++ b/Editor/ReversePInvokeWrap/Analyzer.cs @@ -18,17 +18,6 @@ namespace HybridCLR.Editor.ReversePInvokeWrap public CustomAttribute GenerationAttribute { get; set; } } - public class ABIReversePInvokeMethodInfo - { - public MethodDesc Method { get; set; } - - public CallingConvention Callvention { get; set; } - - public int Count { get; set; } - - public string Signature { get; set; } - } - public class Analyzer {