mirror of https://github.com/icsharpcode/ILSpy.git
12 changed files with 529 additions and 368 deletions
@ -0,0 +1,198 @@
@@ -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 @@
@@ -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