using System; using System.IO; using System.Diagnostics; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Reflection.Metadata.Ecma335; using System.Text; using AtomicEngine; using File = System.IO.File; namespace AtomicTools { class AssemblyInspector { Dictionary InspectorEnums = new Dictionary(); Dictionary InspectorComponents = new Dictionary(); PEReader peFile; MetadataReader metaReader; public AssemblyInspector() { } public string DumpToJSON() { var dict = new Dictionary(); var enumList = new List(); var componentList = new List(); foreach (var entry in InspectorEnums) { enumList.Add(entry.Value.GetJSONDict()); } foreach (var entry in InspectorComponents) { componentList.Add(entry.Value.GetJSONDict()); } dict["enums"] = enumList; dict["components"] = componentList; return MiniJSON.Json.Serialize(dict); } public void Inspect(String pathToAssembly) { using (var stream = File.OpenRead(pathToAssembly)) using (peFile = new PEReader(stream)) { metaReader = peFile.GetMetadataReader(); ParseEnums(); ParseComponents(); } } void ParseComponents() { foreach (var handle in metaReader.TypeDefinitions) { var typeDef = metaReader.GetTypeDefinition(handle); // Is this a generic type? var genericParams = typeDef.GetGenericParameters(); if (genericParams.Count != 0) continue; var typeName = metaReader.GetString(typeDef.Name); var baseTypeHandle = typeDef.BaseType; // Work up to base reference (ie defined outside this assembly) while (baseTypeHandle.Kind == HandleKind.TypeDefinition) { var baseTypeDef = metaReader.GetTypeDefinition((TypeDefinitionHandle)baseTypeHandle); // No way to predetermine if .BaseType is valid try { baseTypeHandle = baseTypeDef.BaseType; } catch (Exception) { break; } } if (baseTypeHandle.Kind == HandleKind.TypeReference) { var typeRef = metaReader.GetTypeReference((TypeReferenceHandle)baseTypeHandle); var baseName = metaReader.GetString(typeRef.Name); if (baseName != "CSComponent") continue; var inspector = new CSComponentInspector(typeDef, peFile, metaReader); var icomponent = inspector.Inspect(); if (icomponent != null) InspectorComponents[icomponent.Name] = icomponent; } } } void ParseEnums() { foreach (var handle in metaReader.TypeDefinitions) { var typeDef = metaReader.GetTypeDefinition(handle); var baseTypeHandle = typeDef.BaseType; if (baseTypeHandle.Kind == HandleKind.TypeReference) { var typeRef = metaReader.GetTypeReference((TypeReferenceHandle)baseTypeHandle); if (metaReader.GetString(typeRef.Name) == "Enum") { ParseEnum(typeDef); } } } } void ParseEnum(TypeDefinition enumTypeDef) { // TODO: verify that int32 is the enums storage type for constant read below InspectorEnum ienum = new InspectorEnum(); ienum.Name = metaReader.GetString(enumTypeDef.Name); InspectorEnums[ienum.Name] = ienum; var fields = enumTypeDef.GetFields(); foreach (var fieldHandle in fields) { var inspectorField = new InspectorField(); var fieldDef = metaReader.GetFieldDefinition(fieldHandle); if ((fieldDef.Attributes & FieldAttributes.HasDefault) != 0) { var constantHandle = fieldDef.GetDefaultValue(); var constant = metaReader.GetConstant(constantHandle); BlobReader constantReader = metaReader.GetBlobReader(constant.Value); ienum.Values[metaReader.GetString(fieldDef.Name)] = constantReader.ReadInt32(); } } return; } } internal class InspectorEnum { public String Name; public Dictionary Values = new Dictionary(); public Dictionary GetJSONDict() { var dict = new Dictionary(); dict["name"] = Name; dict["values"] = Values; return dict; } } internal class InspectorComponent { public String Name; public String Namespace; public Dictionary Fields = new Dictionary(); public Dictionary GetJSONDict() { var dict = new Dictionary(); dict["name"] = Name; dict["namespace"] = Namespace; var fieldList = new List(); foreach (var entry in Fields) { fieldList.Add(entry.Value.GetJSONDict()); } dict["fields"] = fieldList; return dict; } } internal class InspectorField { public Dictionary GetJSONDict() { var dict = new Dictionary(); dict["isEnum"] = IsEnum; dict["isArray"] = IsArray; dict["typeName"] = TypeName; dict["name"] = Name; dict["defaultValue"] = DefaultValue; dict["caPos"] = CustomAttrPositionalArgs; dict["caNamed"] = CustomAttrNamedArgs; return dict; } public bool IsEnum = false; public bool IsArray = false; public string TypeName; // the Name of the InspectorField public string Name; // The DefaultValue if supplied public string DefaultValue; // custom attributes, positional and named public List CustomAttrPositionalArgs = new List(); public Dictionary CustomAttrNamedArgs = new Dictionary(); } }