diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index af30932cb..ccd715010 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -269,6 +269,7 @@ + @@ -295,6 +296,7 @@ + diff --git a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs new file mode 100644 index 000000000..173c4a9fe --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs @@ -0,0 +1,198 @@ +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Security.Cryptography; +using System.Text; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.Metadata +{ + public sealed class AssemblyResolutionException : FileNotFoundException + { + public IAssemblyReference Reference { get; } + + public AssemblyResolutionException(IAssemblyReference reference) + : this(reference, null) + { + } + + public AssemblyResolutionException(IAssemblyReference reference, Exception innerException) + : base($"Failed to resolve assembly: '{reference}'", innerException) + { + this.Reference = reference; + } + } + + public interface IAssemblyResolver + { + PEFile Resolve(IAssemblyReference reference); + } + + public interface IAssemblyReference + { + string Name { get; } + string FullName { get; } + Version Version { get; } + string Culture { get; } + byte[] PublicKeyToken { get; } + + bool IsWindowsRuntime { get; } + bool IsRetargetable { get; } + } + + public class AssemblyNameReference : IAssemblyReference + { + string fullName; + + public string Name { get; private set; } + + public string FullName { + get { + if (fullName != null) + return fullName; + + const string sep = ", "; + + var builder = new StringBuilder(); + builder.Append(Name); + builder.Append(sep); + builder.Append("Version="); + builder.Append(Version.ToString(fieldCount: 4)); + builder.Append(sep); + builder.Append("Culture="); + builder.Append(string.IsNullOrEmpty(Culture) ? "neutral" : Culture); + builder.Append(sep); + builder.Append("PublicKeyToken="); + + var pk_token = PublicKeyToken; + if (pk_token != null && pk_token.Length > 0) { + for (int i = 0; i < pk_token.Length; i++) { + builder.Append(pk_token[i].ToString("x2")); + } + } else + builder.Append("null"); + + if (IsRetargetable) { + builder.Append(sep); + builder.Append("Retargetable=Yes"); + } + + return fullName = builder.ToString(); + } + } + + public Version Version { get; private set; } + + public string Culture { get; private set; } + + public byte[] PublicKeyToken { get; private set; } + + public bool IsWindowsRuntime { get; private set; } + + public bool IsRetargetable { get; private set; } + + public static AssemblyNameReference Parse(string fullName) + { + if (fullName == null) + throw new ArgumentNullException("fullName"); + if (fullName.Length == 0) + throw new ArgumentException("Name can not be empty"); + + var name = new AssemblyNameReference(); + var tokens = fullName.Split(','); + for (int i = 0; i < tokens.Length; i++) { + var token = tokens[i].Trim(); + + if (i == 0) { + name.Name = token; + continue; + } + + var parts = token.Split('='); + if (parts.Length != 2) + throw new ArgumentException("Malformed name"); + + switch (parts[0].ToLowerInvariant()) { + case "version": + name.Version = new Version(parts[1]); + break; + case "culture": + name.Culture = parts[1] == "neutral" ? "" : parts[1]; + break; + case "publickeytoken": + var pk_token = parts[1]; + if (pk_token == "null") + break; + + name.PublicKeyToken = new byte[pk_token.Length / 2]; + for (int j = 0; j < name.PublicKeyToken.Length; j++) + name.PublicKeyToken[j] = Byte.Parse(pk_token.Substring(j * 2, 2), System.Globalization.NumberStyles.HexNumber); + + break; + } + } + + return name; + } + } + + public class AssemblyReference : IAssemblyReference + { + static readonly SHA1 sha1 = SHA1.Create(); + + public PEFile Module { get; } + public AssemblyReferenceHandle Handle { get; } + + System.Reflection.Metadata.AssemblyReference This() => Module.Metadata.GetAssemblyReference(Handle); + + public bool IsWindowsRuntime => (This().Flags & AssemblyFlags.WindowsRuntime) != 0; + public bool IsRetargetable => (This().Flags & AssemblyFlags.Retargetable) != 0; + + public string Name => Module.Metadata.GetString(This().Name); + public string FullName => This().GetFullAssemblyName(Module.Metadata); + public Version Version => This().Version; + public string Culture => Module.Metadata.GetString(This().Culture); + byte[] IAssemblyReference.PublicKeyToken => GetPublicKeyToken(); + + public byte[] GetPublicKeyToken() + { + var inst = This(); + if (inst.PublicKeyOrToken.IsNil) + return Empty.Array; + var bytes = Module.Metadata.GetBlobBytes(inst.PublicKeyOrToken); + if ((inst.Flags & AssemblyFlags.PublicKey) != 0) { + return sha1.ComputeHash(bytes).Skip(12).ToArray(); + } + return bytes; + } + + public AssemblyReference(PEFile module, AssemblyReferenceHandle handle) + { + Module = module ?? throw new ArgumentNullException(nameof(module)); + if (handle.IsNil) + throw new ArgumentNullException(nameof(handle)); + Handle = handle; + } + } +} diff --git a/ICSharpCode.Decompiler/Metadata/Dom.cs b/ICSharpCode.Decompiler/Metadata/Dom.cs index dddb74855..ec9dd13a3 100644 --- a/ICSharpCode.Decompiler/Metadata/Dom.cs +++ b/ICSharpCode.Decompiler/Metadata/Dom.cs @@ -26,38 +26,7 @@ namespace ICSharpCode.Decompiler.Metadata using SRMTypeSpec = System.Reflection.Metadata.TypeSpecification; using SRMAssemblyReference = System.Reflection.Metadata.AssemblyReference; - public sealed class AssemblyResolutionException : FileNotFoundException - { - public IAssemblyReference Reference { get; } - - public AssemblyResolutionException(IAssemblyReference reference) - : this(reference, null) - { - } - - public AssemblyResolutionException(IAssemblyReference reference, Exception innerException) - : base($"Failed to resolve assembly: '{reference}'", innerException) - { - this.Reference = reference; - } - } - public interface IAssemblyResolver - { - PEFile Resolve(IAssemblyReference reference); - } - - public interface IAssemblyReference - { - string Name { get; } - string FullName { get; } - Version Version { get; } - string Culture { get; } - byte[] PublicKeyToken { get; } - - bool IsWindowsRuntime { get; } - bool IsRetargetable { get; } - } public interface IAssemblyDocumentationResolver { @@ -92,170 +61,6 @@ namespace ICSharpCode.Decompiler.Metadata Net_4_0 } - public class PEFile : IDisposable, TypeSystem.IAssemblyReference - { - public string FileName { get; } - public PEReader Reader { get; } - public MetadataReader Metadata { get; } - public IAssemblyResolver AssemblyResolver { get; } - public IAssemblyDocumentationResolver DocumentationResolver { get; set; } - public IDebugInfoProvider DebugInfo { get; set; } - - public PEFile(string fileName, Stream stream, bool throwOnResolveError = false, PEStreamOptions options = PEStreamOptions.Default) - { - this.FileName = fileName; - this.Reader = new PEReader(stream, options); - this.Metadata = Reader.GetMetadataReader(); - this.AssemblyResolver = new UniversalAssemblyResolver(fileName, throwOnResolveError, Reader.DetectTargetFrameworkId(), options); - } - - public PEFile(string fileName, Stream stream, IAssemblyResolver assemblyResolver, PEStreamOptions options = PEStreamOptions.Default) - { - this.FileName = fileName; - this.Reader = new PEReader(stream, options); - this.Metadata = Reader.GetMetadataReader(); - this.AssemblyResolver = assemblyResolver; - } - - public bool IsAssembly => Metadata.IsAssembly; - public string Name => GetName(); - public string FullName => IsAssembly ? Metadata.GetFullAssemblyName() : Name; - - public TargetRuntime GetRuntime() - { - string version = Metadata.MetadataVersion; - switch (version[1]) { - case '1': - if (version[3] == 1) - return TargetRuntime.Net_1_0; - else - return TargetRuntime.Net_1_1; - case '2': - return TargetRuntime.Net_2_0; - case '4': - return TargetRuntime.Net_4_0; - default: - return TargetRuntime.Unknown; - } - - } - - string GetName() - { - var metadata = Metadata; - if (metadata.IsAssembly) - return metadata.GetString(metadata.GetAssemblyDefinition().Name); - return metadata.GetString(metadata.GetModuleDefinition().Name); - } - - public ImmutableArray AssemblyReferences => Metadata.AssemblyReferences.Select(r => new AssemblyReference(this, r)).ToImmutableArray(); - public ImmutableArray ModuleReferences => Metadata.GetModuleReferences().ToImmutableArray(); - public ImmutableArray TopLevelTypeDefinitions => Metadata.GetTopLevelTypeDefinitions().Select(t => new TypeDefinition(this, t)).ToImmutableArray(); - public ImmutableArray Resources => GetResources().ToImmutableArray(); - - IEnumerable GetResources() - { - var metadata = Metadata; - foreach (var h in metadata.ManifestResources) { - yield return new Resource(this, h); - } - } - - public void Dispose() - { - Reader.Dispose(); - } - - Dictionary typeLookup; - - /// - /// Finds the top-level-type with the specified name. - /// - public TypeDefinitionHandle GetTypeDefinition(TopLevelTypeName typeName) - { - var lookup = LazyInit.VolatileRead(ref typeLookup); - if (lookup == null) { - lookup = new Dictionary(); - foreach (var handle in Metadata.TypeDefinitions) { - var td = Metadata.GetTypeDefinition(handle); - if (!td.GetDeclaringType().IsNil) { - continue; // nested type - } - var nsHandle = td.Namespace; - string ns = nsHandle.IsNil ? string.Empty : Metadata.GetString(nsHandle); - string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(Metadata.GetString(td.Name), out int typeParameterCount); - lookup[new TopLevelTypeName(ns, name, typeParameterCount)] = handle; - } - lookup = LazyInit.GetOrSet(ref typeLookup, lookup); - } - if (lookup.TryGetValue(typeName, out var resultHandle)) - return resultHandle; - else - return default; - } - - Dictionary typeForwarderLookup; - - /// - /// Finds the type forwarder with the specified name. - /// - public ExportedTypeHandle GetTypeForwarder(FullTypeName typeName) - { - var lookup = LazyInit.VolatileRead(ref typeForwarderLookup); - if (lookup == null) { - lookup = new Dictionary(); - foreach (var handle in Metadata.ExportedTypes) { - var td = Metadata.GetExportedType(handle); - lookup[td.GetFullTypeName(Metadata)] = handle; - } - lookup = LazyInit.GetOrSet(ref typeForwarderLookup, lookup); - } - if (lookup.TryGetValue(typeName, out var resultHandle)) - return resultHandle; - else - return default; - } - - MethodSemanticsLookup methodSemanticsLookup; - - internal MethodSemanticsLookup MethodSemanticsLookup { - get { - var r = LazyInit.VolatileRead(ref methodSemanticsLookup); - if (r != null) - return r; - else - return LazyInit.GetOrSet(ref methodSemanticsLookup, new MethodSemanticsLookup(Metadata)); - } - } - - public TypeSystem.IAssemblyReference WithOptions(TypeSystemOptions options) - { - return new PEFileWithOptions(this, options); - } - - IAssembly TypeSystem.IAssemblyReference.Resolve(ITypeResolveContext context) - { - return new MetadataAssembly(context.Compilation, this, TypeSystemOptions.Default); - } - - private class PEFileWithOptions : TypeSystem.IAssemblyReference - { - readonly PEFile peFile; - readonly TypeSystemOptions options; - - public PEFileWithOptions(PEFile peFile, TypeSystemOptions options) - { - this.peFile = peFile; - this.options = options; - } - - IAssembly TypeSystem.IAssemblyReference.Resolve(ITypeResolveContext context) - { - return new MetadataAssembly(context.Compilation, peFile, options); - } - } - } - public enum ResourceType { Linked, @@ -331,160 +136,6 @@ namespace ICSharpCode.Decompiler.Metadata } } - public class AssemblyNameReference : IAssemblyReference - { - string fullName; - - public string Name { get; private set; } - - public string FullName { - get { - if (fullName != null) - return fullName; - - const string sep = ", "; - - var builder = new StringBuilder(); - builder.Append(Name); - builder.Append(sep); - builder.Append("Version="); - builder.Append(Version.ToString(fieldCount: 4)); - builder.Append(sep); - builder.Append("Culture="); - builder.Append(string.IsNullOrEmpty(Culture) ? "neutral" : Culture); - builder.Append(sep); - builder.Append("PublicKeyToken="); - - var pk_token = PublicKeyToken; - if (pk_token != null && pk_token.Length > 0) { - for (int i = 0; i < pk_token.Length; i++) { - builder.Append(pk_token[i].ToString("x2")); - } - } else - builder.Append("null"); - - if (IsRetargetable) { - builder.Append(sep); - builder.Append("Retargetable=Yes"); - } - - return fullName = builder.ToString(); - } - } - - public Version Version { get; private set; } - - public string Culture { get; private set; } - - public byte[] PublicKeyToken { get; private set; } - - public bool IsWindowsRuntime { get; private set; } - - public bool IsRetargetable { get; private set; } - - public static AssemblyNameReference Parse(string fullName) - { - if (fullName == null) - throw new ArgumentNullException("fullName"); - if (fullName.Length == 0) - throw new ArgumentException("Name can not be empty"); - - var name = new AssemblyNameReference(); - var tokens = fullName.Split(','); - for (int i = 0; i < tokens.Length; i++) { - var token = tokens[i].Trim(); - - if (i == 0) { - name.Name = token; - continue; - } - - var parts = token.Split('='); - if (parts.Length != 2) - throw new ArgumentException("Malformed name"); - - switch (parts[0].ToLowerInvariant()) { - case "version": - name.Version = new Version(parts[1]); - break; - case "culture": - name.Culture = parts[1] == "neutral" ? "" : parts[1]; - break; - case "publickeytoken": - var pk_token = parts[1]; - if (pk_token == "null") - break; - - name.PublicKeyToken = new byte[pk_token.Length / 2]; - for (int j = 0; j < name.PublicKeyToken.Length; j++) - name.PublicKeyToken[j] = Byte.Parse(pk_token.Substring(j * 2, 2), System.Globalization.NumberStyles.HexNumber); - - break; - } - } - - return name; - } - } - - public struct AssemblyReference : IAssemblyReference, IEquatable - { - static readonly SHA1 sha1 = SHA1.Create(); - - public PEFile Module { get; } - public AssemblyReferenceHandle Handle { get; } - public bool IsNil => Handle.IsNil; - - SRMAssemblyReference This() => Module.Metadata.GetAssemblyReference(Handle); - - public bool IsWindowsRuntime => (This().Flags & AssemblyFlags.WindowsRuntime) != 0; - public bool IsRetargetable => (This().Flags & AssemblyFlags.Retargetable) != 0; - - public string Name => Module.Metadata.GetString(This().Name); - public string FullName => This().GetFullAssemblyName(Module.Metadata); - public Version Version => This().Version; - public string Culture => Module.Metadata.GetString(This().Culture); - byte[] IAssemblyReference.PublicKeyToken => GetPublicKeyToken(); - - public byte[] GetPublicKeyToken() - { - var inst = This(); - if (inst.PublicKeyOrToken.IsNil) - return Empty.Array; - var bytes = Module.Metadata.GetBlobBytes(inst.PublicKeyOrToken); - if ((inst.Flags & AssemblyFlags.PublicKey) != 0) { - return sha1.ComputeHash(bytes).Skip(12).ToArray(); - } - return bytes; - } - - public bool Equals(AssemblyReference other) - { - return Module == other.Module && Handle == other.Handle; - } - - public override bool Equals(object obj) - { - if (obj is AssemblyReference reference) - return Equals(reference); - return false; - } - - public override int GetHashCode() - { - return unchecked(982451629 * Module.GetHashCode() + 982451653 * MetadataTokens.GetToken(Handle)); - } - - public static bool operator ==(AssemblyReference lhs, AssemblyReference rhs) => lhs.Equals(rhs); - public static bool operator !=(AssemblyReference lhs, AssemblyReference rhs) => !lhs.Equals(rhs); - - public AssemblyReference(PEFile module, AssemblyReferenceHandle handle) - { - Module = module; - Handle = handle; - } - } - public struct Entity : IEquatable, IMetadataEntity { public PEFile Module { get; } diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs index 36ef08128..77b1f9f54 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs index 72f82efe8..40da69cc1 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; diff --git a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs index f4ec65fce..1b2c07061 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs @@ -18,11 +18,6 @@ namespace ICSharpCode.Decompiler.Metadata { public static class MetadataExtensions { - public static bool IsNil(this IAssemblyReference reference) - { - return reference == null || (reference is Metadata.AssemblyReference ar && ar.IsNil); - } - public static string GetFullAssemblyName(this MetadataReader reader) { if (!reader.IsAssembly) diff --git a/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs b/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs index b842f640e..aa6f9c2cf 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; diff --git a/ICSharpCode.Decompiler/Metadata/PEFile.cs b/ICSharpCode.Decompiler/Metadata/PEFile.cs new file mode 100644 index 000000000..1d89260cb --- /dev/null +++ b/ICSharpCode.Decompiler/Metadata/PEFile.cs @@ -0,0 +1,194 @@ +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.Metadata +{ + public class PEFile : IDisposable, TypeSystem.IAssemblyReference + { + public string FileName { get; } + public PEReader Reader { get; } + public MetadataReader Metadata { get; } + public IAssemblyResolver AssemblyResolver { get; } + public IAssemblyDocumentationResolver DocumentationResolver { get; set; } + public IDebugInfoProvider DebugInfo { get; set; } + + public PEFile(string fileName, Stream stream, bool throwOnResolveError = false, PEStreamOptions options = PEStreamOptions.Default) + { + this.FileName = fileName; + this.Reader = new PEReader(stream, options); + this.Metadata = Reader.GetMetadataReader(); + this.AssemblyResolver = new UniversalAssemblyResolver(fileName, throwOnResolveError, Reader.DetectTargetFrameworkId(), options); + } + + public PEFile(string fileName, Stream stream, IAssemblyResolver assemblyResolver, PEStreamOptions options = PEStreamOptions.Default) + { + this.FileName = fileName; + this.Reader = new PEReader(stream, options); + this.Metadata = Reader.GetMetadataReader(); + this.AssemblyResolver = assemblyResolver; + } + + public bool IsAssembly => Metadata.IsAssembly; + public string Name => GetName(); + public string FullName => IsAssembly ? Metadata.GetFullAssemblyName() : Name; + + public TargetRuntime GetRuntime() + { + string version = Metadata.MetadataVersion; + switch (version[1]) { + case '1': + if (version[3] == 1) + return TargetRuntime.Net_1_0; + else + return TargetRuntime.Net_1_1; + case '2': + return TargetRuntime.Net_2_0; + case '4': + return TargetRuntime.Net_4_0; + default: + return TargetRuntime.Unknown; + } + + } + + string GetName() + { + var metadata = Metadata; + if (metadata.IsAssembly) + return metadata.GetString(metadata.GetAssemblyDefinition().Name); + return metadata.GetString(metadata.GetModuleDefinition().Name); + } + + public ImmutableArray AssemblyReferences => Metadata.AssemblyReferences.Select(r => new AssemblyReference(this, r)).ToImmutableArray(); + public ImmutableArray ModuleReferences => Metadata.GetModuleReferences().ToImmutableArray(); + public ImmutableArray TopLevelTypeDefinitions => Metadata.GetTopLevelTypeDefinitions().Select(t => new TypeDefinition(this, t)).ToImmutableArray(); + public ImmutableArray Resources => GetResources().ToImmutableArray(); + + IEnumerable GetResources() + { + var metadata = Metadata; + foreach (var h in metadata.ManifestResources) { + yield return new Resource(this, h); + } + } + + public void Dispose() + { + Reader.Dispose(); + } + + Dictionary typeLookup; + + /// + /// Finds the top-level-type with the specified name. + /// + public TypeDefinitionHandle GetTypeDefinition(TopLevelTypeName typeName) + { + var lookup = LazyInit.VolatileRead(ref typeLookup); + if (lookup == null) { + lookup = new Dictionary(); + foreach (var handle in Metadata.TypeDefinitions) { + var td = Metadata.GetTypeDefinition(handle); + if (!td.GetDeclaringType().IsNil) { + continue; // nested type + } + var nsHandle = td.Namespace; + string ns = nsHandle.IsNil ? string.Empty : Metadata.GetString(nsHandle); + string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(Metadata.GetString(td.Name), out int typeParameterCount); + lookup[new TopLevelTypeName(ns, name, typeParameterCount)] = handle; + } + lookup = LazyInit.GetOrSet(ref typeLookup, lookup); + } + if (lookup.TryGetValue(typeName, out var resultHandle)) + return resultHandle; + else + return default; + } + + Dictionary typeForwarderLookup; + + /// + /// Finds the type forwarder with the specified name. + /// + public ExportedTypeHandle GetTypeForwarder(FullTypeName typeName) + { + var lookup = LazyInit.VolatileRead(ref typeForwarderLookup); + if (lookup == null) { + lookup = new Dictionary(); + foreach (var handle in Metadata.ExportedTypes) { + var td = Metadata.GetExportedType(handle); + lookup[td.GetFullTypeName(Metadata)] = handle; + } + lookup = LazyInit.GetOrSet(ref typeForwarderLookup, lookup); + } + if (lookup.TryGetValue(typeName, out var resultHandle)) + return resultHandle; + else + return default; + } + + MethodSemanticsLookup methodSemanticsLookup; + + internal MethodSemanticsLookup MethodSemanticsLookup { + get { + var r = LazyInit.VolatileRead(ref methodSemanticsLookup); + if (r != null) + return r; + else + return LazyInit.GetOrSet(ref methodSemanticsLookup, new MethodSemanticsLookup(Metadata)); + } + } + + public TypeSystem.IAssemblyReference WithOptions(TypeSystemOptions options) + { + return new PEFileWithOptions(this, options); + } + + IAssembly TypeSystem.IAssemblyReference.Resolve(ITypeResolveContext context) + { + return new MetadataAssembly(context.Compilation, this, TypeSystemOptions.Default); + } + + private class PEFileWithOptions : TypeSystem.IAssemblyReference + { + readonly PEFile peFile; + readonly TypeSystemOptions options; + + public PEFileWithOptions(PEFile peFile, TypeSystemOptions options) + { + this.peFile = peFile; + this.options = options; + } + + IAssembly TypeSystem.IAssemblyReference.Resolve(ITypeResolveContext context) + { + return new MetadataAssembly(context.Compilation, peFile, options); + } + } + } +} diff --git a/ICSharpCode.Decompiler/Metadata/SequencePoint.cs b/ICSharpCode.Decompiler/Metadata/SequencePoint.cs index d43829d30..5534b21fc 100644 --- a/ICSharpCode.Decompiler/Metadata/SequencePoint.cs +++ b/ICSharpCode.Decompiler/Metadata/SequencePoint.cs @@ -1,13 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Text; +// Copyright (c) 2018 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; namespace ICSharpCode.Decompiler.Metadata { - using SRMDocument = System.Reflection.Metadata.Document; - /// /// A sequence point read from a PDB file or produced by the decompiler. /// diff --git a/ICSharpCode.Decompiler/Metadata/SignatureBlobComparer.cs b/ICSharpCode.Decompiler/Metadata/SignatureBlobComparer.cs index 556839487..47f237834 100644 --- a/ICSharpCode.Decompiler/Metadata/SignatureBlobComparer.cs +++ b/ICSharpCode.Decompiler/Metadata/SignatureBlobComparer.cs @@ -1,4 +1,22 @@ -using System.Reflection.Metadata; +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index 267fd9772..639ef2d17 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -7,6 +25,7 @@ using System.Text; namespace ICSharpCode.Decompiler.Metadata { + // This inspired by Mono.Cecil's BaseAssemblyResolver/DefaultAssemblyResolver. public class UniversalAssemblyResolver : IAssemblyResolver { DotNetCorePathFinder dotNetCorePathFinder; @@ -141,7 +160,7 @@ namespace ICSharpCode.Decompiler.Metadata string ResolveInternal(IAssemblyReference name) { - if (name.IsNil()) + if (name == null) throw new ArgumentNullException(nameof(name)); var assembly = SearchDirectory(name, directories); diff --git a/ICSharpCode.Decompiler/Metadata/UnresolvedAssemblyNameReference.cs b/ICSharpCode.Decompiler/Metadata/UnresolvedAssemblyNameReference.cs index 13b1ecb7b..fa07e147c 100644 --- a/ICSharpCode.Decompiler/Metadata/UnresolvedAssemblyNameReference.cs +++ b/ICSharpCode.Decompiler/Metadata/UnresolvedAssemblyNameReference.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.Linq; using System.Text;