MethodBridgeGenerator.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using UnityEditor;
  9. using UnityEngine;
  10. namespace HybridCLR.Generators.MethodBridge
  11. {
  12. public class TypeGenInfo
  13. {
  14. public Type Type { get; set; }
  15. public List<MethodInfo> GenericMethods { get; set; }
  16. }
  17. public class MethodBridgeGeneratorOptions
  18. {
  19. public List<Assembly> HotfixAssemblies { get; set; }
  20. public List<Assembly> AllAssemblies { get; set; }
  21. public PlatformABI CallConvention { get; set; }
  22. public string OutputFile { get; set; }
  23. }
  24. public class MethodBridgeGenerator
  25. {
  26. private readonly HashSet<Assembly> _hotfixAssemblies;
  27. private readonly List<Assembly> _assemblies;
  28. private readonly PlatformABI _callConvention;
  29. private readonly string _outputFile;
  30. private readonly IPlatformAdaptor _platformAdaptor;
  31. private readonly HashSet<MethodBridgeSig> _managed2nativeMethodSet = new HashSet<MethodBridgeSig>();
  32. private List<MethodBridgeSig> _managed2nativeMethodList;
  33. private readonly HashSet<MethodBridgeSig> _native2managedMethodSet = new HashSet<MethodBridgeSig>();
  34. private List<MethodBridgeSig> _native2managedMethodList;
  35. private readonly HashSet<MethodBridgeSig> _adjustThunkMethodSet = new HashSet<MethodBridgeSig>();
  36. private List<MethodBridgeSig> _adjustThunkMethodList;
  37. public bool IsHotFixType(Type type)
  38. {
  39. return _hotfixAssemblies.Contains(type.Assembly);
  40. }
  41. public MethodBridgeGenerator(MethodBridgeGeneratorOptions options)
  42. {
  43. _hotfixAssemblies = new HashSet<Assembly>(options.HotfixAssemblies);
  44. _assemblies = options.AllAssemblies;
  45. _callConvention = options.CallConvention;
  46. _outputFile = options.OutputFile;
  47. _platformAdaptor = CreatePlatformAdaptor(options.CallConvention);
  48. }
  49. private static IPlatformAdaptor CreatePlatformAdaptor(PlatformABI type)
  50. {
  51. return type switch
  52. {
  53. PlatformABI.Universal32 => new PlatformAdaptor_Universal32(),
  54. PlatformABI.Universal64 => new PlatformAdaptor_Universal64(),
  55. PlatformABI.Arm64 => new PlatformAdaptor_Arm64(),
  56. _ => throw new NotSupportedException(),
  57. };
  58. }
  59. private string GetTemplateFile()
  60. {
  61. string tplFile = _callConvention switch
  62. {
  63. PlatformABI.Universal32 => "Universal32",
  64. PlatformABI.Universal64 => "Universal64",
  65. PlatformABI.Arm64 => "Arm64",
  66. _ => throw new NotSupportedException(),
  67. };
  68. return $"{Application.dataPath}/Editor/HybridCLR/Generators/Templates/MethodBridge_{tplFile}.cpp";
  69. }
  70. public IEnumerable<TypeGenInfo> GetGenerateTypes()
  71. {
  72. return new List<TypeGenInfo>();
  73. }
  74. private MethodBridgeSig CreateMethodBridgeSig(bool isStatic, ParameterInfo returnType, ParameterInfo[] parameters)
  75. {
  76. var paramInfos = new List<ParamInfo>();
  77. if (!isStatic)
  78. {
  79. // FIXME arm32 is s_i4u4
  80. paramInfos.Add(new ParamInfo() { Type = _platformAdaptor.IsArch32 ? TypeInfo.s_i4u4 : TypeInfo.s_i8u8 });
  81. }
  82. foreach (var paramInfo in parameters)
  83. {
  84. paramInfos.Add(new ParamInfo() { Type = _platformAdaptor.CreateTypeInfo(paramInfo.ParameterType, false) });
  85. }
  86. var mbs = new MethodBridgeSig()
  87. {
  88. ReturnInfo = new ReturnInfo() { Type = returnType != null ? _platformAdaptor.CreateTypeInfo(returnType.ParameterType, true) : TypeInfo.s_void },
  89. ParamInfos = paramInfos,
  90. };
  91. return mbs;
  92. }
  93. private void AddManaged2NativeMethod(MethodBridgeSig method)
  94. {
  95. if (_managed2nativeMethodSet.Add(method))
  96. {
  97. method.Init();
  98. }
  99. }
  100. private void AddNative2ManagedMethod(MethodBridgeSig method)
  101. {
  102. if (_native2managedMethodSet.Add(method))
  103. {
  104. method.Init();
  105. }
  106. }
  107. private void AddAdjustThunkMethod(MethodBridgeSig method)
  108. {
  109. if (_adjustThunkMethodSet.Add(method))
  110. {
  111. method.Init();
  112. }
  113. }
  114. private void ScanType(Type type)
  115. {
  116. if (type.IsGenericTypeDefinition)
  117. {
  118. return;
  119. }
  120. if (!type.IsNested)
  121. {
  122. if (!type.IsPublic)
  123. {
  124. return;
  125. }
  126. }
  127. else
  128. {
  129. if (type.IsNestedPrivate)
  130. {
  131. return;
  132. }
  133. }
  134. var typeDel = typeof(MulticastDelegate);
  135. if (typeDel.IsAssignableFrom(type))
  136. {
  137. var method = type.GetMethod("Invoke");
  138. if (method == null)
  139. {
  140. //Debug.LogError($"delegate:{typeDel.FullName} Invoke not exists");
  141. return;
  142. }
  143. // Debug.Log($"== delegate:{type}");
  144. var instanceCallMethod = CreateMethodBridgeSig(false, method.ReturnParameter, method.GetParameters());
  145. AddManaged2NativeMethod(instanceCallMethod);
  146. AddNative2ManagedMethod(instanceCallMethod);
  147. var staticCallMethod = CreateMethodBridgeSig(true, method.ReturnParameter, method.GetParameters());
  148. AddManaged2NativeMethod(staticCallMethod);
  149. AddNative2ManagedMethod(staticCallMethod);
  150. return;
  151. }
  152. foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public
  153. | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy))
  154. {
  155. if (method.IsGenericMethodDefinition)
  156. {
  157. continue;
  158. }
  159. if (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily))
  160. {
  161. continue;
  162. }
  163. if (method.IsFamily || method.IsPublic)
  164. {
  165. var m2nMethod = CreateMethodBridgeSig(method.IsStatic, method.ReturnParameter, method.GetParameters());
  166. AddManaged2NativeMethod(m2nMethod);
  167. if (type.IsValueType && !method.IsStatic)
  168. {
  169. var adjustThunkMethod = CreateMethodBridgeSig(true, method.ReturnParameter, method.GetParameters());
  170. AddAdjustThunkMethod(adjustThunkMethod);
  171. }
  172. if (method.IsVirtual)
  173. {
  174. AddNative2ManagedMethod(m2nMethod);
  175. }
  176. }
  177. }
  178. foreach (var method in type.GetConstructors(BindingFlags.Instance | BindingFlags.Public
  179. | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy))
  180. {
  181. if (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily))
  182. {
  183. continue;
  184. }
  185. if (method.IsFamily || method.IsPublic)
  186. {
  187. var callMethod = CreateMethodBridgeSig(false, null, method.GetParameters());
  188. AddManaged2NativeMethod(callMethod);
  189. if (type.IsValueType && !method.IsStatic)
  190. {
  191. var invokeMethod = CreateMethodBridgeSig(true, null, method.GetParameters());
  192. AddAdjustThunkMethod(invokeMethod);
  193. }
  194. }
  195. }
  196. foreach (var subType in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
  197. {
  198. ScanType(subType);
  199. }
  200. }
  201. public void PrepareFromAssemblies()
  202. {
  203. foreach (var ass in _assemblies)
  204. {
  205. if (_hotfixAssemblies.Contains(ass))
  206. {
  207. continue;
  208. }
  209. //Debug.Log("prepare assembly:" + ass.FullName);
  210. foreach (var type in ass.GetTypes())
  211. {
  212. ScanType(type);
  213. }
  214. }
  215. }
  216. private void PrepareMethodsFromCustomeGenericTypes()
  217. {
  218. foreach (var type in GeneratorConfig.PrepareCustomGenericTypes())
  219. {
  220. ScanType(type);
  221. }
  222. }
  223. public void PrepareMethods()
  224. {
  225. PrepareMethodsFromCustomeGenericTypes();
  226. foreach(var methodSig in _platformAdaptor.IsArch32 ? GeneratorConfig.PrepareCustomMethodSignatures32() : GeneratorConfig.PrepareCustomMethodSignatures64())
  227. {
  228. var method = MethodBridgeSig.CreateBySignatuer(methodSig);
  229. AddManaged2NativeMethod(method);
  230. AddAdjustThunkMethod(method);
  231. }
  232. PrepareFromAssemblies();
  233. {
  234. var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
  235. foreach (var method in _managed2nativeMethodSet)
  236. {
  237. sortedMethods.Add(method.CreateCallSigName(), method);
  238. }
  239. _managed2nativeMethodList = sortedMethods.Values.ToList();
  240. }
  241. {
  242. var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
  243. foreach (var method in _native2managedMethodSet)
  244. {
  245. sortedMethods.Add(method.CreateCallSigName(), method);
  246. }
  247. _native2managedMethodList = sortedMethods.Values.ToList();
  248. }
  249. {
  250. var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
  251. foreach (var method in _adjustThunkMethodSet)
  252. {
  253. sortedMethods.Add(method.CreateCallSigName(), method);
  254. }
  255. _adjustThunkMethodList = sortedMethods.Values.ToList();
  256. }
  257. }
  258. public void Generate()
  259. {
  260. var frr = new FileRegionReplace(GetTemplateFile());
  261. List<string> lines = new List<string>(20_0000);
  262. Debug.LogFormat("== managed2native method count:{0}", _managed2nativeMethodList.Count);
  263. foreach(var method in _managed2nativeMethodList)
  264. {
  265. _platformAdaptor.GenerateManaged2NativeMethod(method, lines);
  266. }
  267. _platformAdaptor.GenerateManaged2NativeStub(_managed2nativeMethodList, lines);
  268. Debug.LogFormat("== native2managed method count:{0}", _native2managedMethodList.Count);
  269. foreach (var method in _native2managedMethodList)
  270. {
  271. _platformAdaptor.GenerateNative2ManagedMethod(method, lines);
  272. }
  273. _platformAdaptor.GenerateNative2ManagedStub(_native2managedMethodList, lines);
  274. Debug.LogFormat("== adjustThunk method count:{0}", _adjustThunkMethodList.Count);
  275. foreach (var method in _adjustThunkMethodList)
  276. {
  277. _platformAdaptor.GenerateAdjustThunkMethod(method, lines);
  278. }
  279. _platformAdaptor.GenerateAdjustThunkStub(_adjustThunkMethodList, lines);
  280. frr.Replace("INVOKE_STUB", string.Join("\n", lines));
  281. Directory.CreateDirectory(Path.GetDirectoryName(_outputFile));
  282. frr.Commit(_outputFile);
  283. }
  284. }
  285. }