// // Author: // Jb Evain (jbevain@gmail.com) // // Copyright (c) 2008 - 2015 Jb Evain // Copyright (c) 2008 - 2011 Novell, Inc. // // Licensed under the MIT/X11 license. // using MonoFN.Cecil.Cil; using MonoFN.Cecil.Metadata; using MonoFN.Cecil.PE; using MonoFN.Collections.Generic; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Text; using RVA = System.UInt32; namespace MonoFN.Cecil { internal abstract class ModuleReader { protected readonly ModuleDefinition module; protected ModuleReader(Image image, ReadingMode mode) { module = new(image); module.ReadingMode = mode; } protected abstract void ReadModule(); public abstract void ReadSymbols(ModuleDefinition module); protected void ReadModuleManifest(MetadataReader reader) { reader.Populate(module); ReadAssembly(reader); } private void ReadAssembly(MetadataReader reader) { AssemblyNameDefinition name = reader.ReadAssemblyNameDefinition(); if (name == null) { module.kind = ModuleKind.NetModule; return; } AssemblyDefinition assembly = new(); assembly.Name = name; module.assembly = assembly; assembly.main_module = module; } public static ModuleDefinition CreateModule(Image image, ReaderParameters parameters) { ModuleReader reader = CreateModuleReader(image, parameters.ReadingMode); ModuleDefinition module = reader.module; if (parameters.assembly_resolver != null) module.assembly_resolver = Disposable.NotOwned(parameters.assembly_resolver); if (parameters.metadata_resolver != null) module.metadata_resolver = parameters.metadata_resolver; if (parameters.metadata_importer_provider != null) module.metadata_importer = parameters.metadata_importer_provider.GetMetadataImporter(module); if (parameters.reflection_importer_provider != null) module.reflection_importer = parameters.reflection_importer_provider.GetReflectionImporter(module); GetMetadataKind(module, parameters); reader.ReadModule(); ReadSymbols(module, parameters); reader.ReadSymbols(module); if (parameters.ReadingMode == ReadingMode.Immediate) module.MetadataSystem.Clear(); return module; } private static void ReadSymbols(ModuleDefinition module, ReaderParameters parameters) { ISymbolReaderProvider symbol_reader_provider = parameters.SymbolReaderProvider; if (symbol_reader_provider == null && parameters.ReadSymbols) symbol_reader_provider = new DefaultSymbolReaderProvider(); if (symbol_reader_provider != null) { module.SymbolReaderProvider = symbol_reader_provider; ISymbolReader reader = parameters.SymbolStream != null ? symbol_reader_provider.GetSymbolReader(module, parameters.SymbolStream) : symbol_reader_provider.GetSymbolReader(module, module.FileName); if (reader != null) { try { module.ReadSymbols(reader, parameters.ThrowIfSymbolsAreNotMatching); } catch (Exception) { reader.Dispose(); throw; } } } if (module.Image.HasDebugTables()) module.ReadSymbols(new PortablePdbReader(module.Image, module)); } private static void GetMetadataKind(ModuleDefinition module, ReaderParameters parameters) { if (!parameters.ApplyWindowsRuntimeProjections) { module.MetadataKind = MetadataKind.Ecma335; return; } string runtime_version = module.RuntimeVersion; if (!runtime_version.Contains("WindowsRuntime")) module.MetadataKind = MetadataKind.Ecma335; else if (runtime_version.Contains("CLR")) module.MetadataKind = MetadataKind.ManagedWindowsMetadata; else module.MetadataKind = MetadataKind.WindowsMetadata; } private static ModuleReader CreateModuleReader(Image image, ReadingMode mode) { switch (mode) { case ReadingMode.Immediate: return new ImmediateModuleReader(image); case ReadingMode.Deferred: return new DeferredModuleReader(image); default: throw new ArgumentException(); } } } internal sealed class ImmediateModuleReader : ModuleReader { private bool resolve_attributes; public ImmediateModuleReader(Image image) : base(image, ReadingMode.Immediate) { } protected override void ReadModule() { this.module.Read(this.module, (module, reader) => { ReadModuleManifest(reader); ReadModule(module, resolve_attributes: true); }); } public void ReadModule(ModuleDefinition module, bool resolve_attributes) { this.resolve_attributes = resolve_attributes; if (module.HasAssemblyReferences) Mixin.Read(module.AssemblyReferences); if (module.HasResources) Mixin.Read(module.Resources); if (module.HasModuleReferences) Mixin.Read(module.ModuleReferences); if (module.HasTypes) ReadTypes(module.Types); if (module.HasExportedTypes) Mixin.Read(module.ExportedTypes); ReadCustomAttributes(module); AssemblyDefinition assembly = module.Assembly; if (module.kind == ModuleKind.NetModule || assembly == null) return; ReadCustomAttributes(assembly); ReadSecurityDeclarations(assembly); } private void ReadTypes(Collection types) { for (int i = 0; i < types.Count; i++) ReadType(types[i]); } private void ReadType(TypeDefinition type) { ReadGenericParameters(type); if (type.HasInterfaces) ReadInterfaces(type); if (type.HasNestedTypes) ReadTypes(type.NestedTypes); if (type.HasLayoutInfo) Mixin.Read(type.ClassSize); if (type.HasFields) ReadFields(type); if (type.HasMethods) ReadMethods(type); if (type.HasProperties) ReadProperties(type); if (type.HasEvents) ReadEvents(type); ReadSecurityDeclarations(type); ReadCustomAttributes(type); } private void ReadInterfaces(TypeDefinition type) { Collection interfaces = type.Interfaces; for (int i = 0; i < interfaces.Count; i++) ReadCustomAttributes(interfaces[i]); } private void ReadGenericParameters(IGenericParameterProvider provider) { if (!provider.HasGenericParameters) return; Collection parameters = provider.GenericParameters; for (int i = 0; i < parameters.Count; i++) { GenericParameter parameter = parameters[i]; if (parameter.HasConstraints) ReadGenericParameterConstraints(parameter); ReadCustomAttributes(parameter); } } private void ReadGenericParameterConstraints(GenericParameter parameter) { Collection constraints = parameter.Constraints; for (int i = 0; i < constraints.Count; i++) ReadCustomAttributes(constraints[i]); } private void ReadSecurityDeclarations(ISecurityDeclarationProvider provider) { if (!provider.HasSecurityDeclarations) return; Collection security_declarations = provider.SecurityDeclarations; if (!resolve_attributes) return; for (int i = 0; i < security_declarations.Count; i++) { SecurityDeclaration security_declaration = security_declarations[i]; Mixin.Read(security_declaration.SecurityAttributes); } } private void ReadCustomAttributes(ICustomAttributeProvider provider) { if (!provider.HasCustomAttributes) return; Collection custom_attributes = provider.CustomAttributes; if (!resolve_attributes) return; for (int i = 0; i < custom_attributes.Count; i++) { CustomAttribute custom_attribute = custom_attributes[i]; Mixin.Read(custom_attribute.ConstructorArguments); } } private void ReadFields(TypeDefinition type) { Collection fields = type.Fields; for (int i = 0; i < fields.Count; i++) { FieldDefinition field = fields[i]; if (field.HasConstant) Mixin.Read(field.Constant); if (field.HasLayoutInfo) Mixin.Read(field.Offset); if (field.RVA > 0) Mixin.Read(field.InitialValue); if (field.HasMarshalInfo) Mixin.Read(field.MarshalInfo); ReadCustomAttributes(field); } } private void ReadMethods(TypeDefinition type) { Collection methods = type.Methods; for (int i = 0; i < methods.Count; i++) { MethodDefinition method = methods[i]; ReadGenericParameters(method); if (method.HasParameters) ReadParameters(method); if (method.HasOverrides) Mixin.Read(method.Overrides); if (method.IsPInvokeImpl) Mixin.Read(method.PInvokeInfo); ReadSecurityDeclarations(method); ReadCustomAttributes(method); MethodReturnType return_type = method.MethodReturnType; if (return_type.HasConstant) Mixin.Read(return_type.Constant); if (return_type.HasMarshalInfo) Mixin.Read(return_type.MarshalInfo); ReadCustomAttributes(return_type); } } private void ReadParameters(MethodDefinition method) { Collection parameters = method.Parameters; for (int i = 0; i < parameters.Count; i++) { ParameterDefinition parameter = parameters[i]; if (parameter.HasConstant) Mixin.Read(parameter.Constant); if (parameter.HasMarshalInfo) Mixin.Read(parameter.MarshalInfo); ReadCustomAttributes(parameter); } } private void ReadProperties(TypeDefinition type) { Collection properties = type.Properties; for (int i = 0; i < properties.Count; i++) { PropertyDefinition property = properties[i]; Mixin.Read(property.GetMethod); if (property.HasConstant) Mixin.Read(property.Constant); ReadCustomAttributes(property); } } private void ReadEvents(TypeDefinition type) { Collection events = type.Events; for (int i = 0; i < events.Count; i++) { EventDefinition @event = events[i]; Mixin.Read(@event.AddMethod); ReadCustomAttributes(@event); } } public override void ReadSymbols(ModuleDefinition module) { if (module.symbol_reader == null) return; ReadTypesSymbols(module.Types, module.symbol_reader); } private void ReadTypesSymbols(Collection types, ISymbolReader symbol_reader) { for (int i = 0; i < types.Count; i++) { TypeDefinition type = types[i]; if (type.HasNestedTypes) ReadTypesSymbols(type.NestedTypes, symbol_reader); if (type.HasMethods) ReadMethodsSymbols(type, symbol_reader); } } private void ReadMethodsSymbols(TypeDefinition type, ISymbolReader symbol_reader) { Collection methods = type.Methods; for (int i = 0; i < methods.Count; i++) { MethodDefinition method = methods[i]; if (method.HasBody && method.token.RID != 0 && method.debug_info == null) method.debug_info = symbol_reader.Read(method); } } } internal sealed class DeferredModuleReader : ModuleReader { public DeferredModuleReader(Image image) : base(image, ReadingMode.Deferred) { } protected override void ReadModule() { module.Read(module, (_, reader) => ReadModuleManifest(reader)); } public override void ReadSymbols(ModuleDefinition module) { } } internal sealed class MetadataReader : ByteBuffer { internal readonly Image image; internal readonly ModuleDefinition module; internal readonly MetadataSystem metadata; internal CodeReader code; internal IGenericContext context; private readonly MetadataReader metadata_reader; public MetadataReader(ModuleDefinition module) : base(module.Image.TableHeap.data) { image = module.Image; this.module = module; metadata = module.MetadataSystem; code = new(this); } public MetadataReader(Image image, ModuleDefinition module, MetadataReader metadata_reader) : base(image.TableHeap.data) { this.image = image; this.module = module; metadata = module.MetadataSystem; this.metadata_reader = metadata_reader; } private int GetCodedIndexSize(CodedIndex index) { return image.GetCodedIndexSize(index); } private uint ReadByIndexSize(int size) { if (size == 4) return ReadUInt32(); else return ReadUInt16(); } private byte[] ReadBlob() { BlobHeap blob_heap = image.BlobHeap; if (blob_heap == null) { position += 2; return Empty.Array; } return blob_heap.Read(ReadBlobIndex()); } private byte[] ReadBlob(uint signature) { BlobHeap blob_heap = image.BlobHeap; if (blob_heap == null) return Empty.Array; return blob_heap.Read(signature); } private uint ReadBlobIndex() { BlobHeap blob_heap = image.BlobHeap; return ReadByIndexSize(blob_heap != null ? blob_heap.IndexSize : 2); } private void GetBlobView(uint signature, out byte[] blob, out int index, out int count) { BlobHeap blob_heap = image.BlobHeap; if (blob_heap == null) { blob = null; index = count = 0; return; } blob_heap.GetView(signature, out blob, out index, out count); } private string ReadString() { return image.StringHeap.Read(ReadByIndexSize(image.StringHeap.IndexSize)); } private uint ReadStringIndex() { return ReadByIndexSize(image.StringHeap.IndexSize); } private Guid ReadGuid() { return image.GuidHeap.Read(ReadByIndexSize(image.GuidHeap.IndexSize)); } private uint ReadTableIndex(Table table) { return ReadByIndexSize(image.GetTableIndexSize(table)); } private MetadataToken ReadMetadataToken(CodedIndex index) { return index.GetMetadataToken(ReadByIndexSize(GetCodedIndexSize(index))); } private int MoveTo(Table table) { TableInformation info = image.TableHeap[table]; if (info.Length != 0) position = (int)info.Offset; return (int)info.Length; } private bool MoveTo(Table table, uint row) { TableInformation info = image.TableHeap[table]; uint length = info.Length; if (length == 0 || row > length) return false; position = (int)(info.Offset + info.RowSize * (row - 1)); return true; } public AssemblyNameDefinition ReadAssemblyNameDefinition() { if (MoveTo(Table.Assembly) == 0) return null; AssemblyNameDefinition name = new(); name.HashAlgorithm = (AssemblyHashAlgorithm)ReadUInt32(); PopulateVersionAndFlags(name); name.PublicKey = ReadBlob(); PopulateNameAndCulture(name); return name; } public ModuleDefinition Populate(ModuleDefinition module) { if (MoveTo(Table.Module) == 0) return module; Advance(2); // Generation module.Name = ReadString(); module.Mvid = ReadGuid(); return module; } private void InitializeAssemblyReferences() { if (metadata.AssemblyReferences != null) return; int length = MoveTo(Table.AssemblyRef); AssemblyNameReference[] references = metadata.AssemblyReferences = new AssemblyNameReference [length]; for (uint i = 0; i < length; i++) { AssemblyNameReference reference = new(); reference.token = new(TokenType.AssemblyRef, i + 1); PopulateVersionAndFlags(reference); byte[] key_or_token = ReadBlob(); if (reference.HasPublicKey) reference.PublicKey = key_or_token; else reference.PublicKeyToken = key_or_token; PopulateNameAndCulture(reference); reference.Hash = ReadBlob(); references[i] = reference; } } public Collection ReadAssemblyReferences() { InitializeAssemblyReferences(); Collection references = new(metadata.AssemblyReferences); if (module.IsWindowsMetadata()) module.Projections.AddVirtualReferences(references); return references; } public MethodDefinition ReadEntryPoint() { if (module.Image.EntryPointToken == 0) return null; MetadataToken token = new(module.Image.EntryPointToken); return GetMethodDefinition(token.RID); } public Collection ReadModules() { Collection modules = new(1); modules.Add(module); int length = MoveTo(Table.File); for (uint i = 1; i <= length; i++) { FileAttributes attributes = (FileAttributes)ReadUInt32(); string name = ReadString(); ReadBlobIndex(); if (attributes != FileAttributes.ContainsMetaData) continue; ReaderParameters parameters = new() { ReadingMode = module.ReadingMode, SymbolReaderProvider = module.SymbolReaderProvider, AssemblyResolver = module.AssemblyResolver }; ModuleDefinition netmodule = ModuleDefinition.ReadModule(GetModuleFileName(name), parameters); netmodule.assembly = module.assembly; modules.Add(netmodule); } return modules; } private string GetModuleFileName(string name) { if (module.FileName == null) throw new NotSupportedException(); string path = Path.GetDirectoryName(module.FileName); return Path.Combine(path, name); } private void InitializeModuleReferences() { if (metadata.ModuleReferences != null) return; int length = MoveTo(Table.ModuleRef); ModuleReference[] references = metadata.ModuleReferences = new ModuleReference [length]; for (uint i = 0; i < length; i++) { ModuleReference reference = new(ReadString()); reference.token = new(TokenType.ModuleRef, i + 1); references[i] = reference; } } public Collection ReadModuleReferences() { InitializeModuleReferences(); return new(metadata.ModuleReferences); } public bool HasFileResource() { int length = MoveTo(Table.File); if (length == 0) return false; for (uint i = 1; i <= length; i++) if (ReadFileRecord(i).Col1 == FileAttributes.ContainsNoMetaData) return true; return false; } public Collection ReadResources() { int length = MoveTo(Table.ManifestResource); Collection resources = new(length); for (int i = 1; i <= length; i++) { uint offset = ReadUInt32(); ManifestResourceAttributes flags = (ManifestResourceAttributes)ReadUInt32(); string name = ReadString(); MetadataToken implementation = ReadMetadataToken(CodedIndex.Implementation); Resource resource; if (implementation.RID == 0) { resource = new EmbeddedResource(name, flags, offset, this); } else if (implementation.TokenType == TokenType.AssemblyRef) { resource = new AssemblyLinkedResource(name, flags) { Assembly = (AssemblyNameReference)GetTypeReferenceScope(implementation) }; } else if (implementation.TokenType == TokenType.File) { Row file_record = ReadFileRecord(implementation.RID); resource = new LinkedResource(name, flags) { File = file_record.Col2, hash = ReadBlob(file_record.Col3) }; } else { continue; } resources.Add(resource); } return resources; } private Row ReadFileRecord(uint rid) { int position = this.position; if (!MoveTo(Table.File, rid)) throw new ArgumentException(); Row record = new((FileAttributes)ReadUInt32(), ReadString(), ReadBlobIndex()); this.position = position; return record; } public byte[] GetManagedResource(uint offset) { return image.GetReaderAt(image.Resources.VirtualAddress, offset, (o, reader) => { reader.Advance((int)o); return reader.ReadBytes(reader.ReadInt32()); }) ?? Empty.Array; } private void PopulateVersionAndFlags(AssemblyNameReference name) { name.Version = new(ReadUInt16(), ReadUInt16(), ReadUInt16(), ReadUInt16()); name.Attributes = (AssemblyAttributes)ReadUInt32(); } private void PopulateNameAndCulture(AssemblyNameReference name) { name.Name = ReadString(); name.Culture = ReadString(); } public TypeDefinitionCollection ReadTypes() { InitializeTypeDefinitions(); TypeDefinition[] mtypes = metadata.Types; int type_count = mtypes.Length - metadata.NestedTypes.Count; TypeDefinitionCollection types = new(module, type_count); for (int i = 0; i < mtypes.Length; i++) { TypeDefinition type = mtypes[i]; if (IsNested(type.Attributes)) continue; types.Add(type); } if (image.HasTable(Table.MethodPtr) || image.HasTable(Table.FieldPtr)) CompleteTypes(); return types; } private void CompleteTypes() { TypeDefinition[] types = metadata.Types; for (int i = 0; i < types.Length; i++) { TypeDefinition type = types[i]; Mixin.Read(type.Fields); Mixin.Read(type.Methods); } } private void InitializeTypeDefinitions() { if (metadata.Types != null) return; InitializeNestedTypes(); InitializeFields(); InitializeMethods(); int length = MoveTo(Table.TypeDef); TypeDefinition[] types = metadata.Types = new TypeDefinition [length]; for (uint i = 0; i < length; i++) { if (types[i] != null) continue; types[i] = ReadType(i + 1); } if (module.IsWindowsMetadata()) { for (uint i = 0; i < length; i++) { WindowsRuntimeProjections.Project(types[i]); } } } private static bool IsNested(TypeAttributes attributes) { switch (attributes & TypeAttributes.VisibilityMask) { case TypeAttributes.NestedAssembly: case TypeAttributes.NestedFamANDAssem: case TypeAttributes.NestedFamily: case TypeAttributes.NestedFamORAssem: case TypeAttributes.NestedPrivate: case TypeAttributes.NestedPublic: return true; default: return false; } } public bool HasNestedTypes(TypeDefinition type) { Collection mapping; InitializeNestedTypes(); if (!metadata.TryGetNestedTypeMapping(type, out mapping)) return false; return mapping.Count > 0; } public Collection ReadNestedTypes(TypeDefinition type) { InitializeNestedTypes(); Collection mapping; if (!metadata.TryGetNestedTypeMapping(type, out mapping)) return new MemberDefinitionCollection(type); MemberDefinitionCollection nested_types = new(type, mapping.Count); for (int i = 0; i < mapping.Count; i++) { TypeDefinition nested_type = GetTypeDefinition(mapping[i]); if (nested_type != null) nested_types.Add(nested_type); } metadata.RemoveNestedTypeMapping(type); return nested_types; } private void InitializeNestedTypes() { if (metadata.NestedTypes != null) return; int length = MoveTo(Table.NestedClass); metadata.NestedTypes = new(length); metadata.ReverseNestedTypes = new(length); if (length == 0) return; for (int i = 1; i <= length; i++) { uint nested = ReadTableIndex(Table.TypeDef); uint declaring = ReadTableIndex(Table.TypeDef); AddNestedMapping(declaring, nested); } } private void AddNestedMapping(uint declaring, uint nested) { metadata.SetNestedTypeMapping(declaring, AddMapping(metadata.NestedTypes, declaring, nested)); metadata.SetReverseNestedTypeMapping(nested, declaring); } private static Collection AddMapping(Dictionary> cache, TKey key, TValue value) { Collection mapped; if (!cache.TryGetValue(key, out mapped)) { mapped = new(); } mapped.Add(value); return mapped; } private TypeDefinition ReadType(uint rid) { if (!MoveTo(Table.TypeDef, rid)) return null; TypeAttributes attributes = (TypeAttributes)ReadUInt32(); string name = ReadString(); string @namespace = ReadString(); TypeDefinition type = new(@namespace, name, attributes); type.token = new(TokenType.TypeDef, rid); type.scope = module; type.module = module; metadata.AddTypeDefinition(type); context = type; type.BaseType = GetTypeDefOrRef(ReadMetadataToken(CodedIndex.TypeDefOrRef)); type.fields_range = ReadListRange(rid, Table.TypeDef, Table.Field); type.methods_range = ReadListRange(rid, Table.TypeDef, Table.Method); if (IsNested(attributes)) type.DeclaringType = GetNestedTypeDeclaringType(type); return type; } private TypeDefinition GetNestedTypeDeclaringType(TypeDefinition type) { uint declaring_rid; if (!metadata.TryGetReverseNestedTypeMapping(type, out declaring_rid)) return null; metadata.RemoveReverseNestedTypeMapping(type); return GetTypeDefinition(declaring_rid); } private Range ReadListRange(uint current_index, Table current, Table target) { Range list = new(); uint start = ReadTableIndex(target); if (start == 0) return list; uint next_index; TableInformation current_table = image.TableHeap[current]; if (current_index == current_table.Length) { next_index = image.TableHeap[target].Length + 1; } else { int position = this.position; this.position += (int)(current_table.RowSize - image.GetTableIndexSize(target)); next_index = ReadTableIndex(target); this.position = position; } list.Start = start; list.Length = next_index - start; return list; } public Row ReadTypeLayout(TypeDefinition type) { InitializeTypeLayouts(); Row class_layout; uint rid = type.token.RID; if (!metadata.ClassLayouts.TryGetValue(rid, out class_layout)) return new(Mixin.NoDataMarker, Mixin.NoDataMarker); type.PackingSize = (short)class_layout.Col1; type.ClassSize = (int)class_layout.Col2; metadata.ClassLayouts.Remove(rid); return new((short)class_layout.Col1, (int)class_layout.Col2); } private void InitializeTypeLayouts() { if (metadata.ClassLayouts != null) return; int length = MoveTo(Table.ClassLayout); Dictionary> class_layouts = metadata.ClassLayouts = new(length); for (uint i = 0; i < length; i++) { ushort packing_size = ReadUInt16(); uint class_size = ReadUInt32(); uint parent = ReadTableIndex(Table.TypeDef); class_layouts.Add(parent, new(packing_size, class_size)); } } public TypeReference GetTypeDefOrRef(MetadataToken token) { return (TypeReference)LookupToken(token); } public TypeDefinition GetTypeDefinition(uint rid) { InitializeTypeDefinitions(); TypeDefinition type = metadata.GetTypeDefinition(rid); if (type != null) return type; type = ReadTypeDefinition(rid); if (module.IsWindowsMetadata()) WindowsRuntimeProjections.Project(type); return type; } private TypeDefinition ReadTypeDefinition(uint rid) { if (!MoveTo(Table.TypeDef, rid)) return null; return ReadType(rid); } private void InitializeTypeReferences() { if (metadata.TypeReferences != null) return; metadata.TypeReferences = new TypeReference [image.GetTableLength(Table.TypeRef)]; } public TypeReference GetTypeReference(string scope, string full_name) { InitializeTypeReferences(); int length = metadata.TypeReferences.Length; for (uint i = 1; i <= length; i++) { TypeReference type = GetTypeReference(i); if (type.FullName != full_name) continue; if (string.IsNullOrEmpty(scope)) return type; if (type.Scope.Name == scope) return type; } return null; } private TypeReference GetTypeReference(uint rid) { InitializeTypeReferences(); TypeReference type = metadata.GetTypeReference(rid); if (type != null) return type; return ReadTypeReference(rid); } private TypeReference ReadTypeReference(uint rid) { if (!MoveTo(Table.TypeRef, rid)) return null; TypeReference declaring_type = null; IMetadataScope scope; MetadataToken scope_token = ReadMetadataToken(CodedIndex.ResolutionScope); string name = ReadString(); string @namespace = ReadString(); TypeReference type = new(@namespace, name, module, null); type.token = new(TokenType.TypeRef, rid); metadata.AddTypeReference(type); if (scope_token.TokenType == TokenType.TypeRef) { if (scope_token.RID != rid) { declaring_type = GetTypeDefOrRef(scope_token); scope = declaring_type != null ? declaring_type.Scope : module; } else // obfuscated typeref row pointing to self { scope = module; } } else { scope = GetTypeReferenceScope(scope_token); } type.scope = scope; type.DeclaringType = declaring_type; MetadataSystem.TryProcessPrimitiveTypeReference(type); if (type.Module.IsWindowsMetadata()) WindowsRuntimeProjections.Project(type); return type; } private IMetadataScope GetTypeReferenceScope(MetadataToken scope) { if (scope.TokenType == TokenType.Module) return module; IMetadataScope[] scopes; switch (scope.TokenType) { case TokenType.AssemblyRef: InitializeAssemblyReferences(); scopes = metadata.AssemblyReferences; break; case TokenType.ModuleRef: InitializeModuleReferences(); scopes = metadata.ModuleReferences; break; default: throw new NotSupportedException(); } uint index = scope.RID - 1; if (index < 0 || index >= scopes.Length) return null; return scopes[index]; } public IEnumerable GetTypeReferences() { InitializeTypeReferences(); int length = image.GetTableLength(Table.TypeRef); TypeReference[] type_references = new TypeReference [length]; for (uint i = 1; i <= length; i++) type_references[i - 1] = GetTypeReference(i); return type_references; } private TypeReference GetTypeSpecification(uint rid) { if (!MoveTo(Table.TypeSpec, rid)) return null; SignatureReader reader = ReadSignature(ReadBlobIndex()); TypeReference type = reader.ReadTypeSignature(); if (type.token.RID == 0) type.token = new(TokenType.TypeSpec, rid); return type; } private SignatureReader ReadSignature(uint signature) { return new(signature, this); } public bool HasInterfaces(TypeDefinition type) { InitializeInterfaces(); Collection> mapping; return metadata.TryGetInterfaceMapping(type, out mapping); } public InterfaceImplementationCollection ReadInterfaces(TypeDefinition type) { InitializeInterfaces(); Collection> mapping; if (!metadata.TryGetInterfaceMapping(type, out mapping)) return new(type); InterfaceImplementationCollection interfaces = new(type, mapping.Count); context = type; for (int i = 0; i < mapping.Count; i++) { interfaces.Add(new(GetTypeDefOrRef(mapping[i].Col2), new(TokenType.InterfaceImpl, mapping[i].Col1))); } metadata.RemoveInterfaceMapping(type); return interfaces; } private void InitializeInterfaces() { if (metadata.Interfaces != null) return; int length = MoveTo(Table.InterfaceImpl); metadata.Interfaces = new(length); for (uint i = 1; i <= length; i++) { uint type = ReadTableIndex(Table.TypeDef); MetadataToken @interface = ReadMetadataToken(CodedIndex.TypeDefOrRef); AddInterfaceMapping(type, new(i, @interface)); } } private void AddInterfaceMapping(uint type, Row @interface) { metadata.SetInterfaceMapping(type, AddMapping(metadata.Interfaces, type, @interface)); } public Collection ReadFields(TypeDefinition type) { Range fields_range = type.fields_range; if (fields_range.Length == 0) return new MemberDefinitionCollection(type); MemberDefinitionCollection fields = new(type, (int)fields_range.Length); context = type; if (!MoveTo(Table.FieldPtr, fields_range.Start)) { if (!MoveTo(Table.Field, fields_range.Start)) return fields; for (uint i = 0; i < fields_range.Length; i++) ReadField(fields_range.Start + i, fields); } else { ReadPointers(Table.FieldPtr, Table.Field, fields_range, fields, ReadField); } return fields; } private void ReadField(uint field_rid, Collection fields) { FieldAttributes attributes = (FieldAttributes)ReadUInt16(); string name = ReadString(); uint signature = ReadBlobIndex(); FieldDefinition field = new(name, attributes, ReadFieldType(signature)); field.token = new(TokenType.Field, field_rid); metadata.AddFieldDefinition(field); if (IsDeleted(field)) return; fields.Add(field); if (module.IsWindowsMetadata()) WindowsRuntimeProjections.Project(field); } private void InitializeFields() { if (metadata.Fields != null) return; metadata.Fields = new FieldDefinition [image.GetTableLength(Table.Field)]; } private TypeReference ReadFieldType(uint signature) { SignatureReader reader = ReadSignature(signature); const byte field_sig = 0x6; if (reader.ReadByte() != field_sig) throw new NotSupportedException(); return reader.ReadTypeSignature(); } public int ReadFieldRVA(FieldDefinition field) { InitializeFieldRVAs(); uint rid = field.token.RID; RVA rva; if (!metadata.FieldRVAs.TryGetValue(rid, out rva)) return 0; int size = GetFieldTypeSize(field.FieldType); if (size == 0 || rva == 0) return 0; metadata.FieldRVAs.Remove(rid); field.InitialValue = GetFieldInitializeValue(size, rva); return (int)rva; } private byte[] GetFieldInitializeValue(int size, RVA rva) { return image.GetReaderAt(rva, size, (s, reader) => reader.ReadBytes(s)) ?? Empty.Array; } private static int GetFieldTypeSize(TypeReference type) { int size = 0; switch (type.etype) { case ElementType.Boolean: case ElementType.U1: case ElementType.I1: size = 1; break; case ElementType.U2: case ElementType.I2: case ElementType.Char: size = 2; break; case ElementType.U4: case ElementType.I4: case ElementType.R4: size = 4; break; case ElementType.U8: case ElementType.I8: case ElementType.R8: size = 8; break; case ElementType.Ptr: case ElementType.FnPtr: size = IntPtr.Size; break; case ElementType.CModOpt: case ElementType.CModReqD: return GetFieldTypeSize(((IModifierType)type).ElementType); default: TypeDefinition field_type = type.Resolve(); if (field_type != null && field_type.HasLayoutInfo) size = field_type.ClassSize; break; } return size; } private void InitializeFieldRVAs() { if (metadata.FieldRVAs != null) return; int length = MoveTo(Table.FieldRVA); Dictionary field_rvas = metadata.FieldRVAs = new(length); for (int i = 0; i < length; i++) { uint rva = ReadUInt32(); uint field = ReadTableIndex(Table.Field); field_rvas.Add(field, rva); } } public int ReadFieldLayout(FieldDefinition field) { InitializeFieldLayouts(); uint rid = field.token.RID; uint offset; if (!metadata.FieldLayouts.TryGetValue(rid, out offset)) return Mixin.NoDataMarker; metadata.FieldLayouts.Remove(rid); return (int)offset; } private void InitializeFieldLayouts() { if (metadata.FieldLayouts != null) return; int length = MoveTo(Table.FieldLayout); Dictionary field_layouts = metadata.FieldLayouts = new(length); for (int i = 0; i < length; i++) { uint offset = ReadUInt32(); uint field = ReadTableIndex(Table.Field); field_layouts.Add(field, offset); } } public bool HasEvents(TypeDefinition type) { InitializeEvents(); Range range; if (!metadata.TryGetEventsRange(type, out range)) return false; return range.Length > 0; } public Collection ReadEvents(TypeDefinition type) { InitializeEvents(); Range range; if (!metadata.TryGetEventsRange(type, out range)) return new MemberDefinitionCollection(type); MemberDefinitionCollection events = new(type, (int)range.Length); metadata.RemoveEventsRange(type); if (range.Length == 0) return events; context = type; if (!MoveTo(Table.EventPtr, range.Start)) { if (!MoveTo(Table.Event, range.Start)) return events; for (uint i = 0; i < range.Length; i++) ReadEvent(range.Start + i, events); } else { ReadPointers(Table.EventPtr, Table.Event, range, events, ReadEvent); } return events; } private void ReadEvent(uint event_rid, Collection events) { EventAttributes attributes = (EventAttributes)ReadUInt16(); string name = ReadString(); TypeReference event_type = GetTypeDefOrRef(ReadMetadataToken(CodedIndex.TypeDefOrRef)); EventDefinition @event = new(name, attributes, event_type); @event.token = new(TokenType.Event, event_rid); if (IsDeleted(@event)) return; events.Add(@event); } private void InitializeEvents() { if (metadata.Events != null) return; int length = MoveTo(Table.EventMap); metadata.Events = new(length); for (uint i = 1; i <= length; i++) { uint type_rid = ReadTableIndex(Table.TypeDef); Range events_range = ReadListRange(i, Table.EventMap, Table.Event); metadata.AddEventsRange(type_rid, events_range); } } public bool HasProperties(TypeDefinition type) { InitializeProperties(); Range range; if (!metadata.TryGetPropertiesRange(type, out range)) return false; return range.Length > 0; } public Collection ReadProperties(TypeDefinition type) { InitializeProperties(); Range range; if (!metadata.TryGetPropertiesRange(type, out range)) return new MemberDefinitionCollection(type); metadata.RemovePropertiesRange(type); MemberDefinitionCollection properties = new(type, (int)range.Length); if (range.Length == 0) return properties; context = type; if (!MoveTo(Table.PropertyPtr, range.Start)) { if (!MoveTo(Table.Property, range.Start)) return properties; for (uint i = 0; i < range.Length; i++) ReadProperty(range.Start + i, properties); } else { ReadPointers(Table.PropertyPtr, Table.Property, range, properties, ReadProperty); } return properties; } private void ReadProperty(uint property_rid, Collection properties) { PropertyAttributes attributes = (PropertyAttributes)ReadUInt16(); string name = ReadString(); uint signature = ReadBlobIndex(); SignatureReader reader = ReadSignature(signature); const byte property_signature = 0x8; byte calling_convention = reader.ReadByte(); if ((calling_convention & property_signature) == 0) throw new NotSupportedException(); bool has_this = (calling_convention & 0x20) != 0; reader.ReadCompressedUInt32(); // count PropertyDefinition property = new(name, attributes, reader.ReadTypeSignature()); property.HasThis = has_this; property.token = new(TokenType.Property, property_rid); if (IsDeleted(property)) return; properties.Add(property); } private void InitializeProperties() { if (metadata.Properties != null) return; int length = MoveTo(Table.PropertyMap); metadata.Properties = new(length); for (uint i = 1; i <= length; i++) { uint type_rid = ReadTableIndex(Table.TypeDef); Range properties_range = ReadListRange(i, Table.PropertyMap, Table.Property); metadata.AddPropertiesRange(type_rid, properties_range); } } private MethodSemanticsAttributes ReadMethodSemantics(MethodDefinition method) { InitializeMethodSemantics(); Row row; if (!metadata.Semantics.TryGetValue(method.token.RID, out row)) return MethodSemanticsAttributes.None; TypeDefinition type = method.DeclaringType; switch (row.Col1) { case MethodSemanticsAttributes.AddOn: GetEvent(type, row.Col2).add_method = method; break; case MethodSemanticsAttributes.Fire: GetEvent(type, row.Col2).invoke_method = method; break; case MethodSemanticsAttributes.RemoveOn: GetEvent(type, row.Col2).remove_method = method; break; case MethodSemanticsAttributes.Getter: GetProperty(type, row.Col2).get_method = method; break; case MethodSemanticsAttributes.Setter: GetProperty(type, row.Col2).set_method = method; break; case MethodSemanticsAttributes.Other: switch (row.Col2.TokenType) { case TokenType.Event: { EventDefinition @event = GetEvent(type, row.Col2); if (@event.other_methods == null) @event.other_methods = new(); @event.other_methods.Add(method); break; } case TokenType.Property: { PropertyDefinition property = GetProperty(type, row.Col2); if (property.other_methods == null) property.other_methods = new(); property.other_methods.Add(method); break; } default: throw new NotSupportedException(); } break; default: throw new NotSupportedException(); } metadata.Semantics.Remove(method.token.RID); return row.Col1; } private static EventDefinition GetEvent(TypeDefinition type, MetadataToken token) { if (token.TokenType != TokenType.Event) throw new ArgumentException(); return GetMember(type.Events, token); } private static PropertyDefinition GetProperty(TypeDefinition type, MetadataToken token) { if (token.TokenType != TokenType.Property) throw new ArgumentException(); return GetMember(type.Properties, token); } private static TMember GetMember(Collection members, MetadataToken token) where TMember : IMemberDefinition { for (int i = 0; i < members.Count; i++) { TMember member = members[i]; if (member.MetadataToken == token) return member; } throw new ArgumentException(); } private void InitializeMethodSemantics() { if (metadata.Semantics != null) return; int length = MoveTo(Table.MethodSemantics); Dictionary> semantics = metadata.Semantics = new(0); for (uint i = 0; i < length; i++) { MethodSemanticsAttributes attributes = (MethodSemanticsAttributes)ReadUInt16(); uint method_rid = ReadTableIndex(Table.Method); MetadataToken association = ReadMetadataToken(CodedIndex.HasSemantics); semantics[method_rid] = new(attributes, association); } } public void ReadMethods(PropertyDefinition property) { ReadAllSemantics(property.DeclaringType); } public void ReadMethods(EventDefinition @event) { ReadAllSemantics(@event.DeclaringType); } public void ReadAllSemantics(MethodDefinition method) { ReadAllSemantics(method.DeclaringType); } private void ReadAllSemantics(TypeDefinition type) { Collection methods = type.Methods; for (int i = 0; i < methods.Count; i++) { MethodDefinition method = methods[i]; if (method.sem_attrs_ready) continue; method.sem_attrs = ReadMethodSemantics(method); method.sem_attrs_ready = true; } } public Collection ReadMethods(TypeDefinition type) { Range methods_range = type.methods_range; if (methods_range.Length == 0) return new MemberDefinitionCollection(type); MemberDefinitionCollection methods = new(type, (int)methods_range.Length); if (!MoveTo(Table.MethodPtr, methods_range.Start)) { if (!MoveTo(Table.Method, methods_range.Start)) return methods; for (uint i = 0; i < methods_range.Length; i++) ReadMethod(methods_range.Start + i, methods); } else { ReadPointers(Table.MethodPtr, Table.Method, methods_range, methods, ReadMethod); } return methods; } private void ReadPointers(Table ptr, Table table, Range range, Collection members, Action> reader) where TMember : IMemberDefinition { for (uint i = 0; i < range.Length; i++) { MoveTo(ptr, range.Start + i); uint rid = ReadTableIndex(table); MoveTo(table, rid); reader(rid, members); } } private static bool IsDeleted(IMemberDefinition member) { return member.IsSpecialName && member.Name == "_Deleted"; } private void InitializeMethods() { if (metadata.Methods != null) return; metadata.Methods = new MethodDefinition [image.GetTableLength(Table.Method)]; } private void ReadMethod(uint method_rid, Collection methods) { MethodDefinition method = new(); method.rva = ReadUInt32(); method.ImplAttributes = (MethodImplAttributes)ReadUInt16(); method.Attributes = (MethodAttributes)ReadUInt16(); method.Name = ReadString(); method.token = new(TokenType.Method, method_rid); if (IsDeleted(method)) return; methods.Add(method); // attach method uint signature = ReadBlobIndex(); Range param_range = ReadListRange(method_rid, Table.Method, Table.Param); context = method; ReadMethodSignature(signature, method); metadata.AddMethodDefinition(method); if (param_range.Length != 0) { int position = this.position; ReadParameters(method, param_range); this.position = position; } if (module.IsWindowsMetadata()) WindowsRuntimeProjections.Project(method); } private void ReadParameters(MethodDefinition method, Range param_range) { if (!MoveTo(Table.ParamPtr, param_range.Start)) { if (!MoveTo(Table.Param, param_range.Start)) return; for (uint i = 0; i < param_range.Length; i++) ReadParameter(param_range.Start + i, method); } else { ReadParameterPointers(method, param_range); } } private void ReadParameterPointers(MethodDefinition method, Range range) { for (uint i = 0; i < range.Length; i++) { MoveTo(Table.ParamPtr, range.Start + i); uint rid = ReadTableIndex(Table.Param); MoveTo(Table.Param, rid); ReadParameter(rid, method); } } private void ReadParameter(uint param_rid, MethodDefinition method) { ParameterAttributes attributes = (ParameterAttributes)ReadUInt16(); ushort sequence = ReadUInt16(); string name = ReadString(); ParameterDefinition parameter = sequence == 0 ? method.MethodReturnType.Parameter : method.Parameters[sequence - 1]; parameter.token = new(TokenType.Param, param_rid); parameter.Name = name; parameter.Attributes = attributes; } private void ReadMethodSignature(uint signature, IMethodSignature method) { SignatureReader reader = ReadSignature(signature); reader.ReadMethodSignature(method); } public PInvokeInfo ReadPInvokeInfo(MethodDefinition method) { InitializePInvokes(); Row row; uint rid = method.token.RID; if (!metadata.PInvokes.TryGetValue(rid, out row)) return null; metadata.PInvokes.Remove(rid); return new(row.Col1, image.StringHeap.Read(row.Col2), module.ModuleReferences[(int)row.Col3 - 1]); } private void InitializePInvokes() { if (metadata.PInvokes != null) return; int length = MoveTo(Table.ImplMap); Dictionary> pinvokes = metadata.PInvokes = new(length); for (int i = 1; i <= length; i++) { PInvokeAttributes attributes = (PInvokeAttributes)ReadUInt16(); MetadataToken method = ReadMetadataToken(CodedIndex.MemberForwarded); uint name = ReadStringIndex(); uint scope = ReadTableIndex(Table.File); if (method.TokenType != TokenType.Method) continue; pinvokes.Add(method.RID, new(attributes, name, scope)); } } public bool HasGenericParameters(IGenericParameterProvider provider) { InitializeGenericParameters(); Range[] ranges; if (!metadata.TryGetGenericParameterRanges(provider, out ranges)) return false; return RangesSize(ranges) > 0; } public Collection ReadGenericParameters(IGenericParameterProvider provider) { InitializeGenericParameters(); Range[] ranges; if (!metadata.TryGetGenericParameterRanges(provider, out ranges)) return new GenericParameterCollection(provider); metadata.RemoveGenericParameterRange(provider); GenericParameterCollection generic_parameters = new(provider, RangesSize(ranges)); for (int i = 0; i < ranges.Length; i++) ReadGenericParametersRange(ranges[i], provider, generic_parameters); return generic_parameters; } private void ReadGenericParametersRange(Range range, IGenericParameterProvider provider, GenericParameterCollection generic_parameters) { if (!MoveTo(Table.GenericParam, range.Start)) return; for (uint i = 0; i < range.Length; i++) { ReadUInt16(); // index GenericParameterAttributes flags = (GenericParameterAttributes)ReadUInt16(); ReadMetadataToken(CodedIndex.TypeOrMethodDef); string name = ReadString(); GenericParameter parameter = new(name, provider); parameter.token = new(TokenType.GenericParam, range.Start + i); parameter.Attributes = flags; generic_parameters.Add(parameter); } } private void InitializeGenericParameters() { if (metadata.GenericParameters != null) return; metadata.GenericParameters = InitializeRanges(Table.GenericParam, () => { Advance(4); MetadataToken next = ReadMetadataToken(CodedIndex.TypeOrMethodDef); ReadStringIndex(); return next; }); } private Dictionary InitializeRanges(Table table, Func get_next) { int length = MoveTo(table); Dictionary ranges = new(length); if (length == 0) return ranges; MetadataToken owner = MetadataToken.Zero; Range range = new(1, 0); for (uint i = 1; i <= length; i++) { MetadataToken next = get_next(); if (i == 1) { owner = next; range.Length++; } else if (next != owner) { AddRange(ranges, owner, range); range = new(i, 1); owner = next; } else { range.Length++; } } AddRange(ranges, owner, range); return ranges; } private static void AddRange(Dictionary ranges, MetadataToken owner, Range range) { if (owner.RID == 0) return; Range[] slots; if (!ranges.TryGetValue(owner, out slots)) { ranges.Add(owner, new[] { range }); return; } ranges[owner] = slots.Add(range); } public bool HasGenericConstraints(GenericParameter generic_parameter) { InitializeGenericConstraints(); Collection> mapping; if (!metadata.TryGetGenericConstraintMapping(generic_parameter, out mapping)) return false; return mapping.Count > 0; } public GenericParameterConstraintCollection ReadGenericConstraints(GenericParameter generic_parameter) { InitializeGenericConstraints(); Collection> mapping; if (!metadata.TryGetGenericConstraintMapping(generic_parameter, out mapping)) return new(generic_parameter); GenericParameterConstraintCollection constraints = new(generic_parameter, mapping.Count); context = (IGenericContext)generic_parameter.Owner; for (int i = 0; i < mapping.Count; i++) { constraints.Add(new(GetTypeDefOrRef(mapping[i].Col2), new(TokenType.GenericParamConstraint, mapping[i].Col1))); } metadata.RemoveGenericConstraintMapping(generic_parameter); return constraints; } private void InitializeGenericConstraints() { if (metadata.GenericConstraints != null) return; int length = MoveTo(Table.GenericParamConstraint); metadata.GenericConstraints = new(length); for (uint i = 1; i <= length; i++) { AddGenericConstraintMapping(ReadTableIndex(Table.GenericParam), new(i, ReadMetadataToken(CodedIndex.TypeDefOrRef))); } } private void AddGenericConstraintMapping(uint generic_parameter, Row constraint) { metadata.SetGenericConstraintMapping(generic_parameter, AddMapping(metadata.GenericConstraints, generic_parameter, constraint)); } public bool HasOverrides(MethodDefinition method) { InitializeOverrides(); Collection mapping; if (!metadata.TryGetOverrideMapping(method, out mapping)) return false; return mapping.Count > 0; } public Collection ReadOverrides(MethodDefinition method) { InitializeOverrides(); Collection mapping; if (!metadata.TryGetOverrideMapping(method, out mapping)) return new(); Collection overrides = new(mapping.Count); context = method; for (int i = 0; i < mapping.Count; i++) overrides.Add((MethodReference)LookupToken(mapping[i])); metadata.RemoveOverrideMapping(method); return overrides; } private void InitializeOverrides() { if (metadata.Overrides != null) return; int length = MoveTo(Table.MethodImpl); metadata.Overrides = new(length); for (int i = 1; i <= length; i++) { ReadTableIndex(Table.TypeDef); MetadataToken method = ReadMetadataToken(CodedIndex.MethodDefOrRef); if (method.TokenType != TokenType.Method) throw new NotSupportedException(); MetadataToken @override = ReadMetadataToken(CodedIndex.MethodDefOrRef); AddOverrideMapping(method.RID, @override); } } private void AddOverrideMapping(uint method_rid, MetadataToken @override) { metadata.SetOverrideMapping(method_rid, AddMapping(metadata.Overrides, method_rid, @override)); } public MethodBody ReadMethodBody(MethodDefinition method) { return code.ReadMethodBody(method); } public int ReadCodeSize(MethodDefinition method) { return code.ReadCodeSize(method); } public CallSite ReadCallSite(MetadataToken token) { if (!MoveTo(Table.StandAloneSig, token.RID)) return null; uint signature = ReadBlobIndex(); CallSite call_site = new(); ReadMethodSignature(signature, call_site); call_site.MetadataToken = token; return call_site; } public VariableDefinitionCollection ReadVariables(MetadataToken local_var_token, MethodDefinition method = null) { if (!MoveTo(Table.StandAloneSig, local_var_token.RID)) return null; SignatureReader reader = ReadSignature(ReadBlobIndex()); const byte local_sig = 0x7; if (reader.ReadByte() != local_sig) throw new NotSupportedException(); uint count = reader.ReadCompressedUInt32(); if (count == 0) return null; VariableDefinitionCollection variables = new(method, (int)count); for (int i = 0; i < count; i++) variables.Add(new(reader.ReadTypeSignature())); return variables; } public IMetadataTokenProvider LookupToken(MetadataToken token) { uint rid = token.RID; if (rid == 0) return null; if (metadata_reader != null) return metadata_reader.LookupToken(token); IMetadataTokenProvider element; int position = this.position; IGenericContext context = this.context; switch (token.TokenType) { case TokenType.TypeDef: element = GetTypeDefinition(rid); break; case TokenType.TypeRef: element = GetTypeReference(rid); break; case TokenType.TypeSpec: element = GetTypeSpecification(rid); break; case TokenType.Field: element = GetFieldDefinition(rid); break; case TokenType.Method: element = GetMethodDefinition(rid); break; case TokenType.MemberRef: element = GetMemberReference(rid); break; case TokenType.MethodSpec: element = GetMethodSpecification(rid); break; default: return null; } this.position = position; this.context = context; return element; } public FieldDefinition GetFieldDefinition(uint rid) { InitializeTypeDefinitions(); FieldDefinition field = metadata.GetFieldDefinition(rid); if (field != null) return field; return LookupField(rid); } private FieldDefinition LookupField(uint rid) { TypeDefinition type = metadata.GetFieldDeclaringType(rid); if (type == null) return null; Mixin.Read(type.Fields); return metadata.GetFieldDefinition(rid); } public MethodDefinition GetMethodDefinition(uint rid) { InitializeTypeDefinitions(); MethodDefinition method = metadata.GetMethodDefinition(rid); if (method != null) return method; return LookupMethod(rid); } private MethodDefinition LookupMethod(uint rid) { TypeDefinition type = metadata.GetMethodDeclaringType(rid); if (type == null) return null; Mixin.Read(type.Methods); return metadata.GetMethodDefinition(rid); } private MethodSpecification GetMethodSpecification(uint rid) { if (!MoveTo(Table.MethodSpec, rid)) return null; MethodReference element_method = (MethodReference)LookupToken(ReadMetadataToken(CodedIndex.MethodDefOrRef)); uint signature = ReadBlobIndex(); MethodSpecification method_spec = ReadMethodSpecSignature(signature, element_method); method_spec.token = new(TokenType.MethodSpec, rid); return method_spec; } private MethodSpecification ReadMethodSpecSignature(uint signature, MethodReference method) { SignatureReader reader = ReadSignature(signature); const byte methodspec_sig = 0x0a; byte call_conv = reader.ReadByte(); if (call_conv != methodspec_sig) throw new NotSupportedException(); uint arity = reader.ReadCompressedUInt32(); GenericInstanceMethod instance = new(method, (int)arity); reader.ReadGenericInstanceSignature(method, instance, arity); return instance; } private MemberReference GetMemberReference(uint rid) { InitializeMemberReferences(); MemberReference member = metadata.GetMemberReference(rid); if (member != null) return member; member = ReadMemberReference(rid); if (member != null && !member.ContainsGenericParameter) metadata.AddMemberReference(member); return member; } private MemberReference ReadMemberReference(uint rid) { if (!MoveTo(Table.MemberRef, rid)) return null; MetadataToken token = ReadMetadataToken(CodedIndex.MemberRefParent); string name = ReadString(); uint signature = ReadBlobIndex(); MemberReference member; switch (token.TokenType) { case TokenType.TypeDef: case TokenType.TypeRef: case TokenType.TypeSpec: member = ReadTypeMemberReference(token, name, signature); break; case TokenType.Method: member = ReadMethodMemberReference(token, name, signature); break; default: throw new NotSupportedException(); } member.token = new(TokenType.MemberRef, rid); return member; } private MemberReference ReadTypeMemberReference(MetadataToken type, string name, uint signature) { TypeReference declaring_type = GetTypeDefOrRef(type); if (!declaring_type.IsArray) context = declaring_type; MemberReference member = ReadMemberReferenceSignature(signature, declaring_type); member.Name = name; return member; } private MemberReference ReadMemberReferenceSignature(uint signature, TypeReference declaring_type) { SignatureReader reader = ReadSignature(signature); const byte field_sig = 0x6; if (reader.buffer[reader.position] == field_sig) { reader.position++; FieldReference field = new(); field.DeclaringType = declaring_type; field.FieldType = reader.ReadTypeSignature(); return field; } else { MethodReference method = new(); method.DeclaringType = declaring_type; reader.ReadMethodSignature(method); return method; } } private MemberReference ReadMethodMemberReference(MetadataToken token, string name, uint signature) { MethodDefinition method = GetMethodDefinition(token.RID); context = method; MemberReference member = ReadMemberReferenceSignature(signature, method.DeclaringType); member.Name = name; return member; } private void InitializeMemberReferences() { if (metadata.MemberReferences != null) return; metadata.MemberReferences = new MemberReference [image.GetTableLength(Table.MemberRef)]; } public IEnumerable GetMemberReferences() { InitializeMemberReferences(); int length = image.GetTableLength(Table.MemberRef); TypeSystem type_system = module.TypeSystem; MethodDefinition context = new(string.Empty, MethodAttributes.Static, type_system.Void); context.DeclaringType = new(string.Empty, string.Empty, TypeAttributes.Public); MemberReference[] member_references = new MemberReference [length]; for (uint i = 1; i <= length; i++) { this.context = context; member_references[i - 1] = GetMemberReference(i); } return member_references; } private void InitializeConstants() { if (metadata.Constants != null) return; int length = MoveTo(Table.Constant); Dictionary> constants = metadata.Constants = new(length); for (uint i = 1; i <= length; i++) { ElementType type = (ElementType)ReadUInt16(); MetadataToken owner = ReadMetadataToken(CodedIndex.HasConstant); uint signature = ReadBlobIndex(); constants.Add(owner, new(type, signature)); } } public TypeReference ReadConstantSignature(MetadataToken token) { if (token.TokenType != TokenType.Signature) throw new NotSupportedException(); if (token.RID == 0) return null; if (!MoveTo(Table.StandAloneSig, token.RID)) return null; return ReadFieldType(ReadBlobIndex()); } public object ReadConstant(IConstantProvider owner) { InitializeConstants(); Row row; if (!metadata.Constants.TryGetValue(owner.MetadataToken, out row)) return Mixin.NoValue; metadata.Constants.Remove(owner.MetadataToken); return ReadConstantValue(row.Col1, row.Col2); } private object ReadConstantValue(ElementType etype, uint signature) { switch (etype) { case ElementType.Class: case ElementType.Object: return null; case ElementType.String: return ReadConstantString(signature); default: return ReadConstantPrimitive(etype, signature); } } private string ReadConstantString(uint signature) { byte[] blob; int index, count; GetBlobView(signature, out blob, out index, out count); if (count == 0) return string.Empty; if ((count & 1) == 1) count--; return Encoding.Unicode.GetString(blob, index, count); } private object ReadConstantPrimitive(ElementType type, uint signature) { SignatureReader reader = ReadSignature(signature); return reader.ReadConstantSignature(type); } internal void InitializeCustomAttributes() { if (metadata.CustomAttributes != null) return; metadata.CustomAttributes = InitializeRanges(Table.CustomAttribute, () => { MetadataToken next = ReadMetadataToken(CodedIndex.HasCustomAttribute); ReadMetadataToken(CodedIndex.CustomAttributeType); ReadBlobIndex(); return next; }); } public bool HasCustomAttributes(ICustomAttributeProvider owner) { InitializeCustomAttributes(); Range[] ranges; if (!metadata.TryGetCustomAttributeRanges(owner, out ranges)) return false; return RangesSize(ranges) > 0; } public Collection ReadCustomAttributes(ICustomAttributeProvider owner) { InitializeCustomAttributes(); Range[] ranges; if (!metadata.TryGetCustomAttributeRanges(owner, out ranges)) return new(); Collection custom_attributes = new(RangesSize(ranges)); for (int i = 0; i < ranges.Length; i++) ReadCustomAttributeRange(ranges[i], custom_attributes); metadata.RemoveCustomAttributeRange(owner); if (module.IsWindowsMetadata()) foreach (CustomAttribute custom_attribute in custom_attributes) WindowsRuntimeProjections.Project(owner, custom_attribute); return custom_attributes; } private void ReadCustomAttributeRange(Range range, Collection custom_attributes) { if (!MoveTo(Table.CustomAttribute, range.Start)) return; for (int i = 0; i < range.Length; i++) { ReadMetadataToken(CodedIndex.HasCustomAttribute); MethodReference constructor = (MethodReference)LookupToken(ReadMetadataToken(CodedIndex.CustomAttributeType)); uint signature = ReadBlobIndex(); custom_attributes.Add(new(signature, constructor)); } } private static int RangesSize(Range[] ranges) { uint size = 0; for (int i = 0; i < ranges.Length; i++) size += ranges[i].Length; return (int)size; } public IEnumerable GetCustomAttributes() { InitializeTypeDefinitions(); uint length = image.TableHeap[Table.CustomAttribute].Length; Collection custom_attributes = new((int)length); ReadCustomAttributeRange(new(1, length), custom_attributes); return custom_attributes; } public byte[] ReadCustomAttributeBlob(uint signature) { return ReadBlob(signature); } public void ReadCustomAttributeSignature(CustomAttribute attribute) { SignatureReader reader = ReadSignature(attribute.signature); if (!reader.CanReadMore()) return; if (reader.ReadUInt16() != 0x0001) throw new InvalidOperationException(); MethodReference constructor = attribute.Constructor; if (constructor.HasParameters) reader.ReadCustomAttributeConstructorArguments(attribute, constructor.Parameters); if (!reader.CanReadMore()) return; ushort named = reader.ReadUInt16(); if (named == 0) return; reader.ReadCustomAttributeNamedArguments(named, ref attribute.fields, ref attribute.properties); } private void InitializeMarshalInfos() { if (metadata.FieldMarshals != null) return; int length = MoveTo(Table.FieldMarshal); Dictionary marshals = metadata.FieldMarshals = new(length); for (int i = 0; i < length; i++) { MetadataToken token = ReadMetadataToken(CodedIndex.HasFieldMarshal); uint signature = ReadBlobIndex(); if (token.RID == 0) continue; marshals.Add(token, signature); } } public bool HasMarshalInfo(IMarshalInfoProvider owner) { InitializeMarshalInfos(); return metadata.FieldMarshals.ContainsKey(owner.MetadataToken); } public MarshalInfo ReadMarshalInfo(IMarshalInfoProvider owner) { InitializeMarshalInfos(); uint signature; if (!metadata.FieldMarshals.TryGetValue(owner.MetadataToken, out signature)) return null; SignatureReader reader = ReadSignature(signature); metadata.FieldMarshals.Remove(owner.MetadataToken); return reader.ReadMarshalInfo(); } private void InitializeSecurityDeclarations() { if (metadata.SecurityDeclarations != null) return; metadata.SecurityDeclarations = InitializeRanges(Table.DeclSecurity, () => { ReadUInt16(); MetadataToken next = ReadMetadataToken(CodedIndex.HasDeclSecurity); ReadBlobIndex(); return next; }); } public bool HasSecurityDeclarations(ISecurityDeclarationProvider owner) { InitializeSecurityDeclarations(); Range[] ranges; if (!metadata.TryGetSecurityDeclarationRanges(owner, out ranges)) return false; return RangesSize(ranges) > 0; } public Collection ReadSecurityDeclarations(ISecurityDeclarationProvider owner) { InitializeSecurityDeclarations(); Range[] ranges; if (!metadata.TryGetSecurityDeclarationRanges(owner, out ranges)) return new(); Collection security_declarations = new(RangesSize(ranges)); for (int i = 0; i < ranges.Length; i++) ReadSecurityDeclarationRange(ranges[i], security_declarations); metadata.RemoveSecurityDeclarationRange(owner); return security_declarations; } private void ReadSecurityDeclarationRange(Range range, Collection security_declarations) { if (!MoveTo(Table.DeclSecurity, range.Start)) return; for (int i = 0; i < range.Length; i++) { SecurityAction action = (SecurityAction)ReadUInt16(); ReadMetadataToken(CodedIndex.HasDeclSecurity); uint signature = ReadBlobIndex(); security_declarations.Add(new(action, signature, module)); } } public byte[] ReadSecurityDeclarationBlob(uint signature) { return ReadBlob(signature); } public void ReadSecurityDeclarationSignature(SecurityDeclaration declaration) { uint signature = declaration.signature; SignatureReader reader = ReadSignature(signature); if (reader.buffer[reader.position] != '.') { ReadXmlSecurityDeclaration(signature, declaration); return; } reader.position++; uint count = reader.ReadCompressedUInt32(); Collection attributes = new((int)count); for (int i = 0; i < count; i++) attributes.Add(reader.ReadSecurityAttribute()); declaration.security_attributes = attributes; } private void ReadXmlSecurityDeclaration(uint signature, SecurityDeclaration declaration) { Collection attributes = new(1); SecurityAttribute attribute = new(module.TypeSystem.LookupType("System.Security.Permissions", "PermissionSetAttribute")); attribute.properties = new(1); attribute.properties.Add(new("XML", new(module.TypeSystem.String, ReadUnicodeStringBlob(signature)))); attributes.Add(attribute); declaration.security_attributes = attributes; } public Collection ReadExportedTypes() { int length = MoveTo(Table.ExportedType); if (length == 0) return new(); Collection exported_types = new(length); for (int i = 1; i <= length; i++) { TypeAttributes attributes = (TypeAttributes)ReadUInt32(); uint identifier = ReadUInt32(); string name = ReadString(); string @namespace = ReadString(); MetadataToken implementation = ReadMetadataToken(CodedIndex.Implementation); ExportedType declaring_type = null; IMetadataScope scope = null; switch (implementation.TokenType) { case TokenType.AssemblyRef: case TokenType.File: scope = GetExportedTypeScope(implementation); break; case TokenType.ExportedType: // FIXME: if the table is not properly sorted declaring_type = exported_types[(int)implementation.RID - 1]; break; } ExportedType exported_type = new(@namespace, name, module, scope) { Attributes = attributes, Identifier = (int)identifier, DeclaringType = declaring_type }; exported_type.token = new(TokenType.ExportedType, i); exported_types.Add(exported_type); } return exported_types; } private IMetadataScope GetExportedTypeScope(MetadataToken token) { int position = this.position; IMetadataScope scope; switch (token.TokenType) { case TokenType.AssemblyRef: InitializeAssemblyReferences(); scope = metadata.GetAssemblyNameReference(token.RID); break; case TokenType.File: InitializeModuleReferences(); scope = GetModuleReferenceFromFile(token); break; default: throw new NotSupportedException(); } this.position = position; return scope; } private ModuleReference GetModuleReferenceFromFile(MetadataToken token) { if (!MoveTo(Table.File, token.RID)) return null; ReadUInt32(); string file_name = ReadString(); Collection modules = module.ModuleReferences; ModuleReference reference; for (int i = 0; i < modules.Count; i++) { reference = modules[i]; if (reference.Name == file_name) return reference; } reference = new(file_name); modules.Add(reference); return reference; } private void InitializeDocuments() { if (metadata.Documents != null) return; int length = MoveTo(Table.Document); Document[] documents = metadata.Documents = new Document [length]; for (uint i = 1; i <= length; i++) { uint name_index = ReadBlobIndex(); Guid hash_algorithm = ReadGuid(); byte[] hash = ReadBlob(); Guid language = ReadGuid(); SignatureReader signature = ReadSignature(name_index); string name = signature.ReadDocumentName(); documents[i - 1] = new(name) { HashAlgorithmGuid = hash_algorithm, Hash = hash, LanguageGuid = language, token = new(TokenType.Document, i) }; } } public Collection ReadSequencePoints(MethodDefinition method) { InitializeDocuments(); if (!MoveTo(Table.MethodDebugInformation, method.MetadataToken.RID)) return new(0); uint document_index = ReadTableIndex(Table.Document); uint signature = ReadBlobIndex(); if (signature == 0) return new(0); Document document = GetDocument(document_index); SignatureReader reader = ReadSignature(signature); return reader.ReadSequencePoints(document); } public Document GetDocument(uint rid) { Document document = metadata.GetDocument(rid); if (document == null) return null; document.custom_infos = GetCustomDebugInformation(document); return document; } private void InitializeLocalScopes() { if (metadata.LocalScopes != null) return; InitializeMethods(); int length = MoveTo(Table.LocalScope); metadata.LocalScopes = new(); for (uint i = 1; i <= length; i++) { uint method = ReadTableIndex(Table.Method); uint import = ReadTableIndex(Table.ImportScope); Range variables = ReadListRange(i, Table.LocalScope, Table.LocalVariable); Range constants = ReadListRange(i, Table.LocalScope, Table.LocalConstant); uint scope_start = ReadUInt32(); uint scope_length = ReadUInt32(); metadata.SetLocalScopes(method, AddMapping(metadata.LocalScopes, method, new(import, variables, constants, scope_start, scope_length, i))); } } public ScopeDebugInformation ReadScope(MethodDefinition method) { InitializeLocalScopes(); InitializeImportScopes(); Collection> records; if (!metadata.TryGetLocalScopes(method, out records)) return null; ScopeDebugInformation method_scope = null as ScopeDebugInformation; for (int i = 0; i < records.Count; i++) { ScopeDebugInformation scope = ReadLocalScope(records[i]); if (i == 0) { method_scope = scope; continue; } if (!AddScope(method_scope.scopes, scope)) method_scope.Scopes.Add(scope); } return method_scope; } private static bool AddScope(Collection scopes, ScopeDebugInformation scope) { if (scopes.IsNullOrEmpty()) return false; foreach (ScopeDebugInformation sub_scope in scopes) { if (sub_scope.HasScopes && AddScope(sub_scope.Scopes, scope)) return true; if (scope.Start.Offset >= sub_scope.Start.Offset && scope.End.Offset <= sub_scope.End.Offset) { sub_scope.Scopes.Add(scope); return true; } } return false; } private ScopeDebugInformation ReadLocalScope(Row record) { ScopeDebugInformation scope = new() { start = new((int)record.Col4), end = new((int)(record.Col4 + record.Col5)), token = new(TokenType.LocalScope, record.Col6) }; if (record.Col1 > 0) scope.import = metadata.GetImportScope(record.Col1); if (record.Col2.Length > 0) { scope.variables = new((int)record.Col2.Length); for (uint i = 0; i < record.Col2.Length; i++) { VariableDebugInformation variable = ReadLocalVariable(record.Col2.Start + i); if (variable != null) scope.variables.Add(variable); } } if (record.Col3.Length > 0) { scope.constants = new((int)record.Col3.Length); for (uint i = 0; i < record.Col3.Length; i++) { ConstantDebugInformation constant = ReadLocalConstant(record.Col3.Start + i); if (constant != null) scope.constants.Add(constant); } } return scope; } private VariableDebugInformation ReadLocalVariable(uint rid) { if (!MoveTo(Table.LocalVariable, rid)) return null; VariableAttributes attributes = (VariableAttributes)ReadUInt16(); ushort index = ReadUInt16(); string name = ReadString(); VariableDebugInformation variable = new(index, name) { Attributes = attributes, token = new(TokenType.LocalVariable, rid) }; variable.custom_infos = GetCustomDebugInformation(variable); return variable; } private ConstantDebugInformation ReadLocalConstant(uint rid) { if (!MoveTo(Table.LocalConstant, rid)) return null; string name = ReadString(); SignatureReader signature = ReadSignature(ReadBlobIndex()); TypeReference type = signature.ReadTypeSignature(); object value; if (type.etype == ElementType.String) { if (signature.CanReadMore() && signature.buffer[signature.position] != 0xff) { byte[] bytes = signature.ReadBytes((int)(signature.sig_length - (signature.position - signature.start))); value = Encoding.Unicode.GetString(bytes, 0, bytes.Length); } else { value = null; } } else if (type.IsTypeOf("System", "Decimal")) { byte b = signature.ReadByte(); value = new decimal(signature.ReadInt32(), signature.ReadInt32(), signature.ReadInt32(), (b & 0x80) != 0, (byte)(b & 0x7f)); } else if (type.IsTypeOf("System", "DateTime")) { value = new DateTime(signature.ReadInt64()); } else if (type.etype == ElementType.Object || type.etype == ElementType.None || type.etype == ElementType.Class || type.etype == ElementType.Array || type.etype == ElementType.GenericInst) { value = null; } else { value = signature.ReadConstantSignature(type.etype); } ConstantDebugInformation constant = new(name, type, value) { token = new(TokenType.LocalConstant, rid) }; constant.custom_infos = GetCustomDebugInformation(constant); return constant; } private void InitializeImportScopes() { if (metadata.ImportScopes != null) return; int length = MoveTo(Table.ImportScope); metadata.ImportScopes = new ImportDebugInformation [length]; for (int i = 1; i <= length; i++) { ReadTableIndex(Table.ImportScope); ImportDebugInformation import = new(); import.token = new(TokenType.ImportScope, i); SignatureReader signature = ReadSignature(ReadBlobIndex()); while (signature.CanReadMore()) import.Targets.Add(ReadImportTarget(signature)); metadata.ImportScopes[i - 1] = import; } MoveTo(Table.ImportScope); for (int i = 0; i < length; i++) { uint parent = ReadTableIndex(Table.ImportScope); ReadBlobIndex(); if (parent != 0) metadata.ImportScopes[i].Parent = metadata.GetImportScope(parent); } } public string ReadUTF8StringBlob(uint signature) { return ReadStringBlob(signature, Encoding.UTF8); } private string ReadUnicodeStringBlob(uint signature) { return ReadStringBlob(signature, Encoding.Unicode); } private string ReadStringBlob(uint signature, Encoding encoding) { byte[] blob; int index, count; GetBlobView(signature, out blob, out index, out count); if (count == 0) return string.Empty; return encoding.GetString(blob, index, count); } private ImportTarget ReadImportTarget(SignatureReader signature) { AssemblyNameReference reference = null; string @namespace = null; string alias = null; TypeReference type = null; ImportTargetKind kind = (ImportTargetKind)signature.ReadCompressedUInt32(); switch (kind) { case ImportTargetKind.ImportNamespace: @namespace = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); break; case ImportTargetKind.ImportNamespaceInAssembly: reference = metadata.GetAssemblyNameReference(signature.ReadCompressedUInt32()); @namespace = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); break; case ImportTargetKind.ImportType: type = signature.ReadTypeToken(); break; case ImportTargetKind.ImportXmlNamespaceWithAlias: alias = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); @namespace = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); break; case ImportTargetKind.ImportAlias: alias = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); break; case ImportTargetKind.DefineAssemblyAlias: alias = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); reference = metadata.GetAssemblyNameReference(signature.ReadCompressedUInt32()); break; case ImportTargetKind.DefineNamespaceAlias: alias = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); @namespace = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); break; case ImportTargetKind.DefineNamespaceInAssemblyAlias: alias = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); reference = metadata.GetAssemblyNameReference(signature.ReadCompressedUInt32()); @namespace = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); break; case ImportTargetKind.DefineTypeAlias: alias = ReadUTF8StringBlob(signature.ReadCompressedUInt32()); type = signature.ReadTypeToken(); break; } return new(kind) { alias = alias, type = type, @namespace = @namespace, reference = reference }; } private void InitializeStateMachineMethods() { if (metadata.StateMachineMethods != null) return; int length = MoveTo(Table.StateMachineMethod); metadata.StateMachineMethods = new(length); for (int i = 0; i < length; i++) metadata.StateMachineMethods.Add(ReadTableIndex(Table.Method), ReadTableIndex(Table.Method)); } public MethodDefinition ReadStateMachineKickoffMethod(MethodDefinition method) { InitializeStateMachineMethods(); uint rid; if (!metadata.TryGetStateMachineKickOffMethod(method, out rid)) return null; return GetMethodDefinition(rid); } private void InitializeCustomDebugInformations() { if (metadata.CustomDebugInformations != null) return; int length = MoveTo(Table.CustomDebugInformation); metadata.CustomDebugInformations = new(); for (uint i = 1; i <= length; i++) { MetadataToken token = ReadMetadataToken(CodedIndex.HasCustomDebugInformation); Row info = new(ReadGuid(), ReadBlobIndex(), i); Row[] infos; metadata.CustomDebugInformations.TryGetValue(token, out infos); metadata.CustomDebugInformations[token] = infos.Add(info); } } public Collection GetCustomDebugInformation(ICustomDebugInformationProvider provider) { InitializeCustomDebugInformations(); Row[] rows; if (!metadata.CustomDebugInformations.TryGetValue(provider.MetadataToken, out rows)) return null; Collection infos = new(rows.Length); for (int i = 0; i < rows.Length; i++) { if (rows[i].Col1 == StateMachineScopeDebugInformation.KindIdentifier) { SignatureReader signature = ReadSignature(rows[i].Col2); Collection scopes = new(); while (signature.CanReadMore()) { int start = signature.ReadInt32(); int end = start + signature.ReadInt32(); scopes.Add(new(start, end)); } StateMachineScopeDebugInformation state_machine = new(); state_machine.scopes = scopes; infos.Add(state_machine); } else if (rows[i].Col1 == AsyncMethodBodyDebugInformation.KindIdentifier) { SignatureReader signature = ReadSignature(rows[i].Col2); int catch_offset = signature.ReadInt32() - 1; Collection yields = new(); Collection resumes = new(); Collection resume_methods = new(); while (signature.CanReadMore()) { yields.Add(new(signature.ReadInt32())); resumes.Add(new(signature.ReadInt32())); resume_methods.Add(GetMethodDefinition(signature.ReadCompressedUInt32())); } AsyncMethodBodyDebugInformation async_body = new(catch_offset); async_body.yields = yields; async_body.resumes = resumes; async_body.resume_methods = resume_methods; infos.Add(async_body); } else if (rows[i].Col1 == EmbeddedSourceDebugInformation.KindIdentifier) { infos.Add(new EmbeddedSourceDebugInformation(rows[i].Col2, this)); } else if (rows[i].Col1 == SourceLinkDebugInformation.KindIdentifier) { infos.Add(new SourceLinkDebugInformation(Encoding.UTF8.GetString(ReadBlob(rows[i].Col2)))); } else { infos.Add(new BinaryCustomDebugInformation(rows[i].Col1, ReadBlob(rows[i].Col2))); } infos[i].token = new(TokenType.CustomDebugInformation, rows[i].Col3); } return infos; } public byte[] ReadRawEmbeddedSourceDebugInformation(uint index) { SignatureReader signature = ReadSignature(index); return signature.ReadBytes((int)signature.sig_length); } public Row ReadEmbeddedSourceDebugInformation(uint index) { SignatureReader signature = ReadSignature(index); int format = signature.ReadInt32(); uint length = signature.sig_length - 4; if (format == 0) { return new(signature.ReadBytes((int)length), false); } else if (format > 0) { MemoryStream compressed_stream = new(signature.ReadBytes((int)length)); byte[] decompressed_document = new byte [format]; // if positive, format is the decompressed length of the document MemoryStream decompressed_stream = new(decompressed_document); using (DeflateStream deflate_stream = new(compressed_stream, CompressionMode.Decompress, leaveOpen: true)) { deflate_stream.CopyTo(decompressed_stream); } return new(decompressed_document, true); } else { throw new NotSupportedException(); } } } internal sealed class SignatureReader : ByteBuffer { private readonly MetadataReader reader; internal readonly uint start, sig_length; private TypeSystem TypeSystem { get { return reader.module.TypeSystem; } } public SignatureReader(uint blob, MetadataReader reader) : base(reader.image.BlobHeap.data) { this.reader = reader; position = (int)blob; sig_length = ReadCompressedUInt32(); start = (uint)position; } private MetadataToken ReadTypeTokenSignature() { return CodedIndex.TypeDefOrRef.GetMetadataToken(ReadCompressedUInt32()); } private GenericParameter GetGenericParameter(GenericParameterType type, uint var) { IGenericContext context = reader.context; int index = (int)var; if (context == null) return GetUnboundGenericParameter(type, index); IGenericParameterProvider provider; switch (type) { case GenericParameterType.Type: provider = context.Type; break; case GenericParameterType.Method: provider = context.Method; break; default: throw new NotSupportedException(); } if (!context.IsDefinition) CheckGenericContext(provider, index); if (index >= provider.GenericParameters.Count) return GetUnboundGenericParameter(type, index); return provider.GenericParameters[index]; } private GenericParameter GetUnboundGenericParameter(GenericParameterType type, int index) { return new(index, type, reader.module); } private static void CheckGenericContext(IGenericParameterProvider owner, int index) { Collection owner_parameters = owner.GenericParameters; for (int i = owner_parameters.Count; i <= index; i++) owner_parameters.Add(new(owner)); } public void ReadGenericInstanceSignature(IGenericParameterProvider provider, IGenericInstance instance, uint arity) { if (!provider.IsDefinition) CheckGenericContext(provider, (int)arity - 1); Collection instance_arguments = instance.GenericArguments; for (int i = 0; i < arity; i++) instance_arguments.Add(ReadTypeSignature()); } private ArrayType ReadArrayTypeSignature() { ArrayType array = new(ReadTypeSignature()); uint rank = ReadCompressedUInt32(); uint[] sizes = new uint [ReadCompressedUInt32()]; for (int i = 0; i < sizes.Length; i++) sizes[i] = ReadCompressedUInt32(); int[] low_bounds = new int [ReadCompressedUInt32()]; for (int i = 0; i < low_bounds.Length; i++) low_bounds[i] = ReadCompressedInt32(); array.Dimensions.Clear(); for (int i = 0; i < rank; i++) { int? lower = null, upper = null; if (i < low_bounds.Length) lower = low_bounds[i]; if (i < sizes.Length) upper = lower + (int)sizes[i] - 1; array.Dimensions.Add(new(lower, upper)); } return array; } private TypeReference GetTypeDefOrRef(MetadataToken token) { return reader.GetTypeDefOrRef(token); } public TypeReference ReadTypeSignature() { return ReadTypeSignature((ElementType)ReadByte()); } public TypeReference ReadTypeToken() { return GetTypeDefOrRef(ReadTypeTokenSignature()); } private TypeReference ReadTypeSignature(ElementType etype) { switch (etype) { case ElementType.ValueType: { TypeReference value_type = GetTypeDefOrRef(ReadTypeTokenSignature()); value_type.KnownValueType(); return value_type; } case ElementType.Class: return GetTypeDefOrRef(ReadTypeTokenSignature()); case ElementType.Ptr: return new PointerType(ReadTypeSignature()); case ElementType.FnPtr: { FunctionPointerType fptr = new(); ReadMethodSignature(fptr); return fptr; } case ElementType.ByRef: return new ByReferenceType(ReadTypeSignature()); case ElementType.Pinned: return new PinnedType(ReadTypeSignature()); case ElementType.SzArray: return new ArrayType(ReadTypeSignature()); case ElementType.Array: return ReadArrayTypeSignature(); case ElementType.CModOpt: return new OptionalModifierType(GetTypeDefOrRef(ReadTypeTokenSignature()), ReadTypeSignature()); case ElementType.CModReqD: return new RequiredModifierType(GetTypeDefOrRef(ReadTypeTokenSignature()), ReadTypeSignature()); case ElementType.Sentinel: return new SentinelType(ReadTypeSignature()); case ElementType.Var: return GetGenericParameter(GenericParameterType.Type, ReadCompressedUInt32()); case ElementType.MVar: return GetGenericParameter(GenericParameterType.Method, ReadCompressedUInt32()); case ElementType.GenericInst: { bool is_value_type = ReadByte() == (byte)ElementType.ValueType; TypeReference element_type = GetTypeDefOrRef(ReadTypeTokenSignature()); uint arity = ReadCompressedUInt32(); GenericInstanceType generic_instance = new(element_type, (int)arity); ReadGenericInstanceSignature(element_type, generic_instance, arity); if (is_value_type) { generic_instance.KnownValueType(); element_type.GetElementType().KnownValueType(); } return generic_instance; } case ElementType.Object: return TypeSystem.Object; case ElementType.Void: return TypeSystem.Void; case ElementType.TypedByRef: return TypeSystem.TypedReference; case ElementType.I: return TypeSystem.IntPtr; case ElementType.U: return TypeSystem.UIntPtr; default: return GetPrimitiveType(etype); } } public void ReadMethodSignature(IMethodSignature method) { byte calling_convention = ReadByte(); const byte has_this = 0x20; const byte explicit_this = 0x40; if ((calling_convention & has_this) != 0) { method.HasThis = true; calling_convention = (byte)(calling_convention & ~has_this); } if ((calling_convention & explicit_this) != 0) { method.ExplicitThis = true; calling_convention = (byte)(calling_convention & ~explicit_this); } method.CallingConvention = (MethodCallingConvention)calling_convention; MethodReference generic_context = method as MethodReference; if (generic_context != null && !generic_context.DeclaringType.IsArray) reader.context = generic_context; if ((calling_convention & 0x10) != 0) { uint arity = ReadCompressedUInt32(); if (generic_context != null && !generic_context.IsDefinition) CheckGenericContext(generic_context, (int)arity - 1); } uint param_count = ReadCompressedUInt32(); method.MethodReturnType.ReturnType = ReadTypeSignature(); if (param_count == 0) return; Collection parameters; MethodReference method_ref = method as MethodReference; if (method_ref != null) parameters = method_ref.parameters = new(method, (int)param_count); else parameters = method.Parameters; for (int i = 0; i < param_count; i++) parameters.Add(new(ReadTypeSignature())); } public object ReadConstantSignature(ElementType type) { return ReadPrimitiveValue(type); } public void ReadCustomAttributeConstructorArguments(CustomAttribute attribute, Collection parameters) { int count = parameters.Count; if (count == 0) return; attribute.arguments = new(count); for (int i = 0; i < count; i++) attribute.arguments.Add(ReadCustomAttributeFixedArgument(parameters[i].ParameterType)); } private CustomAttributeArgument ReadCustomAttributeFixedArgument(TypeReference type) { if (type.IsArray) return ReadCustomAttributeFixedArrayArgument((ArrayType)type); return ReadCustomAttributeElement(type); } public void ReadCustomAttributeNamedArguments(ushort count, ref Collection fields, ref Collection properties) { for (int i = 0; i < count; i++) { if (!CanReadMore()) return; ReadCustomAttributeNamedArgument(ref fields, ref properties); } } private void ReadCustomAttributeNamedArgument(ref Collection fields, ref Collection properties) { byte kind = ReadByte(); TypeReference type = ReadCustomAttributeFieldOrPropType(); string name = ReadUTF8String(); Collection container; switch (kind) { case 0x53: container = GetCustomAttributeNamedArgumentCollection(ref fields); break; case 0x54: container = GetCustomAttributeNamedArgumentCollection(ref properties); break; default: throw new NotSupportedException(); } container.Add(new(name, ReadCustomAttributeFixedArgument(type))); } private static Collection GetCustomAttributeNamedArgumentCollection(ref Collection collection) { if (collection != null) return collection; return collection = new(); } private CustomAttributeArgument ReadCustomAttributeFixedArrayArgument(ArrayType type) { uint length = ReadUInt32(); if (length == 0xffffffff) return new(type, null); if (length == 0) return new(type, Empty.Array); CustomAttributeArgument[] arguments = new CustomAttributeArgument [length]; TypeReference element_type = type.ElementType; for (int i = 0; i < length; i++) arguments[i] = ReadCustomAttributeElement(element_type); return new(type, arguments); } private CustomAttributeArgument ReadCustomAttributeElement(TypeReference type) { if (type.IsArray) return ReadCustomAttributeFixedArrayArgument((ArrayType)type); return new(type, type.etype == ElementType.Object ? ReadCustomAttributeElement(ReadCustomAttributeFieldOrPropType()) : ReadCustomAttributeElementValue(type)); } private object ReadCustomAttributeElementValue(TypeReference type) { ElementType etype = type.etype; switch (etype) { case ElementType.String: return ReadUTF8String(); case ElementType.None: if (type.IsTypeOf("System", "Type")) return ReadTypeReference(); return ReadCustomAttributeEnum(type); default: return ReadPrimitiveValue(etype); } } private object ReadPrimitiveValue(ElementType type) { switch (type) { case ElementType.Boolean: return ReadByte() == 1; case ElementType.I1: return (sbyte)ReadByte(); case ElementType.U1: return ReadByte(); case ElementType.Char: return (char)ReadUInt16(); case ElementType.I2: return ReadInt16(); case ElementType.U2: return ReadUInt16(); case ElementType.I4: return ReadInt32(); case ElementType.U4: return ReadUInt32(); case ElementType.I8: return ReadInt64(); case ElementType.U8: return ReadUInt64(); case ElementType.R4: return ReadSingle(); case ElementType.R8: return ReadDouble(); default: throw new NotImplementedException(type.ToString()); } } private TypeReference GetPrimitiveType(ElementType etype) { switch (etype) { case ElementType.Boolean: return TypeSystem.Boolean; case ElementType.Char: return TypeSystem.Char; case ElementType.I1: return TypeSystem.SByte; case ElementType.U1: return TypeSystem.Byte; case ElementType.I2: return TypeSystem.Int16; case ElementType.U2: return TypeSystem.UInt16; case ElementType.I4: return TypeSystem.Int32; case ElementType.U4: return TypeSystem.UInt32; case ElementType.I8: return TypeSystem.Int64; case ElementType.U8: return TypeSystem.UInt64; case ElementType.R4: return TypeSystem.Single; case ElementType.R8: return TypeSystem.Double; case ElementType.String: return TypeSystem.String; default: throw new NotImplementedException(etype.ToString()); } } private TypeReference ReadCustomAttributeFieldOrPropType() { ElementType etype = (ElementType)ReadByte(); switch (etype) { case ElementType.Boxed: return TypeSystem.Object; case ElementType.SzArray: return new ArrayType(ReadCustomAttributeFieldOrPropType()); case ElementType.Enum: return ReadTypeReference(); case ElementType.Type: return TypeSystem.LookupType("System", "Type"); default: return GetPrimitiveType(etype); } } public TypeReference ReadTypeReference() { return TypeParser.ParseType(reader.module, ReadUTF8String()); } private object ReadCustomAttributeEnum(TypeReference enum_type) { TypeDefinition type = enum_type.CheckedResolve(); if (!type.IsEnum) throw new ArgumentException(); return ReadCustomAttributeElementValue(type.GetEnumUnderlyingType()); } public SecurityAttribute ReadSecurityAttribute() { SecurityAttribute attribute = new(ReadTypeReference()); ReadCompressedUInt32(); ReadCustomAttributeNamedArguments((ushort)ReadCompressedUInt32(), ref attribute.fields, ref attribute.properties); return attribute; } public MarshalInfo ReadMarshalInfo() { NativeType native = ReadNativeType(); switch (native) { case NativeType.Array: { ArrayMarshalInfo array = new(); if (CanReadMore()) array.element_type = ReadNativeType(); if (CanReadMore()) array.size_parameter_index = (int)ReadCompressedUInt32(); if (CanReadMore()) array.size = (int)ReadCompressedUInt32(); if (CanReadMore()) array.size_parameter_multiplier = (int)ReadCompressedUInt32(); return array; } case NativeType.SafeArray: { SafeArrayMarshalInfo array = new(); if (CanReadMore()) array.element_type = ReadVariantType(); return array; } case NativeType.FixedArray: { FixedArrayMarshalInfo array = new(); if (CanReadMore()) array.size = (int)ReadCompressedUInt32(); if (CanReadMore()) array.element_type = ReadNativeType(); return array; } case NativeType.FixedSysString: { FixedSysStringMarshalInfo sys_string = new(); if (CanReadMore()) sys_string.size = (int)ReadCompressedUInt32(); return sys_string; } case NativeType.CustomMarshaler: { CustomMarshalInfo marshaler = new(); string guid_value = ReadUTF8String(); marshaler.guid = !string.IsNullOrEmpty(guid_value) ? new(guid_value) : Guid.Empty; marshaler.unmanaged_type = ReadUTF8String(); marshaler.managed_type = ReadTypeReference(); marshaler.cookie = ReadUTF8String(); return marshaler; } default: return new(native); } } private NativeType ReadNativeType() { return (NativeType)ReadByte(); } private VariantType ReadVariantType() { return (VariantType)ReadByte(); } private string ReadUTF8String() { if (buffer[position] == 0xff) { position++; return null; } int length = (int)ReadCompressedUInt32(); if (length == 0) return string.Empty; if (position + length > buffer.Length) return string.Empty; string @string = Encoding.UTF8.GetString(buffer, position, length); position += length; return @string; } public string ReadDocumentName() { char separator = (char)buffer[position]; position++; StringBuilder builder = new(); for (int i = 0; CanReadMore(); i++) { if (i > 0 && separator != 0) builder.Append(separator); uint part = ReadCompressedUInt32(); if (part != 0) builder.Append(reader.ReadUTF8StringBlob(part)); } return builder.ToString(); } public Collection ReadSequencePoints(Document document) { ReadCompressedUInt32(); // local_sig_token if (document == null) document = reader.GetDocument(ReadCompressedUInt32()); int offset = 0; int start_line = 0; int start_column = 0; bool first_non_hidden = true; // there's about 5 compressed int32's per sequenec points. we don't know exactly how many // but let's take a conservative guess so we dont end up reallocating the sequence_points collection // as it grows. long bytes_remaining_for_sequencepoints = sig_length - (position - start); int estimated_sequencepoint_amount = (int)bytes_remaining_for_sequencepoints / 5; Collection sequence_points = new(estimated_sequencepoint_amount); for (int i = 0; CanReadMore(); i++) { int delta_il = (int)ReadCompressedUInt32(); if (i > 0 && delta_il == 0) { document = reader.GetDocument(ReadCompressedUInt32()); continue; } offset += delta_il; int delta_lines = (int)ReadCompressedUInt32(); int delta_columns = delta_lines == 0 ? (int)ReadCompressedUInt32() : ReadCompressedInt32(); if (delta_lines == 0 && delta_columns == 0) { sequence_points.Add(new(offset, document) { StartLine = 0xfeefee, EndLine = 0xfeefee, StartColumn = 0, EndColumn = 0 }); continue; } if (first_non_hidden) { start_line = (int)ReadCompressedUInt32(); start_column = (int)ReadCompressedUInt32(); } else { start_line += ReadCompressedInt32(); start_column += ReadCompressedInt32(); } sequence_points.Add(new(offset, document) { StartLine = start_line, StartColumn = start_column, EndLine = start_line + delta_lines, EndColumn = start_column + delta_columns }); first_non_hidden = false; } return sequence_points; } public bool CanReadMore() { return position - start < sig_length; } } }