AssemblyInspector.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. var parentName = metaReader.GetString(typeDef.Name);
  58. var baseTypeHandle = typeDef.BaseType;
  59. // Work up to base reference (ie defined outside this assembly)
  60. while (baseTypeHandle.Kind == HandleKind.TypeDefinition)
  61. {
  62. var baseTypeDef = metaReader.GetTypeDefinition((TypeDefinitionHandle)baseTypeHandle);
  63. // No way to predetermine if .BaseType is valid
  64. try
  65. {
  66. baseTypeHandle = baseTypeDef.BaseType;
  67. }
  68. catch (Exception) { break; }
  69. }
  70. if (baseTypeHandle.Kind == HandleKind.TypeReference)
  71. {
  72. var typeRef = metaReader.GetTypeReference((TypeReferenceHandle)baseTypeHandle);
  73. var name = metaReader.GetString(typeRef.Name);
  74. if (name != "CSComponent")
  75. continue;
  76. var inspector = new CSComponentInspector(typeDef, peFile, metaReader);
  77. var icomponent = inspector.Inspect();
  78. if (icomponent != null)
  79. InspectorComponents[icomponent.Name] = icomponent;
  80. }
  81. }
  82. }
  83. void ParseEnums()
  84. {
  85. foreach (var handle in metaReader.TypeDefinitions)
  86. {
  87. var typeDef = metaReader.GetTypeDefinition(handle);
  88. var baseTypeHandle = typeDef.BaseType;
  89. if (baseTypeHandle.Kind == HandleKind.TypeReference)
  90. {
  91. var typeRef = metaReader.GetTypeReference((TypeReferenceHandle)baseTypeHandle);
  92. if (metaReader.GetString(typeRef.Name) == "Enum")
  93. {
  94. ParseEnum(typeDef);
  95. }
  96. }
  97. }
  98. }
  99. void ParseEnum(TypeDefinition enumTypeDef)
  100. {
  101. // TODO: verify that int32 is the enums storage type for constant read below
  102. InspectorEnum ienum = new InspectorEnum();
  103. ienum.Name = metaReader.GetString(enumTypeDef.Name);
  104. InspectorEnums[ienum.Name] = ienum;
  105. var fields = enumTypeDef.GetFields();
  106. foreach (var fieldHandle in fields)
  107. {
  108. var inspectorField = new InspectorField();
  109. var fieldDef = metaReader.GetFieldDefinition(fieldHandle);
  110. if ((fieldDef.Attributes & FieldAttributes.HasDefault) != 0)
  111. {
  112. var constantHandle = fieldDef.GetDefaultValue();
  113. var constant = metaReader.GetConstant(constantHandle);
  114. BlobReader constantReader = metaReader.GetBlobReader(constant.Value);
  115. ienum.Values[metaReader.GetString(fieldDef.Name)] = constantReader.ReadInt32();
  116. }
  117. }
  118. return;
  119. }
  120. }
  121. internal class InspectorEnum
  122. {
  123. public String Name;
  124. public Dictionary<string, int> Values = new Dictionary<string, int>();
  125. public Dictionary<string, object> GetJSONDict()
  126. {
  127. var dict = new Dictionary<string, object>();
  128. dict["name"] = Name;
  129. dict["values"] = Values;
  130. return dict;
  131. }
  132. }
  133. internal class InspectorComponent
  134. {
  135. public String Name;
  136. public String Namespace;
  137. public Dictionary<string, InspectorField> Fields = new Dictionary<string, InspectorField>();
  138. public Dictionary<string, object> GetJSONDict()
  139. {
  140. var dict = new Dictionary<string, object>();
  141. dict["name"] = Name;
  142. dict["namespace"] = Namespace;
  143. var fieldList = new List<object>();
  144. foreach (var entry in Fields)
  145. {
  146. fieldList.Add(entry.Value.GetJSONDict());
  147. }
  148. dict["fields"] = fieldList;
  149. return dict;
  150. }
  151. }
  152. internal class InspectorField
  153. {
  154. public Dictionary<string, object> GetJSONDict()
  155. {
  156. var dict = new Dictionary<string, object>();
  157. dict["isEnum"] = IsEnum;
  158. dict["typeName"] = TypeName;
  159. dict["name"] = Name;
  160. dict["defaultValue"] = DefaultValue;
  161. dict["caPos"] = CustomAttrPositionalArgs;
  162. dict["caNamed"] = CustomAttrNamedArgs;
  163. return dict;
  164. }
  165. public bool IsEnum = false;
  166. public string TypeName;
  167. // the Name of the InspectorField
  168. public string Name;
  169. // The DefaultValue if supplied
  170. public string DefaultValue;
  171. // custom attributes, positional and named
  172. public List<string> CustomAttrPositionalArgs = new List<string>();
  173. public Dictionary<string, string> CustomAttrNamedArgs = new Dictionary<string, string>();
  174. }
  175. }