mirror of https://github.com/icsharpcode/ILSpy.git
12 changed files with 529 additions and 368 deletions
@ -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<byte>.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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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<AssemblyReference> AssemblyReferences => Metadata.AssemblyReferences.Select(r => new AssemblyReference(this, r)).ToImmutableArray(); |
||||||
|
public ImmutableArray<ModuleReferenceHandle> ModuleReferences => Metadata.GetModuleReferences().ToImmutableArray(); |
||||||
|
public ImmutableArray<TypeDefinition> TopLevelTypeDefinitions => Metadata.GetTopLevelTypeDefinitions().Select(t => new TypeDefinition(this, t)).ToImmutableArray(); |
||||||
|
public ImmutableArray<Resource> Resources => GetResources().ToImmutableArray(); |
||||||
|
|
||||||
|
IEnumerable<Resource> GetResources() |
||||||
|
{ |
||||||
|
var metadata = Metadata; |
||||||
|
foreach (var h in metadata.ManifestResources) { |
||||||
|
yield return new Resource(this, h); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
Reader.Dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
Dictionary<TopLevelTypeName, TypeDefinitionHandle> typeLookup; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the top-level-type with the specified name.
|
||||||
|
/// </summary>
|
||||||
|
public TypeDefinitionHandle GetTypeDefinition(TopLevelTypeName typeName) |
||||||
|
{ |
||||||
|
var lookup = LazyInit.VolatileRead(ref typeLookup); |
||||||
|
if (lookup == null) { |
||||||
|
lookup = new Dictionary<TopLevelTypeName, TypeDefinitionHandle>(); |
||||||
|
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<FullTypeName, ExportedTypeHandle> typeForwarderLookup; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the type forwarder with the specified name.
|
||||||
|
/// </summary>
|
||||||
|
public ExportedTypeHandle GetTypeForwarder(FullTypeName typeName) |
||||||
|
{ |
||||||
|
var lookup = LazyInit.VolatileRead(ref typeForwarderLookup); |
||||||
|
if (lookup == null) { |
||||||
|
lookup = new Dictionary<FullTypeName, ExportedTypeHandle>(); |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue