AssemblyInspector.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using System;
  2. using System.IO;
  3. using System.Diagnostics;
  4. using System.Collections.Generic;
  5. using System.Collections.Immutable;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Reflection.Metadata;
  9. using System.Reflection.PortableExecutable;
  10. using System.Reflection.Metadata.Ecma335;
  11. using System.Text;
  12. using AtomicEngine;
  13. using File = System.IO.File;
  14. namespace AtomicTools
  15. {
  16. class AssemblyInspector
  17. {
  18. Dictionary<string, InspectorEnum> InspectorEnums = new Dictionary<string, InspectorEnum>();
  19. Dictionary<string, InspectorComponent> InspectorComponents = new Dictionary<string, InspectorComponent>();
  20. PEReader peFile;
  21. MetadataReader metaReader;
  22. public AssemblyInspector()
  23. {
  24. }
  25. public string DumpToJSON()
  26. {
  27. var dict = new Dictionary<string, object>();
  28. var enumList = new List<object>();
  29. var componentList = new List<object>();
  30. foreach (var entry in InspectorEnums)
  31. {
  32. enumList.Add(entry.Value.GetJSONDict());
  33. }
  34. foreach (var entry in InspectorComponents)
  35. {
  36. componentList.Add(entry.Value.GetJSONDict());
  37. }
  38. dict["enums"] = enumList;
  39. dict["components"] = componentList;
  40. return MiniJSON.Json.Serialize(dict);
  41. }
  42. public void Inspect(String pathToAssembly)
  43. {
  44. using (var stream = File.OpenRead(pathToAssembly))
  45. using (peFile = new PEReader(stream))
  46. {
  47. metaReader = peFile.GetMetadataReader();
  48. ParseEnums();
  49. ParseComponents();
  50. }
  51. }
  52. void ParseComponents()
  53. {
  54. foreach (var handle in metaReader.TypeDefinitions)
  55. {
  56. var typeDef = metaReader.GetTypeDefinition(handle);
  57. // Is this a generic type?
  58. var genericParams = typeDef.GetGenericParameters();
  59. if (genericParams.Count != 0)
  60. continue;
  61. var typeName = metaReader.GetString(typeDef.Name);
  62. var baseTypeHandle = typeDef.BaseType;
  63. // Work up to base reference (ie defined outside this assembly)
  64. while (baseTypeHandle.Kind == HandleKind.TypeDefinition)
  65. {
  66. var baseTypeDef = metaReader.GetTypeDefinition((TypeDefinitionHandle)baseTypeHandle);
  67. // No way to predetermine if .BaseType is valid
  68. try
  69. {
  70. baseTypeHandle = baseTypeDef.BaseType;
  71. }
  72. catch (Exception) { break; }
  73. }
  74. if (baseTypeHandle.Kind == HandleKind.TypeReference)
  75. {
  76. var typeRef = metaReader.GetTypeReference((TypeReferenceHandle)baseTypeHandle);
  77. var baseName = metaReader.GetString(typeRef.Name);
  78. if (baseName != "CSComponent")
  79. continue;
  80. var inspector = new CSComponentInspector(typeDef, peFile, metaReader);
  81. var icomponent = inspector.Inspect();
  82. if (icomponent != null)
  83. InspectorComponents[icomponent.Name] = icomponent;
  84. }
  85. }
  86. }
  87. void ParseEnums()
  88. {
  89. foreach (var handle in metaReader.TypeDefinitions)
  90. {
  91. var typeDef = metaReader.GetTypeDefinition(handle);
  92. var baseTypeHandle = typeDef.BaseType;
  93. if (baseTypeHandle.Kind == HandleKind.TypeReference)
  94. {
  95. var typeRef = metaReader.GetTypeReference((TypeReferenceHandle)baseTypeHandle);
  96. if (metaReader.GetString(typeRef.Name) == "Enum")
  97. {
  98. ParseEnum(typeDef);
  99. }
  100. }
  101. }
  102. }
  103. void ParseEnum(TypeDefinition enumTypeDef)
  104. {
  105. // TODO: verify that int32 is the enums storage type for constant read below
  106. InspectorEnum ienum = new InspectorEnum();
  107. ienum.Name = metaReader.GetString(enumTypeDef.Name);
  108. InspectorEnums[ienum.Name] = ienum;
  109. var fields = enumTypeDef.GetFields();
  110. foreach (var fieldHandle in fields)
  111. {
  112. var inspectorField = new InspectorField();
  113. var fieldDef = metaReader.GetFieldDefinition(fieldHandle);
  114. if ((fieldDef.Attributes & FieldAttributes.HasDefault) != 0)
  115. {
  116. var constantHandle = fieldDef.GetDefaultValue();
  117. var constant = metaReader.GetConstant(constantHandle);
  118. BlobReader constantReader = metaReader.GetBlobReader(constant.Value);
  119. ienum.Values[metaReader.GetString(fieldDef.Name)] = constantReader.ReadInt32();
  120. }
  121. }
  122. return;
  123. }
  124. }
  125. internal class InspectorEnum
  126. {
  127. public String Name;
  128. public Dictionary<string, int> Values = new Dictionary<string, int>();
  129. public Dictionary<string, object> GetJSONDict()
  130. {
  131. var dict = new Dictionary<string, object>();
  132. dict["name"] = Name;
  133. dict["values"] = Values;
  134. return dict;
  135. }
  136. }
  137. internal class InspectorComponent
  138. {
  139. public String Name;
  140. public String Namespace;
  141. public Dictionary<string, InspectorField> Fields = new Dictionary<string, InspectorField>();
  142. public Dictionary<string, object> GetJSONDict()
  143. {
  144. var dict = new Dictionary<string, object>();
  145. dict["name"] = Name;
  146. dict["namespace"] = Namespace;
  147. var fieldList = new List<object>();
  148. foreach (var entry in Fields)
  149. {
  150. fieldList.Add(entry.Value.GetJSONDict());
  151. }
  152. dict["fields"] = fieldList;
  153. return dict;
  154. }
  155. }
  156. internal class InspectorField
  157. {
  158. public Dictionary<string, object> GetJSONDict()
  159. {
  160. var dict = new Dictionary<string, object>();
  161. dict["isEnum"] = IsEnum;
  162. dict["isArray"] = IsArray;
  163. dict["typeName"] = TypeName;
  164. dict["name"] = Name;
  165. dict["defaultValue"] = DefaultValue;
  166. dict["caPos"] = CustomAttrPositionalArgs;
  167. dict["caNamed"] = CustomAttrNamedArgs;
  168. return dict;
  169. }
  170. public bool IsEnum = false;
  171. public bool IsArray = false;
  172. public string TypeName;
  173. // the Name of the InspectorField
  174. public string Name;
  175. // The DefaultValue if supplied
  176. public string DefaultValue;
  177. // custom attributes, positional and named
  178. public List<string> CustomAttrPositionalArgs = new List<string>();
  179. public Dictionary<string, string> CustomAttrNamedArgs = new Dictionary<string, string>();
  180. }
  181. }