Generator.cs 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. using dnlib.DotNet;
  2. using HybridCLR.Editor.ABI;
  3. using HybridCLR.Editor.Meta;
  4. using HybridCLR.Editor.ReversePInvokeWrap;
  5. using HybridCLR.Editor.Template;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.ComponentModel.DataAnnotations;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Reflection;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using UnityEditor;
  15. using UnityEngine;
  16. using TypeInfo = HybridCLR.Editor.ABI.TypeInfo;
  17. using CallingConvention = System.Runtime.InteropServices.CallingConvention;
  18. using System.Security.Cryptography;
  19. using TypeAttributes = dnlib.DotNet.TypeAttributes;
  20. namespace HybridCLR.Editor.MethodBridge
  21. {
  22. public class Generator
  23. {
  24. public class Options
  25. {
  26. public string TemplateCode { get; set; }
  27. public string OutputFile { get; set; }
  28. public IReadOnlyCollection<GenericMethod> GenericMethods { get; set; }
  29. public List<RawReversePInvokeMethodInfo> ReversePInvokeMethods { get; set; }
  30. public bool Development { get; set; }
  31. }
  32. private readonly List<GenericMethod> _genericMethods;
  33. private readonly List<RawReversePInvokeMethodInfo> _originalReversePInvokeMethods;
  34. private readonly string _templateCode;
  35. private readonly string _outputFile;
  36. private readonly bool _development;
  37. private readonly TypeCreator _typeCreator;
  38. private readonly HashSet<MethodDesc> _managed2nativeMethodSet = new HashSet<MethodDesc>();
  39. private readonly HashSet<MethodDesc> _native2managedMethodSet = new HashSet<MethodDesc>();
  40. private readonly HashSet<MethodDesc> _adjustThunkMethodSet = new HashSet<MethodDesc>();
  41. private List<ABIReversePInvokeMethodInfo> _reversePInvokeMethods;
  42. public Generator(Options options)
  43. {
  44. List<(GenericMethod, string)> genericMethodInfo = options.GenericMethods.Select(m => (m, m.ToString())).ToList();
  45. genericMethodInfo.Sort((a, b) => string.CompareOrdinal(a.Item2, b.Item2));
  46. _genericMethods = genericMethodInfo.Select(m => m.Item1).ToList();
  47. _originalReversePInvokeMethods = options.ReversePInvokeMethods;
  48. _templateCode = options.TemplateCode;
  49. _outputFile = options.OutputFile;
  50. _typeCreator = new TypeCreator();
  51. _development = options.Development;
  52. }
  53. private readonly Dictionary<string, TypeInfo> _sig2Types = new Dictionary<string, TypeInfo>();
  54. private TypeInfo GetSharedTypeInfo(TypeSig type)
  55. {
  56. var typeInfo = _typeCreator.CreateTypeInfo(type);
  57. if (!typeInfo.IsStruct)
  58. {
  59. return typeInfo;
  60. }
  61. string sigName = ToFullName(typeInfo.Klass);
  62. if (!_sig2Types.TryGetValue(sigName, out var sharedTypeInfo))
  63. {
  64. sharedTypeInfo = typeInfo;
  65. _sig2Types.Add(sigName, sharedTypeInfo);
  66. }
  67. return sharedTypeInfo;
  68. }
  69. private MethodDesc CreateMethodDesc(MethodDef methodDef, bool forceRemoveThis, TypeSig returnType, List<TypeSig> parameters)
  70. {
  71. var paramInfos = new List<ParamInfo>();
  72. if (forceRemoveThis && !methodDef.IsStatic)
  73. {
  74. parameters.RemoveAt(0);
  75. }
  76. if (returnType.ContainsGenericParameter)
  77. {
  78. throw new Exception($"[PreservedMethod] method:{methodDef} has generic parameters");
  79. }
  80. foreach (var paramInfo in parameters)
  81. {
  82. if (paramInfo.ContainsGenericParameter)
  83. {
  84. throw new Exception($"[PreservedMethod] method:{methodDef} has generic parameters");
  85. }
  86. paramInfos.Add(new ParamInfo() { Type = GetSharedTypeInfo(paramInfo) });
  87. }
  88. var mbs = new MethodDesc()
  89. {
  90. MethodDef = methodDef,
  91. ReturnInfo = new ReturnInfo() { Type = returnType != null ? GetSharedTypeInfo(returnType) : TypeInfo.s_void },
  92. ParamInfos = paramInfos,
  93. };
  94. return mbs;
  95. }
  96. private void AddManaged2NativeMethod(MethodDesc method)
  97. {
  98. method.Init();
  99. _managed2nativeMethodSet.Add(method);
  100. }
  101. private void AddNative2ManagedMethod(MethodDesc method)
  102. {
  103. method.Init();
  104. _native2managedMethodSet.Add(method);
  105. }
  106. private void AddAdjustThunkMethod(MethodDesc method)
  107. {
  108. method.Init();
  109. _adjustThunkMethodSet.Add(method);
  110. }
  111. private void ProcessMethod(MethodDef method, List<TypeSig> klassInst, List<TypeSig> methodInst)
  112. {
  113. if (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily))
  114. {
  115. if (klassInst == null && methodInst == null)
  116. {
  117. return;
  118. }
  119. else
  120. {
  121. //Debug.Log($"[PreservedMethod] method:{method}");
  122. }
  123. }
  124. ICorLibTypes corLibTypes = method.Module.CorLibTypes;
  125. TypeSig returnType;
  126. List<TypeSig> parameters;
  127. if (klassInst == null && methodInst == null)
  128. {
  129. if (method.HasGenericParameters)
  130. {
  131. throw new Exception($"[PreservedMethod] method:{method} has generic parameters");
  132. }
  133. returnType = MetaUtil.ToShareTypeSig(corLibTypes, method.ReturnType);
  134. parameters = method.Parameters.Select(p => MetaUtil.ToShareTypeSig(corLibTypes, p.Type)).ToList();
  135. }
  136. else
  137. {
  138. var gc = new GenericArgumentContext(klassInst, methodInst);
  139. returnType = MetaUtil.ToShareTypeSig(corLibTypes, MetaUtil.Inflate(method.ReturnType, gc));
  140. parameters = method.Parameters.Select(p => MetaUtil.ToShareTypeSig(corLibTypes, MetaUtil.Inflate(p.Type, gc))).ToList();
  141. }
  142. var m2nMethod = CreateMethodDesc(method, false, returnType, parameters);
  143. AddManaged2NativeMethod(m2nMethod);
  144. if (method.IsVirtual)
  145. {
  146. if (method.DeclaringType.IsInterface)
  147. {
  148. AddAdjustThunkMethod(m2nMethod);
  149. }
  150. //var adjustThunkMethod = CreateMethodDesc(method, true, returnType, parameters);
  151. AddNative2ManagedMethod(m2nMethod);
  152. }
  153. if (method.Name == "Invoke" && method.DeclaringType.IsDelegate)
  154. {
  155. var openMethod = CreateMethodDesc(method, true, returnType, parameters);
  156. AddNative2ManagedMethod(openMethod);
  157. }
  158. }
  159. private void PrepareMethodBridges()
  160. {
  161. foreach (var method in _genericMethods)
  162. {
  163. ProcessMethod(method.Method, method.KlassInst, method.MethodInst);
  164. }
  165. foreach (var reversePInvokeMethod in _originalReversePInvokeMethods)
  166. {
  167. MethodDef method = reversePInvokeMethod.Method;
  168. ICorLibTypes corLibTypes = method.Module.CorLibTypes;
  169. var returnType = MetaUtil.ToShareTypeSig(corLibTypes, method.ReturnType);
  170. var parameters = method.Parameters.Select(p => MetaUtil.ToShareTypeSig(corLibTypes, p.Type)).ToList();
  171. var sharedMethod = CreateMethodDesc(method, true, returnType, parameters);
  172. sharedMethod.Init();
  173. AddNative2ManagedMethod(sharedMethod);
  174. }
  175. }
  176. static void CheckUnique(IEnumerable<string> names)
  177. {
  178. var set = new HashSet<string>();
  179. foreach (var name in names)
  180. {
  181. if (!set.Add(name))
  182. {
  183. throw new Exception($"[CheckUnique] duplicate name:{name}");
  184. }
  185. }
  186. }
  187. private List<MethodDesc> _managed2NativeMethodList0;
  188. private List<MethodDesc> _native2ManagedMethodList0;
  189. private List<MethodDesc> _adjustThunkMethodList0;
  190. private List<TypeInfo> _structTypes0;
  191. private void CollectTypesAndMethods()
  192. {
  193. _managed2NativeMethodList0 = _managed2nativeMethodSet.ToList();
  194. _managed2NativeMethodList0.Sort((a, b) => string.CompareOrdinal(a.Sig, b.Sig));
  195. _native2ManagedMethodList0 = _native2managedMethodSet.ToList();
  196. _native2ManagedMethodList0.Sort((a, b) => string.CompareOrdinal(a.Sig, b.Sig));
  197. _adjustThunkMethodList0 = _adjustThunkMethodSet.ToList();
  198. _adjustThunkMethodList0.Sort((a, b) => string.CompareOrdinal(a.Sig, b.Sig));
  199. var structTypeSet = new HashSet<TypeInfo>();
  200. CollectStructDefs(_managed2NativeMethodList0, structTypeSet);
  201. CollectStructDefs(_native2ManagedMethodList0, structTypeSet);
  202. CollectStructDefs(_adjustThunkMethodList0, structTypeSet);
  203. _structTypes0 = structTypeSet.ToList();
  204. _structTypes0.Sort((a, b) => a.TypeId - b.TypeId);
  205. CheckUnique(_structTypes0.Select(t => ToFullName(t.Klass)));
  206. CheckUnique(_structTypes0.Select(t => t.CreateSigName()));
  207. Debug.LogFormat("== before optimization struct:{3} managed2native:{0} native2managed:{1} adjustThunk:{2}",
  208. _managed2NativeMethodList0.Count, _native2ManagedMethodList0.Count, _adjustThunkMethodList0.Count, _structTypes0.Count);
  209. }
  210. private class AnalyzeFieldInfo
  211. {
  212. public FieldDef field;
  213. public TypeInfo type;
  214. }
  215. private class AnalyzeTypeInfo
  216. {
  217. public TypeInfo toSharedType;
  218. public List<AnalyzeFieldInfo> fields;
  219. public string signature;
  220. public ClassLayout classLayout;
  221. public TypeAttributes layout;
  222. }
  223. private readonly Dictionary<TypeInfo, AnalyzeTypeInfo> _analyzeTypeInfos = new Dictionary<TypeInfo, AnalyzeTypeInfo>();
  224. private readonly Dictionary<string, TypeInfo> _signature2Type = new Dictionary<string, TypeInfo>();
  225. private AnalyzeTypeInfo CalculateAnalyzeTypeInfoBasic(TypeInfo typeInfo)
  226. {
  227. TypeSig type = typeInfo.Klass;
  228. TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow();
  229. List<TypeSig> klassInst = type.ToGenericInstSig()?.GenericArguments?.ToList();
  230. GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
  231. ClassLayout sa = typeDef.ClassLayout;
  232. var analyzeTypeInfo = new AnalyzeTypeInfo()
  233. {
  234. classLayout = sa,
  235. layout = typeDef.Layout,
  236. };
  237. // don't share type with explicit layout
  238. if (sa != null)
  239. {
  240. analyzeTypeInfo.toSharedType = typeInfo;
  241. analyzeTypeInfo.signature = typeInfo.CreateSigName();
  242. _signature2Type.Add(analyzeTypeInfo.signature, typeInfo);
  243. return analyzeTypeInfo;
  244. }
  245. var fields = analyzeTypeInfo.fields = new List<AnalyzeFieldInfo>();
  246. foreach (FieldDef field in typeDef.Fields)
  247. {
  248. if (field.IsStatic)
  249. {
  250. continue;
  251. }
  252. TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType;
  253. fields.Add(new AnalyzeFieldInfo { field = field, type = GetSharedTypeInfo(fieldType) });
  254. }
  255. return analyzeTypeInfo;
  256. }
  257. private string GetOrCalculateTypeInfoSignature(TypeInfo typeInfo)
  258. {
  259. if (!typeInfo.IsStruct)
  260. {
  261. return typeInfo.CreateSigName();
  262. }
  263. var ati = _analyzeTypeInfos[typeInfo];
  264. //if (_analyzeTypeInfos.TryGetValue(typeInfo, out var ati))
  265. //{
  266. // return ati.signature;
  267. //}
  268. //ati = CalculateAnalyzeTypeInfoBasic(typeInfo);
  269. //_analyzeTypeInfos.Add(typeInfo, ati);
  270. if (ati.signature != null)
  271. {
  272. return ati.signature;
  273. }
  274. var sigBuf = new StringBuilder();
  275. if (ati.classLayout != null)
  276. {
  277. sigBuf.Append($"[{ati.classLayout.ClassSize}|{ati.classLayout.PackingSize}|{ati.classLayout}]");
  278. }
  279. if (ati.layout != 0)
  280. {
  281. sigBuf.Append($"[{(int)ati.layout}]");
  282. }
  283. foreach (var field in ati.fields)
  284. {
  285. string fieldOffset = field.field.FieldOffset != null ? field.field.FieldOffset.ToString() + "|" : "";
  286. sigBuf.Append("{" + fieldOffset + GetOrCalculateTypeInfoSignature(ToIsomorphicType(field.type)) + "}");
  287. }
  288. return ati.signature = sigBuf.ToString();
  289. }
  290. private TypeInfo ToIsomorphicType(TypeInfo type)
  291. {
  292. if (!type.IsStruct)
  293. {
  294. return type;
  295. }
  296. if (!_analyzeTypeInfos.TryGetValue(type, out var ati))
  297. {
  298. ati = CalculateAnalyzeTypeInfoBasic(type);
  299. _analyzeTypeInfos.Add(type, ati);
  300. }
  301. if (ati.toSharedType == null)
  302. {
  303. string signature = GetOrCalculateTypeInfoSignature(type);
  304. Debug.Assert(signature == ati.signature);
  305. if (_signature2Type.TryGetValue(signature, out var sharedType))
  306. {
  307. // Debug.Log($"[ToIsomorphicType] type:{type.Klass} ==> sharedType:{sharedType.Klass} signature:{signature} ");
  308. ati.toSharedType = sharedType;
  309. }
  310. else
  311. {
  312. ati.toSharedType = type;
  313. _signature2Type.Add(signature, type);
  314. }
  315. }
  316. return ati.toSharedType;
  317. }
  318. private MethodDesc ToIsomorphicMethod(MethodDesc method)
  319. {
  320. var paramInfos = new List<ParamInfo>();
  321. foreach (var paramInfo in method.ParamInfos)
  322. {
  323. paramInfos.Add(new ParamInfo() { Type = ToIsomorphicType(paramInfo.Type) });
  324. }
  325. var mbs = new MethodDesc()
  326. {
  327. MethodDef = method.MethodDef,
  328. ReturnInfo = new ReturnInfo() { Type = ToIsomorphicType(method.ReturnInfo.Type) },
  329. ParamInfos = paramInfos,
  330. };
  331. mbs.Init();
  332. return mbs;
  333. }
  334. private List<MethodDesc> _managed2NativeMethodList;
  335. private List<MethodDesc> _native2ManagedMethodList;
  336. private List<MethodDesc> _adjustThunkMethodList;
  337. private List<TypeInfo> structTypes;
  338. private void BuildAnalyzeTypeInfos()
  339. {
  340. foreach (var type in _structTypes0)
  341. {
  342. ToIsomorphicType(type);
  343. }
  344. structTypes = _signature2Type.Values.ToList();
  345. structTypes.Sort((a, b) => a.TypeId - b.TypeId);
  346. }
  347. private List<MethodDesc> ToUniqueOrderedList(List<MethodDesc> methods)
  348. {
  349. var methodMap = new SortedDictionary<string, MethodDesc>();
  350. foreach (var method in methods)
  351. {
  352. var sharedMethod = ToIsomorphicMethod(method);
  353. var sig = sharedMethod.Sig;
  354. if (!methodMap.TryGetValue(sig, out var _))
  355. {
  356. methodMap.Add(sig, sharedMethod);
  357. }
  358. }
  359. return methodMap.Values.ToList();
  360. }
  361. private static string MakeSignature(MethodDesc desc, CallingConvention CallingConventionention)
  362. {
  363. string convStr = ((char)('A' + (int)CallingConventionention - 1)).ToString();
  364. return $"{convStr}{desc.Sig}";
  365. }
  366. private static CallingConvention GetCallingConvention(MethodDef method)
  367. {
  368. var monoPInvokeCallbackAttr = method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Name == "MonoPInvokeCallbackAttribute");
  369. if (monoPInvokeCallbackAttr == null)
  370. {
  371. return CallingConvention.Winapi;
  372. }
  373. object delegateTypeSig = monoPInvokeCallbackAttr.ConstructorArguments[0].Value;
  374. TypeDef delegateTypeDef;
  375. if (delegateTypeSig is ClassSig classSig)
  376. {
  377. delegateTypeDef = classSig.TypeDefOrRef.ResolveTypeDefThrow();
  378. }
  379. else if (delegateTypeSig is GenericInstSig genericInstSig)
  380. {
  381. delegateTypeDef = genericInstSig.GenericType.TypeDefOrRef.ResolveTypeDefThrow();
  382. }
  383. else
  384. {
  385. delegateTypeDef = null;
  386. }
  387. if (delegateTypeDef == null)
  388. {
  389. throw new NotSupportedException($"Unsupported delegate type: {delegateTypeSig}");
  390. }
  391. var attr = delegateTypeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute");
  392. if (attr == null)
  393. {
  394. return CallingConvention.Winapi;
  395. }
  396. var conv = attr.ConstructorArguments[0].Value;
  397. return (CallingConvention)conv;
  398. }
  399. private List<ABIReversePInvokeMethodInfo> BuildABIMethods(List<RawReversePInvokeMethodInfo> rawMethods)
  400. {
  401. var methodsBySig = new Dictionary<string, ABIReversePInvokeMethodInfo>();
  402. foreach (var method in rawMethods)
  403. {
  404. var sharedMethod = new MethodDesc
  405. {
  406. MethodDef = method.Method,
  407. ReturnInfo = new ReturnInfo { Type = _typeCreator.CreateTypeInfo(method.Method.ReturnType) },
  408. ParamInfos = method.Method.Parameters.Select(p => new ParamInfo { Type = _typeCreator.CreateTypeInfo(p.Type) }).ToList(),
  409. };
  410. sharedMethod.Init();
  411. sharedMethod = ToIsomorphicMethod(sharedMethod);
  412. CallingConvention callingConv = GetCallingConvention(method.Method);
  413. string signature = MakeSignature(sharedMethod, callingConv);
  414. if (!methodsBySig.TryGetValue(signature, out var arm))
  415. {
  416. arm = new ABIReversePInvokeMethodInfo()
  417. {
  418. Method = sharedMethod,
  419. Signature = signature,
  420. Count = 0,
  421. Callvention = callingConv,
  422. };
  423. methodsBySig.Add(signature, arm);
  424. }
  425. int preserveCount = method.GenerationAttribute != null ? (int)method.GenerationAttribute.ConstructorArguments[0].Value : 1;
  426. arm.Count += preserveCount;
  427. }
  428. var newMethods = methodsBySig.Values.ToList();
  429. newMethods.Sort((a, b) => string.CompareOrdinal(a.Signature, b.Signature));
  430. return newMethods;
  431. }
  432. private void BuildOptimizedMethods()
  433. {
  434. _managed2NativeMethodList = ToUniqueOrderedList(_managed2NativeMethodList0);
  435. _native2ManagedMethodList = ToUniqueOrderedList(_native2ManagedMethodList0);
  436. _adjustThunkMethodList = ToUniqueOrderedList(_adjustThunkMethodList0);
  437. _reversePInvokeMethods = BuildABIMethods(_originalReversePInvokeMethods);
  438. }
  439. private void OptimizationTypesAndMethods()
  440. {
  441. BuildAnalyzeTypeInfos();
  442. BuildOptimizedMethods();
  443. Debug.LogFormat("== after optimization struct:{3} managed2native:{0} native2managed:{1} adjustThunk:{2}",
  444. _managed2NativeMethodList.Count, _native2ManagedMethodList.Count, _adjustThunkMethodList.Count, structTypes.Count);
  445. }
  446. private void GenerateCode()
  447. {
  448. var frr = new FileRegionReplace(_templateCode);
  449. List<string> lines = new List<string>(20_0000)
  450. {
  451. "\n",
  452. $"// DEVELOPMENT={(_development ? 1 : 0)}",
  453. "\n"
  454. };
  455. var classInfos = new List<ClassInfo>();
  456. var classTypeSet = new HashSet<TypeInfo>();
  457. foreach (var type in structTypes)
  458. {
  459. GenerateClassInfo(type, classTypeSet, classInfos);
  460. }
  461. GenerateStructDefines(classInfos, lines);
  462. // use structTypes0 to generate signature
  463. GenerateStructureSignatureStub(_structTypes0, lines);
  464. foreach (var method in _managed2NativeMethodList)
  465. {
  466. GenerateManaged2NativeMethod(method, lines);
  467. }
  468. GenerateManaged2NativeStub(_managed2NativeMethodList, lines);
  469. foreach (var method in _native2ManagedMethodList)
  470. {
  471. GenerateNative2ManagedMethod(method, lines);
  472. }
  473. GenerateNative2ManagedStub(_native2ManagedMethodList, lines);
  474. foreach (var method in _adjustThunkMethodList)
  475. {
  476. GenerateAdjustThunkMethod(method, lines);
  477. }
  478. GenerateAdjustThunkStub(_adjustThunkMethodList, lines);
  479. GenerateReversePInvokeWrappers(_reversePInvokeMethods, lines);
  480. frr.Replace("CODE", string.Join("\n", lines));
  481. Directory.CreateDirectory(Path.GetDirectoryName(_outputFile));
  482. frr.Commit(_outputFile);
  483. }
  484. private static string GetIl2cppCallConventionName(CallingConvention conv)
  485. {
  486. switch (conv)
  487. {
  488. case 0:
  489. case CallingConvention.Winapi:
  490. return "DEFAULT_CALL";
  491. case CallingConvention.Cdecl:
  492. return "CDECL";
  493. case CallingConvention.StdCall:
  494. return "STDCALL";
  495. case CallingConvention.ThisCall:
  496. return "THISCALL";
  497. case CallingConvention.FastCall:
  498. return "FASTCALL";
  499. default:
  500. throw new NotSupportedException($"Unsupported CallingConvention {conv}");
  501. }
  502. }
  503. private void GenerateReversePInvokeWrappers(List<ABIReversePInvokeMethodInfo> methods, List<string> lines)
  504. {
  505. int methodIndex = 0;
  506. var stubCodes = new List<string>();
  507. foreach (var methodInfo in methods)
  508. {
  509. MethodDesc method = methodInfo.Method;
  510. string il2cppCallConventionName = GetIl2cppCallConventionName(methodInfo.Callvention);
  511. string paramDeclaringListWithoutMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}"));
  512. string paramNameListWithoutMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"__arg{p.Index}").Concat(new string[] { "method" }));
  513. string paramTypeListWithMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()}").Concat(new string[] { "const MethodInfo*" }));
  514. string methodTypeDef = $"typedef {method.ReturnInfo.Type.GetTypeName()} (*Callback)({paramTypeListWithMethodInfoStr})";
  515. for (int i = 0; i < methodInfo.Count; i++, methodIndex++)
  516. {
  517. lines.Add($@"
  518. {method.ReturnInfo.Type.GetTypeName()} {il2cppCallConventionName} __ReversePInvokeMethod_{methodIndex}({paramDeclaringListWithoutMethodInfoStr})
  519. {{
  520. il2cpp::vm::ScopedThreadAttacher _vmThreadHelper;
  521. const MethodInfo* method = InterpreterModule::GetMethodInfoByReversePInvokeWrapperIndex({methodIndex});
  522. {methodTypeDef};
  523. {(method.ReturnInfo.IsVoid ? "" : "return ")}((Callback)(method->methodPointerCallByInterp))({paramNameListWithoutMethodInfoStr});
  524. }}
  525. ");
  526. stubCodes.Add($"\t{{\"{methodInfo.Signature}\", (Il2CppMethodPointer)__ReversePInvokeMethod_{methodIndex}}},");
  527. }
  528. Debug.Log($"[ReversePInvokeWrap.Generator] method:{method.MethodDef} wrapperCount:{methodInfo.Count}");
  529. }
  530. lines.Add(@"
  531. const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStub[]
  532. {
  533. ");
  534. lines.AddRange(stubCodes);
  535. lines.Add(@"
  536. {nullptr, nullptr},
  537. };
  538. ");
  539. }
  540. public void Generate()
  541. {
  542. PrepareMethodBridges();
  543. CollectTypesAndMethods();
  544. OptimizationTypesAndMethods();
  545. GenerateCode();
  546. }
  547. private void CollectStructDefs(List<MethodDesc> methods, HashSet<TypeInfo> structTypes)
  548. {
  549. foreach (var method in methods)
  550. {
  551. foreach(var paramInfo in method.ParamInfos)
  552. {
  553. if (paramInfo.Type.IsStruct)
  554. {
  555. structTypes.Add(paramInfo.Type);
  556. if (paramInfo.Type.Klass.ContainsGenericParameter)
  557. {
  558. throw new Exception($"[CollectStructDefs] method:{method.MethodDef} type:{paramInfo.Type.Klass} contains generic parameter");
  559. }
  560. }
  561. }
  562. if (method.ReturnInfo.Type.IsStruct)
  563. {
  564. structTypes.Add(method.ReturnInfo.Type);
  565. if (method.ReturnInfo.Type.Klass.ContainsGenericParameter)
  566. {
  567. throw new Exception($"[CollectStructDefs] method:{method.MethodDef} type:{method.ReturnInfo.Type.Klass} contains generic parameter");
  568. }
  569. }
  570. }
  571. }
  572. class FieldInfo
  573. {
  574. public FieldDef field;
  575. public TypeInfo type;
  576. }
  577. class ClassInfo
  578. {
  579. public TypeInfo type;
  580. public TypeDef typeDef;
  581. public List<FieldInfo> fields = new List<FieldInfo>();
  582. public ClassLayout layout;
  583. }
  584. private void GenerateClassInfo(TypeInfo type, HashSet<TypeInfo> typeSet, List<ClassInfo> classInfos)
  585. {
  586. if (!typeSet.Add(type))
  587. {
  588. return;
  589. }
  590. TypeSig typeSig = type.Klass;
  591. var fields = new List<FieldInfo>();
  592. TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow();
  593. List<TypeSig> klassInst = typeSig.ToGenericInstSig()?.GenericArguments?.ToList();
  594. GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
  595. ClassLayout sa = typeDef.ClassLayout;
  596. ICorLibTypes corLibTypes = typeDef.Module.CorLibTypes;
  597. foreach (FieldDef field in typeDef.Fields)
  598. {
  599. if (field.IsStatic)
  600. {
  601. continue;
  602. }
  603. TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType;
  604. fieldType = MetaUtil.ToShareTypeSig(corLibTypes, fieldType);
  605. var fieldTypeInfo = ToIsomorphicType(_typeCreator.CreateTypeInfo(fieldType));
  606. if (fieldTypeInfo.IsStruct)
  607. {
  608. GenerateClassInfo(fieldTypeInfo, typeSet, classInfos);
  609. }
  610. fields.Add(new FieldInfo { field = field, type = fieldTypeInfo });
  611. }
  612. classInfos.Add(new ClassInfo() { type = type, typeDef = typeDef, fields = fields, layout = sa });
  613. }
  614. private void GenerateStructDefines(List<ClassInfo> classInfos, List<string> lines)
  615. {
  616. foreach (var ci in classInfos)
  617. {
  618. lines.Add($"// {ci.type.Klass}");
  619. uint packingSize = ci.layout?.PackingSize ?? 0;
  620. if (packingSize != 0)
  621. {
  622. lines.Add($"#pragma pack(push, {packingSize})");
  623. }
  624. uint classSize = ci.layout?.ClassSize ?? 0;
  625. if (ci.typeDef.IsExplicitLayout)
  626. {
  627. lines.Add($"union {ci.type.GetTypeName()} {{");
  628. if (classSize > 0)
  629. {
  630. lines.Add($"\tstruct {{ char __fieldSize_offsetPadding[{classSize}];}};");
  631. }
  632. int index = 0;
  633. foreach (var field in ci.fields)
  634. {
  635. uint offset = field.field.FieldOffset.Value;
  636. string fieldName = $"__{index}";
  637. string commentFieldName = $"{field.field.Name}";
  638. lines.Add("\t#pragma pack(push, 1)");
  639. lines.Add($"\tstruct {{ {(offset > 0 ? $"char {fieldName}_offsetPadding[{offset}];" : "")} {field.type.GetTypeName()} {fieldName};}}; // {commentFieldName}");
  640. lines.Add($"\t#pragma pack(pop)");
  641. lines.Add($"\tstruct {{ {field.type.GetTypeName()} {fieldName}_forAlignmentOnly;}}; // {commentFieldName}");
  642. ++index;
  643. }
  644. }
  645. else
  646. {
  647. lines.Add($"{(classSize > 0 ? "union" : "struct")} {ci.type.GetTypeName()} {{");
  648. if (classSize > 0)
  649. {
  650. lines.Add($"\tstruct {{ char __fieldSize_offsetPadding[{classSize}];}};");
  651. lines.Add("\tstruct {");
  652. }
  653. int index = 0;
  654. foreach (var field in ci.fields)
  655. {
  656. string fieldName = $"__{index}";
  657. string commentFieldName = $"{field.field.Name}";
  658. lines.Add($"\t{field.type.GetTypeName()} {fieldName}; // {commentFieldName}");
  659. ++index;
  660. }
  661. if (classSize > 0)
  662. {
  663. lines.Add("\t};");
  664. }
  665. }
  666. lines.Add("};");
  667. if (packingSize != 0)
  668. {
  669. lines.Add($"#pragma pack(pop)");
  670. }
  671. }
  672. }
  673. public const string SigOfObj = "u";
  674. public static string ToFullName(TypeSig type)
  675. {
  676. type = type.RemovePinnedAndModifiers();
  677. switch (type.ElementType)
  678. {
  679. case ElementType.Void: return "v";
  680. case ElementType.Boolean: return "u1";
  681. case ElementType.I1: return "i1";
  682. case ElementType.U1: return "u1";
  683. case ElementType.I2: return "i2";
  684. case ElementType.Char:
  685. case ElementType.U2: return "u2";
  686. case ElementType.I4: return "i4";
  687. case ElementType.U4: return "u4";
  688. case ElementType.I8: return "i8";
  689. case ElementType.U8: return "u8";
  690. case ElementType.R4: return "r4";
  691. case ElementType.R8: return "r8";
  692. case ElementType.I: return "i";
  693. case ElementType.U:
  694. case ElementType.String:
  695. case ElementType.Ptr:
  696. case ElementType.ByRef:
  697. case ElementType.Class:
  698. case ElementType.Array:
  699. case ElementType.SZArray:
  700. case ElementType.FnPtr:
  701. case ElementType.Object:
  702. return SigOfObj;
  703. case ElementType.Module:
  704. case ElementType.Var:
  705. case ElementType.MVar:
  706. throw new NotSupportedException($"ToFullName type:{type}");
  707. case ElementType.TypedByRef: return TypeInfo.strTypedByRef;
  708. case ElementType.ValueType:
  709. {
  710. TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDef();
  711. if (typeDef == null)
  712. {
  713. throw new Exception($"type:{type} definition could not be found. Please try `HybridCLR/Genergate/LinkXml`, then Build once to generate the AOT dll, and then regenerate the bridge function");
  714. }
  715. if (typeDef.IsEnum)
  716. {
  717. return ToFullName(typeDef.GetEnumUnderlyingType());
  718. }
  719. return ToValueTypeFullName((ClassOrValueTypeSig)type);
  720. }
  721. case ElementType.GenericInst:
  722. {
  723. GenericInstSig gis = (GenericInstSig)type;
  724. if (!gis.GenericType.IsValueType)
  725. {
  726. return SigOfObj;
  727. }
  728. TypeDef typeDef = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef();
  729. if (typeDef.IsEnum)
  730. {
  731. return ToFullName(typeDef.GetEnumUnderlyingType());
  732. }
  733. return $"{ToValueTypeFullName(gis.GenericType)}<{string.Join(",", gis.GenericArguments.Select(a => ToFullName(a)))}>";
  734. }
  735. default: throw new NotSupportedException($"{type.ElementType}");
  736. }
  737. }
  738. private static bool IsSystemOrUnityAssembly(ModuleDef module)
  739. {
  740. if (module.IsCoreLibraryModule == true)
  741. {
  742. return true;
  743. }
  744. string assName = module.Assembly.Name.String;
  745. return assName.StartsWith("System.") || assName.StartsWith("UnityEngine.");
  746. }
  747. private static string ToValueTypeFullName(ClassOrValueTypeSig type)
  748. {
  749. TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDef();
  750. if (typeDef == null)
  751. {
  752. throw new Exception($"type:{type} resolve fail");
  753. }
  754. if (typeDef.DeclaringType != null)
  755. {
  756. return $"{ToValueTypeFullName((ClassOrValueTypeSig)typeDef.DeclaringType.ToTypeSig())}/{typeDef.Name}";
  757. }
  758. if (IsSystemOrUnityAssembly(typeDef.Module))
  759. {
  760. return type.FullName;
  761. }
  762. return $"{Path.GetFileNameWithoutExtension(typeDef.Module.Name)}:{typeDef.FullName}";
  763. }
  764. public void GenerateStructureSignatureStub(List<TypeInfo> types, List<string> lines)
  765. {
  766. lines.Add("const FullName2Signature hybridclr::interpreter::g_fullName2SignatureStub[] = {");
  767. foreach (var type in types)
  768. {
  769. TypeInfo isoType = ToIsomorphicType(type);
  770. lines.Add($"\t{{\"{ToFullName(type.Klass)}\", \"{isoType.CreateSigName()}\"}},");
  771. }
  772. lines.Add("\t{ nullptr, nullptr},");
  773. lines.Add("};");
  774. }
  775. public void GenerateManaged2NativeStub(List<MethodDesc> methods, List<string> lines)
  776. {
  777. lines.Add($@"
  778. const Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] =
  779. {{
  780. ");
  781. foreach (var method in methods)
  782. {
  783. lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", __M2N_{method.CreateInvokeSigName()}}},");
  784. }
  785. lines.Add($"\t{{nullptr, nullptr}},");
  786. lines.Add("};");
  787. }
  788. public void GenerateNative2ManagedStub(List<MethodDesc> methods, List<string> lines)
  789. {
  790. lines.Add($@"
  791. const Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] =
  792. {{
  793. ");
  794. foreach (var method in methods)
  795. {
  796. lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_{method.CreateInvokeSigName()}}},");
  797. }
  798. lines.Add($"\t{{nullptr, nullptr}},");
  799. lines.Add("};");
  800. }
  801. public void GenerateAdjustThunkStub(List<MethodDesc> methods, List<string> lines)
  802. {
  803. lines.Add($@"
  804. const NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] =
  805. {{
  806. ");
  807. foreach (var method in methods)
  808. {
  809. lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_AdjustorThunk_{method.CreateCallSigName()}}},");
  810. }
  811. lines.Add($"\t{{nullptr, nullptr}},");
  812. lines.Add("};");
  813. }
  814. private string GetManaged2NativePassParam(TypeInfo type, string varName)
  815. {
  816. return $"M2NFromValueOrAddress<{type.GetTypeName()}>({varName})";
  817. }
  818. private string GetNative2ManagedPassParam(TypeInfo type, string varName)
  819. {
  820. return type.NeedExpandValue() ? $"(uint64_t)({varName})" : $"N2MAsUint64ValueOrAddress<{type.GetTypeName()}>({varName})";
  821. }
  822. public void GenerateManaged2NativeMethod(MethodDesc method, List<string> lines)
  823. {
  824. string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
  825. string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => GetManaged2NativePassParam(p.Type, $"localVarBase+argVarIndexs[{p.Index}]")).Concat(new string[] { "method" }));
  826. lines.Add($@"
  827. static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
  828. {{
  829. typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr});
  830. {(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(method->methodPointerCallByInterp))({paramNameListStr});
  831. }}
  832. ");
  833. }
  834. public string GenerateArgumentSizeAndOffset(List<ParamInfo> paramInfos)
  835. {
  836. StringBuilder s = new StringBuilder();
  837. int index = 0;
  838. foreach (var param in paramInfos)
  839. {
  840. s.AppendLine($"\tconstexpr int __ARG_OFFSET_{index}__ = {(index > 0 ? $"__ARG_OFFSET_{index - 1}__ + __ARG_SIZE_{index-1}__" : "0")};");
  841. s.AppendLine($"\tconstexpr int __ARG_SIZE_{index}__ = (sizeof(__arg{index}) + 7)/8;");
  842. index++;
  843. }
  844. s.AppendLine($"\tconstexpr int __TOTAL_ARG_SIZE__ = {(paramInfos.Count > 0 ? $"__ARG_OFFSET_{index-1}__ + __ARG_SIZE_{index-1}__" : "1")};");
  845. return s.ToString();
  846. }
  847. public string GenerateCopyArgumentToInterpreterStack(List<ParamInfo> paramInfos)
  848. {
  849. StringBuilder s = new StringBuilder();
  850. int index = 0;
  851. foreach (var param in paramInfos)
  852. {
  853. if (param.Type.IsPrimitiveType)
  854. {
  855. if (param.Type.NeedExpandValue())
  856. {
  857. s.AppendLine($"\targs[__ARG_OFFSET_{index}__].u64 = __arg{index};");
  858. }
  859. else
  860. {
  861. s.AppendLine($"\t*({param.Type.GetTypeName()}*)(args + __ARG_OFFSET_{index}__) = __arg{index};");
  862. }
  863. }
  864. else
  865. {
  866. s.AppendLine($"\t*({param.Type.GetTypeName()}*)(args + __ARG_OFFSET_{index}__) = __arg{index};");
  867. }
  868. index++;
  869. }
  870. return s.ToString();
  871. }
  872. private void GenerateNative2ManagedMethod0(MethodDesc method, bool adjustorThunk, List<string> lines)
  873. {
  874. string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
  875. lines.Add($@"
  876. static {method.ReturnInfo.Type.GetTypeName()} __N2M_{(adjustorThunk ? "AdjustorThunk_" : "")}{method.CreateCallSigName()}({paramListStr})
  877. {{
  878. {(adjustorThunk ? "__arg0 += sizeof(Il2CppObject);" : "")}
  879. {GenerateArgumentSizeAndOffset(method.ParamInfos)}
  880. StackObject args[__TOTAL_ARG_SIZE__];
  881. {GenerateCopyArgumentToInterpreterStack(method.ParamInfos)}
  882. {(method.ReturnInfo.IsVoid ? "Interpreter::Execute(method, args, nullptr);" : $"{method.ReturnInfo.Type.GetTypeName()} ret; Interpreter::Execute(method, args, &ret); return ret;")}
  883. }}
  884. ");
  885. }
  886. public void GenerateNative2ManagedMethod(MethodDesc method, List<string> lines)
  887. {
  888. GenerateNative2ManagedMethod0(method, false, lines);
  889. }
  890. public void GenerateAdjustThunkMethod(MethodDesc method, List<string> lines)
  891. {
  892. GenerateNative2ManagedMethod0(method, true, lines);
  893. }
  894. }
  895. }