Analyzer.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. using dnlib.DotNet;
  2. using HybridCLR.Editor.Meta;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using UnityEngine;
  9. namespace HybridCLR.Editor.AOT
  10. {
  11. public class Analyzer
  12. {
  13. public class Options
  14. {
  15. public AssemblyReferenceDeepCollector Collector { get; set; }
  16. public int MaxIterationCount { get; set; }
  17. }
  18. private readonly int _maxInterationCount;
  19. private readonly AssemblyReferenceDeepCollector _assemblyCollector;
  20. private readonly HashSet<GenericClass> _genericTypes = new HashSet<GenericClass>();
  21. private readonly HashSet<GenericMethod> _genericMethods = new HashSet<GenericMethod>();
  22. private List<GenericMethod> _processingMethods = new List<GenericMethod>();
  23. private List<GenericMethod> _newMethods = new List<GenericMethod>();
  24. public IReadOnlyCollection<GenericClass> GenericTypes => _genericTypes;
  25. public IReadOnlyCollection<GenericMethod> GenericMethods => _genericMethods;
  26. private readonly MethodReferenceAnalyzer _methodReferenceAnalyzer;
  27. private readonly HashSet<string> _hotUpdateAssemblyFiles;
  28. public ConstraintContext ConstraintContext { get; } = new ConstraintContext();
  29. public List<GenericClass> AotGenericTypes { get; } = new List<GenericClass>();
  30. public List<GenericMethod> AotGenericMethods { get; } = new List<GenericMethod>();
  31. public Analyzer(Options options)
  32. {
  33. _assemblyCollector = options.Collector;
  34. _maxInterationCount = options.MaxIterationCount;
  35. _methodReferenceAnalyzer = new MethodReferenceAnalyzer(this.OnNewMethod);
  36. _hotUpdateAssemblyFiles = new HashSet<string>(options.Collector.GetRootAssemblyNames().Select(assName => assName + ".dll"));
  37. }
  38. private void TryAddAndWalkGenericType(GenericClass gc)
  39. {
  40. if (gc == null)
  41. {
  42. return;
  43. }
  44. gc = gc.ToGenericShare();
  45. if (_genericTypes.Add(gc) && NeedWalk(null, gc.Type))
  46. {
  47. WalkType(gc);
  48. }
  49. }
  50. private bool NeedWalk(MethodDef callFrom, TypeDef type)
  51. {
  52. return _hotUpdateAssemblyFiles.Contains(type.Module.Name) || callFrom == null || callFrom.HasGenericParameters;
  53. }
  54. private bool IsAotType(TypeDef type)
  55. {
  56. return !_hotUpdateAssemblyFiles.Contains(type.Module.Name);
  57. }
  58. private bool IsAotGenericMethod(MethodDef method)
  59. {
  60. return IsAotType(method.DeclaringType) && method.HasGenericParameters;
  61. }
  62. private void OnNewMethod(MethodDef methodDef, List<TypeSig> klassGenericInst, List<TypeSig> methodGenericInst, GenericMethod method)
  63. {
  64. if(method == null)
  65. {
  66. return;
  67. }
  68. if (NeedWalk(methodDef, method.Method.DeclaringType) && _genericMethods.Add(method))
  69. {
  70. _newMethods.Add(method);
  71. }
  72. if (method.KlassInst != null)
  73. {
  74. TryAddAndWalkGenericType(new GenericClass(method.Method.DeclaringType, method.KlassInst));
  75. }
  76. }
  77. private void TryAddMethodNotWalkType(GenericMethod method)
  78. {
  79. if (method == null)
  80. {
  81. return;
  82. }
  83. if (NeedWalk(null, method.Method.DeclaringType) && _genericMethods.Add(method))
  84. {
  85. _newMethods.Add(method);
  86. }
  87. }
  88. private void WalkType(GenericClass gc)
  89. {
  90. //Debug.Log($"typespec:{sig} {sig.GenericType} {sig.GenericType.TypeDefOrRef.ResolveTypeDef()}");
  91. //Debug.Log($"== walk generic type:{new GenericInstSig(gc.Type.ToTypeSig().ToClassOrValueTypeSig(), gc.KlassInst)}");
  92. ITypeDefOrRef baseType = gc.Type.BaseType;
  93. if (baseType != null && baseType.TryGetGenericInstSig() != null)
  94. {
  95. GenericClass parentType = GenericClass.ResolveClass((TypeSpec)baseType, new GenericArgumentContext(gc.KlassInst, null));
  96. TryAddAndWalkGenericType(parentType);
  97. }
  98. foreach (var method in gc.Type.Methods)
  99. {
  100. if (method.HasGenericParameters || !method.HasBody || method.Body.Instructions == null)
  101. {
  102. continue;
  103. }
  104. var gm = new GenericMethod(method, gc.KlassInst, null).ToGenericShare();
  105. //Debug.Log($"add method:{gm.Method} {gm.KlassInst}");
  106. TryAddMethodNotWalkType(gm);
  107. }
  108. }
  109. private void WalkType(TypeDef typeDef)
  110. {
  111. if (typeDef.HasGenericParameters)
  112. {
  113. return;
  114. }
  115. ITypeDefOrRef baseType = typeDef.BaseType;
  116. if (baseType != null && baseType.TryGetGenericInstSig() != null)
  117. {
  118. GenericClass gc = GenericClass.ResolveClass((TypeSpec)baseType, null);
  119. TryAddAndWalkGenericType(gc);
  120. }
  121. }
  122. private void Prepare()
  123. {
  124. // 将所有非泛型函数全部加入函数列表,同时立马walk这些method。
  125. // 后续迭代中将只遍历MethodSpec
  126. foreach (var ass in _assemblyCollector.GetLoadedModulesOfRootAssemblies())
  127. {
  128. foreach (TypeDef typeDef in ass.GetTypes())
  129. {
  130. WalkType(typeDef);
  131. }
  132. for (uint rid = 1, n = ass.Metadata.TablesStream.TypeSpecTable.Rows; rid <= n; rid++)
  133. {
  134. var ts = ass.ResolveTypeSpec(rid);
  135. var cs = GenericClass.ResolveClass(ts, null)?.ToGenericShare();
  136. if (cs != null)
  137. {
  138. TryAddAndWalkGenericType(cs);
  139. }
  140. }
  141. for (uint rid = 1, n = ass.Metadata.TablesStream.MethodSpecTable.Rows; rid <= n; rid++)
  142. {
  143. var ms = ass.ResolveMethodSpec(rid);
  144. var gm = GenericMethod.ResolveMethod(ms, null)?.ToGenericShare();
  145. TryAddMethodNotWalkType(gm);
  146. }
  147. }
  148. Debug.Log($"PostPrepare genericTypes:{_genericTypes.Count} genericMethods:{_genericMethods.Count} newMethods:{_newMethods.Count}");
  149. }
  150. private void RecursiveCollect()
  151. {
  152. for (int i = 0; i < _maxInterationCount && _newMethods.Count > 0; i++)
  153. {
  154. var temp = _processingMethods;
  155. _processingMethods = _newMethods;
  156. _newMethods = temp;
  157. _newMethods.Clear();
  158. foreach (var method in _processingMethods)
  159. {
  160. _methodReferenceAnalyzer.WalkMethod(method.Method, method.KlassInst, method.MethodInst);
  161. }
  162. Debug.Log($"iteration:[{i}] genericClass:{_genericTypes.Count} genericMethods:{_genericMethods.Count} newMethods:{_newMethods.Count}");
  163. }
  164. }
  165. private void FilterAOTGenericTypeAndMethods()
  166. {
  167. ConstraintContext cc = this.ConstraintContext;
  168. AotGenericTypes.AddRange(_genericTypes.Where(type => IsAotType(type.Type)).Select(gc => cc.ApplyConstraints(gc)));
  169. AotGenericMethods.AddRange(_genericMethods.Where(method => IsAotGenericMethod(method.Method)).Select(gm => cc.ApplyConstraints(gm)));
  170. }
  171. public void Run()
  172. {
  173. Prepare();
  174. RecursiveCollect();
  175. FilterAOTGenericTypeAndMethods();
  176. }
  177. }
  178. }