Browse Source

Implement new declsec parser in ReflectionDisassembler + added unit tests.

pull/1030/head
Siegfried Pammer 7 years ago
parent
commit
ce855885b1
  1. 68
      ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs
  2. 1
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  3. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 381
      ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SecurityDeclarations.il
  5. 516
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  6. 9
      ICSharpCode.Decompiler/Metadata/CustomAttributeDecoder.cs
  7. 4
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

68
ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// 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.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.Tests.Helpers;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
[TestFixture, Parallelizable(ParallelScope.All)]
public class DisassemblerPrettyTestRunner
{
static readonly string TestCasePath = Tester.TestCasePath + "/Disassembler/Pretty";
[Test]
public void AllFilesHaveTests()
{
var testNames = typeof(DisassemblerPrettyTestRunner).GetMethods()
.Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any())
.Select(m => m.Name)
.ToArray();
foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles()) {
if (file.Extension.Equals(".il", StringComparison.OrdinalIgnoreCase)) {
var testName = file.Name.Split('.')[0];
Assert.Contains(testName, testNames);
}
}
}
[Test]
public void SecurityDeclarations()
{
Run();
}
void Run([CallerMemberName] string testName = null)
{
var ilExpectedFile = Path.Combine(TestCasePath, testName + ".il");
var ilResultFile = Path.Combine(TestCasePath, testName + ".result.il");
var executable = Tester.AssembleIL(ilExpectedFile, AssemblerOptions.Library);
var disassembled = Tester.Disassemble(executable, ilResultFile, AssemblerOptions.UseOwnDisassembler);
CodeAssert.FilesAreEqual(ilExpectedFile, ilResultFile);
}
}
}

1
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -121,6 +121,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -121,6 +121,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var metadata = peFile.Metadata;
var output = new PlainTextOutput(writer);
ReflectionDisassembler rd = new ReflectionDisassembler(output, CancellationToken.None);
rd.AssemblyResolver = new UniversalAssemblyResolver(sourceFileName, true, null);
rd.DetectControlStructure = false;
rd.WriteAssemblyReferences(metadata);
if (metadata.IsAssembly)

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -62,11 +62,13 @@ @@ -62,11 +62,13 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="Semantics\ConversionTests.cs" />
<Compile Include="Semantics\ExplicitConversionTest.cs" />
<Compile Include="Semantics\OverloadResolutionTests.cs" />
<Compile Include="DataFlowTest.cs" />
<Compile Include="TestCases\Correctness\RefLocalsAndReturns.cs" />
<None Include="TestCases\Disassembler\Pretty\SecurityDeclarations.il" />
<Compile Include="TestCases\Pretty\CustomAttributeConflicts.cs" />
<Compile Include="TestCases\Pretty\DynamicTests.cs" />
<Compile Include="TestCases\Pretty\Issue1080.cs" />

381
ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/SecurityDeclarations.il

@ -0,0 +1,381 @@ @@ -0,0 +1,381 @@
.assembly extern mscorlib
{
.publickeytoken = (
b7 7a 5c 56 19 34 e0 89
)
.ver 4:0:0:0
}
.assembly SecurityDeclarations
{
.custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = (
01 00 07 31 2e 30 2e 30 2e 30 00 00
)
.hash algorithm 0x00008004 // SHA1
.ver 1:0:0:0
}
.module SecurityDeclarations.dll
// MVID: {761F919A-2373-48EB-9282-9DAB26913D43}
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WindowsCui
.corflags 0x00000001 // ILOnly
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class private sequential ansi sealed beforefieldinit SecurityDeclarations.TestStruct
extends [mscorlib]System.ValueType
{
.pack 0
.size 1
} // end of class SecurityDeclarations.TestStruct
.class private auto ansi beforefieldinit SecurityDeclarations.SimpleType
extends [mscorlib]System.Object
{
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method SimpleType::.ctor
} // end of class SecurityDeclarations.SimpleType
.class private auto ansi sealed SecurityDeclarations.TestEnum
extends [mscorlib]System.Enum
{
// Fields
.field public specialname rtspecialname int32 value__
.field public static literal valuetype SecurityDeclarations.TestEnum A = int32(0)
.field public static literal valuetype SecurityDeclarations.TestEnum B = int32(1)
.field public static literal valuetype SecurityDeclarations.TestEnum C = int32(2)
} // end of class SecurityDeclarations.TestEnum
.class private auto ansi beforefieldinit SecurityDeclarations.SecurityAttrTest
extends [mscorlib]System.Security.Permissions.SecurityAttribute
{
// Fields
.field private string[] _testStringArray
.field private int32[] _testInt32Array
.field private valuetype SecurityDeclarations.TestEnum[] _testEnumArray
.field private class [mscorlib]System.Type[] _testTypeArray
.field public int32 TestInt32
.field public class [mscorlib]System.Type TestType
.field public valuetype SecurityDeclarations.TestEnum TestEnumType
.field public object TestBoxed
.field public object TestBoxed2
.field public string TestString
.field public object TestBoxedString
.field public object TestBoxedArray
.field public object TestBoxedType
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor (
valuetype [mscorlib]System.Security.Permissions.SecurityAction action
) cil managed
{
// Method begins at RVA 0x2059
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: call instance void [mscorlib]System.Security.Permissions.SecurityAttribute::.ctor(valuetype [mscorlib]System.Security.Permissions.SecurityAction)
IL_0007: nop
IL_0008: nop
IL_0009: ret
} // end of method SecurityAttrTest::.ctor
.method public hidebysig virtual
instance class [mscorlib]System.Security.IPermission CreatePermission () cil managed
{
// Method begins at RVA 0x2064
// Code size 7 (0x7)
.maxstack 8
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor()
IL_0006: throw
} // end of method SecurityAttrTest::CreatePermission
.method public hidebysig specialname
instance string[] get_TestStringArray () cil managed
{
// Method begins at RVA 0x206c
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray
IL_0006: ret
} // end of method SecurityAttrTest::get_TestStringArray
.method public hidebysig specialname
instance void set_TestStringArray (
string[] 'value'
) cil managed
{
// Method begins at RVA 0x2074
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld string[] SecurityDeclarations.SecurityAttrTest::_testStringArray
IL_0007: ret
} // end of method SecurityAttrTest::set_TestStringArray
.method public hidebysig specialname
instance int32[] get_TestInt32Array () cil managed
{
// Method begins at RVA 0x207d
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array
IL_0006: ret
} // end of method SecurityAttrTest::get_TestInt32Array
.method public hidebysig specialname
instance void set_TestInt32Array (
int32[] 'value'
) cil managed
{
// Method begins at RVA 0x2085
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32[] SecurityDeclarations.SecurityAttrTest::_testInt32Array
IL_0007: ret
} // end of method SecurityAttrTest::set_TestInt32Array
.method public hidebysig specialname
instance valuetype SecurityDeclarations.TestEnum[] get_TestEnumArray () cil managed
{
// Method begins at RVA 0x208e
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray
IL_0006: ret
} // end of method SecurityAttrTest::get_TestEnumArray
.method public hidebysig specialname
instance void set_TestEnumArray (
valuetype SecurityDeclarations.TestEnum[] 'value'
) cil managed
{
// Method begins at RVA 0x2096
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::_testEnumArray
IL_0007: ret
} // end of method SecurityAttrTest::set_TestEnumArray
.method public hidebysig specialname
instance class [mscorlib]System.Type[] get_TestTypeArray () cil managed
{
// Method begins at RVA 0x209f
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray
IL_0006: ret
} // end of method SecurityAttrTest::get_TestTypeArray
.method public hidebysig specialname
instance void set_TestTypeArray (
class [mscorlib]System.Type[] 'value'
) cil managed
{
// Method begins at RVA 0x20a7
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::_testTypeArray
IL_0007: ret
} // end of method SecurityAttrTest::set_TestTypeArray
// Properties
.property instance string[] TestStringArray()
{
.get instance string[] SecurityDeclarations.SecurityAttrTest::get_TestStringArray()
.set instance void SecurityDeclarations.SecurityAttrTest::set_TestStringArray(string[])
}
.property instance int32[] TestInt32Array()
{
.get instance int32[] SecurityDeclarations.SecurityAttrTest::get_TestInt32Array()
.set instance void SecurityDeclarations.SecurityAttrTest::set_TestInt32Array(int32[])
}
.property instance valuetype SecurityDeclarations.TestEnum[] TestEnumArray()
{
.get instance valuetype SecurityDeclarations.TestEnum[] SecurityDeclarations.SecurityAttrTest::get_TestEnumArray()
.set instance void SecurityDeclarations.SecurityAttrTest::set_TestEnumArray(valuetype SecurityDeclarations.TestEnum[])
}
.property instance class [mscorlib]System.Type[] TestTypeArray()
{
.get instance class [mscorlib]System.Type[] SecurityDeclarations.SecurityAttrTest::get_TestTypeArray()
.set instance void SecurityDeclarations.SecurityAttrTest::set_TestTypeArray(class [mscorlib]System.Type[])
}
} // end of class SecurityDeclarations.SecurityAttrTest
.class private auto ansi beforefieldinit SecurityDeclarations.TestStringTypes
extends [mscorlib]System.Object
{
.permissionset assert = {
class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = {
field string TestString = string('Hello World!')
field object TestBoxedString = object(string('Boxed String'))
property string[] TestStringArray = string[2]('a' 'b')
field object TestBoxedArray = object(string[2]('c' 'd'))
}
}
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method TestStringTypes::.ctor
} // end of class SecurityDeclarations.TestStringTypes
.class private auto ansi beforefieldinit SecurityDeclarations.TestTypeTypes
extends [mscorlib]System.Object
{
.permissionset demand = {
class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = {
field type TestType = type(SecurityDeclarations.SimpleType)
field object TestBoxed = object(type(SecurityDeclarations.TestEnum))
property type[] TestTypeArray = type[2](SecurityDeclarations.TestStruct SecurityDeclarations.SimpleType)
field object TestBoxedArray = object(type[2](SecurityDeclarations.TestStringTypes SecurityDeclarations.TestTypeTypes))
}
}
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method TestTypeTypes::.ctor
} // end of class SecurityDeclarations.TestTypeTypes
.class private auto ansi beforefieldinit SecurityDeclarations.TestEnumTypes
extends [mscorlib]System.Object
{
.permissionset inheritcheck = {
class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = {
field enum SecurityDeclarations.TestEnum TestEnumType = int32(0)
field object TestBoxed = object(int32(1))
property enum SecurityDeclarations.TestEnum[] TestEnumArray = int32[3](0 1 2)
field object TestBoxed2 = object(object[4](int32(0) int32(1) int32(2) object[1](int32(3))))
field object TestBoxedArray = object(int32[3](0 1 2))
}
}
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method TestEnumTypes::.ctor
} // end of class SecurityDeclarations.TestEnumTypes
.class private auto ansi beforefieldinit SecurityDeclarations.TestInt32Types
extends [mscorlib]System.Object
{
.permissionset permitonly = {
class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = {
field int32 TestInt32 = int32(5)
field object TestBoxed = object(int32(10))
property int32[] TestInt32Array = int32[3](1 2 3)
field object TestBoxedArray = object(int32[3](4 5 6))
field object TestBoxed2 = object(object[3](int32(7) int32(8) int32(9)))
}
}
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method TestInt32Types::.ctor
} // end of class SecurityDeclarations.TestInt32Types
.class private auto ansi beforefieldinit SecurityDeclarations.NestedArrays
extends [mscorlib]System.Object
{
.permissionset assert = {
class 'SecurityDeclarations.SecurityAttrTest, SecurityDeclarations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' = {
field object TestBoxed2 = object(object[4](int32(1) int32(2) int32(3) object[3](int32(4) int32(5) int32(6))))
}
}
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method NestedArrays::.ctor
} // end of class SecurityDeclarations.NestedArrays

516
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -133,7 +133,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -133,7 +133,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleMethod(PEFile module, MethodDefinitionHandle handle)
{
mscorlib = null;
var genericContext = new GenericContext(handle, module);
// write method header
output.WriteReference(module, handle, ".method", isDefinition: true);
@ -144,7 +143,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -144,7 +143,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleMethodHeader(PEFile module, MethodDefinitionHandle handle)
{
mscorlib = null;
var genericContext = new GenericContext(handle, module);
// write method header
output.WriteReference(module, handle, ".method", isDefinition: true);
@ -399,136 +397,90 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -399,136 +397,90 @@ namespace ICSharpCode.Decompiler.Disassembler
}
}
void TryDecodeSecurityDeclaration(TextOutputWithRollback output, BlobReader blob, PEFile module)
class SecurityDeclarationDecoder : ICustomAttributeTypeProvider<(PrimitiveTypeCode, string)>
{
output.WriteLine(" = {");
output.Indent();
readonly ITextOutput output;
readonly IAssemblyResolver resolver;
readonly PEFile module;
string currentAssemblyName = null;
string currentFullAssemblyName = null;
if (module.Metadata.IsAssembly) {
currentAssemblyName = module.Metadata.GetString(module.Metadata.GetAssemblyDefinition().Name);
currentFullAssemblyName = module.Metadata.GetFullAssemblyName();
}
int count = blob.ReadCompressedInteger();
for (int i = 0; i < count; i++) {
var fullTypeName = blob.ReadSerializedString();
string[] nameParts = fullTypeName.Split(new[] { ", " }, StringSplitOptions.None);
if (nameParts.Length < 2 || nameParts[1] == currentAssemblyName) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(fullTypeName));
} else {
output.Write('[');
output.Write(nameParts[1]);
output.Write(']');
output.Write(nameParts[0]);
public SecurityDeclarationDecoder(ITextOutput output, IAssemblyResolver resolver, PEFile module)
{
this.output = output;
this.resolver = resolver;
this.module = module;
}
output.Write(" = {");
blob.ReadCompressedInteger(); // ?
// The specification seems to be incorrect here, so I'm using the logic from Cecil instead.
int argCount = blob.ReadCompressedInteger();
if (argCount > 0) {
output.WriteLine();
output.Indent();
for (int j = 0; j < argCount; j++) {
WriteSecurityDeclarationArgument(output, module, ref blob);
output.WriteLine();
public (PrimitiveTypeCode, string) GetPrimitiveType(PrimitiveTypeCode typeCode)
{
return (typeCode, null);
}
output.Unindent();
public (PrimitiveTypeCode, string) GetSystemType()
{
return (0, "type");
}
output.Write('}');
if (i + 1 < count)
output.Write(',');
output.WriteLine();
}
output.Unindent();
output.WriteLine("}");
public (PrimitiveTypeCode, string) GetSZArrayType((PrimitiveTypeCode, string) elementType)
{
return (elementType.Item1, (elementType.Item2 ?? PrimitiveTypeCodeToString(elementType.Item1)) + "[]");
}
enum TypeKind
public (PrimitiveTypeCode, string) GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
Primitive,
Type,
Enum
throw new NotImplementedException();
}
struct TypeInfo
public (PrimitiveTypeCode, string) GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
public PrimitiveSerializationTypeCode TypeCode;
public TypeKind Kind;
public bool IsArray;
public bool IsBoxed;
public string TypeName;
throw new NotImplementedException();
}
public TypeInfo(PrimitiveSerializationTypeCode typeCode, TypeKind kind, bool isArray, bool isBoxed, string typeName)
public (PrimitiveTypeCode, string) GetTypeFromSerializedName(string name)
{
TypeCode = typeCode;
Kind = kind;
IsArray = isArray;
IsBoxed = isBoxed;
TypeName = typeName;
if (resolver == null)
throw new EnumUnderlyingTypeResolveException();
var (containingModule, typeDefHandle) = ResolveType(name, module);
if (typeDefHandle.IsNil)
throw new EnumUnderlyingTypeResolveException();
if (typeDefHandle.IsEnum(containingModule.Metadata, out var typeCode))
return (typeCode, "enum " + name);
return (0, name);
}
public PrimitiveTypeCode GetUnderlyingEnumType((PrimitiveTypeCode, string) type)
{
return type.Item1;
}
static TypeInfo ReadArgumentType(ref BlobReader blob)
public bool IsSystemType((PrimitiveTypeCode, string) type)
{
var b = blob.ReadByte();
if (2 <= b && b <= 14) {
return new TypeInfo((PrimitiveSerializationTypeCode)b, TypeKind.Primitive, false, false, null);
return "type" == type.Item2;
}
switch (b) {
case 0x1d:
var result = ReadArgumentType(ref blob);
return new TypeInfo(result.TypeCode, result.Kind, true, false, result.TypeName);
case 0x50:
return new TypeInfo(0, TypeKind.Type, false, false, null);
case 0x51: // boxed primitive type
return new TypeInfo(0, TypeKind.Primitive, false, true, null);
case 0x55: // enum
return new TypeInfo(0, TypeKind.Enum, false, false, blob.ReadSerializedString());
default:
throw new BadImageFormatException($"Unexpected custom attribute type 0x{b:x}.");
}
}
object ReadSimpleArgumentValue(ref BlobReader blob, PEFile module, ref TypeInfo typeInfo)
{
switch (typeInfo.Kind) {
case TypeKind.Enum:
if (typeInfo.TypeCode == 0 && typeInfo.TypeName != null) {
typeInfo.TypeCode = ResolveEnumUnderlyingType(typeInfo.TypeName, module, out _);
}
goto case TypeKind.Primitive;
case TypeKind.Primitive:
switch (typeInfo.TypeCode) {
case PrimitiveSerializationTypeCode.Boolean: return blob.ReadBoolean();
case PrimitiveSerializationTypeCode.Byte: return blob.ReadByte();
case PrimitiveSerializationTypeCode.SByte: return blob.ReadSByte();
case PrimitiveSerializationTypeCode.Char: return blob.ReadChar();
case PrimitiveSerializationTypeCode.Int16: return blob.ReadInt16();
case PrimitiveSerializationTypeCode.UInt16: return blob.ReadUInt16();
case PrimitiveSerializationTypeCode.Int32: return blob.ReadInt32();
case PrimitiveSerializationTypeCode.UInt32: return blob.ReadUInt32();
case PrimitiveSerializationTypeCode.Int64: return blob.ReadInt64();
case PrimitiveSerializationTypeCode.UInt64: return blob.ReadUInt64();
case PrimitiveSerializationTypeCode.Single: return blob.ReadSingle();
case PrimitiveSerializationTypeCode.Double: return blob.ReadDouble();
case PrimitiveSerializationTypeCode.String: return blob.ReadSerializedString();
default: throw new ArgumentOutOfRangeException(nameof(typeInfo.TypeCode));
}
case TypeKind.Type:
return blob.ReadSerializedString();
default:
throw new ArgumentOutOfRangeException(nameof(typeInfo.Kind));
(PEFile, TypeDefinitionHandle) ResolveType(string typeName, PEFile module)
{
string[] nameParts = typeName.Split(new[] { ", " }, 2, StringSplitOptions.None);
string[] typeNameParts = nameParts[0].Split('.');
PEFile containingModule = null;
TypeDefinitionHandle typeDefHandle = default;
// if we deal with an assembly-qualified name, resolve the assembly
if (nameParts.Length == 2)
containingModule = resolver.Resolve(AssemblyNameReference.Parse(nameParts[1]));
if (containingModule != null) {
// try to find the type in the assembly
typeDefHandle = FindType(containingModule, typeNameParts);
} else {
// just fully-qualified name, try current assembly
typeDefHandle = FindType(module, typeNameParts);
containingModule = module;
if (typeDefHandle.IsNil && TryResolveMscorlib(out var mscorlib)) {
// otherwise try mscorlib
typeDefHandle = FindType(mscorlib, typeNameParts);
containingModule = mscorlib;
}
}
PrimitiveSerializationTypeCode ResolveEnumUnderlyingType(string typeName, PEFile module, out (PEFile Module, EntityHandle Handle) typeDefinition)
{
typeDefinition = default;
return (containingModule, typeDefHandle);
TypeDefinitionHandle FindType(PEFile currentModule, string[] name)
{
@ -562,30 +514,17 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -562,30 +514,17 @@ namespace ICSharpCode.Decompiler.Disassembler
}
return default;
}
string[] nameParts = typeName.Split(new[] { ", " }, 2, StringSplitOptions.None);
string[] typeNameParts = nameParts[0].Split('.');
PEFile containingModule = null;
TypeDefinitionHandle typeDefHandle = default;
// if we deal with an assembly-qualified name, resolve the assembly
if (nameParts.Length == 2)
containingModule = AssemblyResolver.Resolve(AssemblyNameReference.Parse(nameParts[1]));
if (containingModule != null) {
// try to find the type in the assembly
typeDefHandle = FindType(containingModule, typeNameParts);
} else {
// just fully-qualified name, try current assembly
typeDefHandle = FindType(module, typeNameParts);
containingModule = module;
if (typeDefHandle.IsNil && TryResolveMscorlib(out var mscorlib)) {
// otherwise try mscorlib
typeDefHandle = FindType(mscorlib, typeNameParts);
containingModule = mscorlib;
}
}
PrimitiveTypeCode ResolveEnumUnderlyingType(string typeName, PEFile module)
{
if (typeName.StartsWith("enum ", StringComparison.Ordinal))
typeName = typeName.Substring(5);
var (containingModule, typeDefHandle) = ResolveType(typeName, module);
if (typeDefHandle.IsNil || !typeDefHandle.IsEnum(containingModule.Metadata, out var typeCode))
throw new EnumUnderlyingTypeResolveException();
typeDefinition = (containingModule, typeDefHandle);
return (PrimitiveSerializationTypeCode)typeCode;
return typeCode;
}
PEFile mscorlib;
@ -597,243 +536,175 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -597,243 +536,175 @@ namespace ICSharpCode.Decompiler.Disassembler
mscorlib = this.mscorlib;
return true;
}
if (AssemblyResolver == null) {
if (resolver == null) {
return false;
}
this.mscorlib = mscorlib = AssemblyResolver.Resolve(AssemblyNameReference.Parse("mscorlib"));
this.mscorlib = mscorlib = resolver.Resolve(AssemblyNameReference.Parse("mscorlib"));
return this.mscorlib != null;
}
}
object ReadArgumentValue(ref BlobReader blob, PEFile module, ref TypeInfo typeInfo)
void TryDecodeSecurityDeclaration(TextOutputWithRollback output, BlobReader blob, PEFile module)
{
if (typeInfo.IsArray) {
uint elementCount = blob.ReadUInt32();
if (elementCount == 0xFFFF_FFFF) {
return null;
} else {
var array = new object[elementCount];
for (int i = 0; i < elementCount; i++) {
array[i] = ReadSimpleArgumentValue(ref blob, module, ref typeInfo);
}
return array;
}
} else if (typeInfo.IsBoxed) {
typeInfo = ReadArgumentType(ref blob);
typeInfo.IsBoxed = true;
if (typeInfo.IsArray) {
return ReadArgumentValue(ref blob, module, ref typeInfo);
} else {
return ReadSimpleArgumentValue(ref blob, module, ref typeInfo);
output.WriteLine(" = {");
output.Indent();
string currentAssemblyName = null;
string currentFullAssemblyName = null;
if (module.Metadata.IsAssembly) {
currentAssemblyName = module.Metadata.GetString(module.Metadata.GetAssemblyDefinition().Name);
currentFullAssemblyName = module.Metadata.GetFullAssemblyName();
}
int count = blob.ReadCompressedInteger();
for (int i = 0; i < count; i++) {
var fullTypeName = blob.ReadSerializedString();
string[] nameParts = fullTypeName.Split(new[] { ", " }, StringSplitOptions.None);
if (nameParts.Length < 2 || nameParts[1] == currentAssemblyName) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(fullTypeName));
} else {
return ReadSimpleArgumentValue(ref blob, module, ref typeInfo);
}
output.Write('[');
output.Write(nameParts[1]);
output.Write(']');
output.Write(nameParts[0]);
}
output.Write(" = {");
blob.ReadCompressedInteger(); // ?
// The specification seems to be incorrect here, so I'm using the logic from Cecil instead.
int argCount = blob.ReadCompressedInteger();
static void WritePrimitiveTypeCode(ITextOutput output, TypeCode typeCode)
{
switch (typeCode) {
case TypeCode.Boolean:
output.Write("bool");
break;
case TypeCode.Char:
output.Write("char");
break;
case TypeCode.SByte:
output.Write("int8");
break;
case TypeCode.Byte:
output.Write("uint8");
break;
case TypeCode.Int16:
output.Write("int16");
break;
case TypeCode.UInt16:
output.Write("uint16");
break;
case TypeCode.Int32:
output.Write("int32");
break;
case TypeCode.UInt32:
output.Write("uint32");
break;
case TypeCode.Int64:
output.Write("int64");
break;
case TypeCode.UInt64:
output.Write("uint64");
break;
case TypeCode.Single:
output.Write("float32");
break;
case TypeCode.Double:
output.Write("float64");
break;
}
}
var decoder = new CustomAttributeDecoder<(PrimitiveTypeCode Code, string Name)>(new SecurityDeclarationDecoder(output, AssemblyResolver, module), module.Metadata, provideBoxingTypeInfo: true);
var arguments = decoder.DecodeNamedArguments(ref blob, argCount);
static void WritePrimitiveTypeCode(ITextOutput output, PrimitiveSerializationTypeCode typeCode)
{
switch (typeCode) {
case PrimitiveSerializationTypeCode.Boolean:
output.Write("bool");
break;
case PrimitiveSerializationTypeCode.Byte:
output.Write("uint8");
break;
case PrimitiveSerializationTypeCode.SByte:
output.Write("int8");
break;
case PrimitiveSerializationTypeCode.Char:
output.Write("char");
break;
case PrimitiveSerializationTypeCode.Int16:
output.Write("int16");
break;
case PrimitiveSerializationTypeCode.UInt16:
output.Write("uint16");
break;
case PrimitiveSerializationTypeCode.Int32:
output.Write("int32");
break;
case PrimitiveSerializationTypeCode.UInt32:
output.Write("uint32");
break;
case PrimitiveSerializationTypeCode.Int64:
output.Write("int64");
break;
case PrimitiveSerializationTypeCode.UInt64:
output.Write("uint64");
break;
case PrimitiveSerializationTypeCode.Single:
output.Write("float32");
break;
case PrimitiveSerializationTypeCode.Double:
output.Write("float64");
break;
case PrimitiveSerializationTypeCode.String:
output.Write("string");
break;
}
if (argCount > 0) {
output.WriteLine();
output.Indent();
}
void WriteSecurityDeclarationArgument(ITextOutput output, PEFile module, ref BlobReader blob)
{
switch (blob.ReadByte()) {
case 0x53:
foreach (var argument in arguments) {
switch (argument.Kind) {
case CustomAttributeNamedArgumentKind.Field:
output.Write("field ");
break;
case 0x54:
case CustomAttributeNamedArgumentKind.Property:
output.Write("property ");
break;
}
var typeInfo = ReadArgumentType(ref blob);
var typeDefinition = default((PEFile Module, EntityHandle Handle));
if (typeInfo.Kind == TypeKind.Enum) {
typeInfo.TypeCode = ResolveEnumUnderlyingType(typeInfo.TypeName, module, out typeDefinition);
output.Write(argument.Type.Name ?? PrimitiveTypeCodeToString(argument.Type.Code));
output.Write(" " + argument.Name + " = ");
WriteValue(output, argument.Type, argument.Value);
output.WriteLine();
}
var name = blob.ReadSerializedString();
object value = ReadArgumentValue(ref blob, module, ref typeInfo);
WriteTypeInfo(output, module, typeInfo, typeDefinition.Module, typeDefinition.Handle);
if (argCount > 0) {
output.Unindent();
}
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(name));
output.Write(" = ");
output.Write('}');
if (typeInfo.IsBoxed) {
output.Write("object(");
if (i + 1 < count)
output.Write(',');
output.WriteLine();
}
WriteTypeInfo(output, module, typeInfo, typeDefinition.Module, typeDefinition.Handle, isValue: true);
output.Unindent();
output.WriteLine("}");
}
if (typeInfo.IsArray && ((object[])value).Length >= 0) {
output.Write(((object[])value).Length.ToString());
output.Write(']');
output.Write('(');
void WriteValue(ITextOutput output, (PrimitiveTypeCode Code, string Name) type, object value)
{
if (value is CustomAttributeTypedArgument<(PrimitiveTypeCode, string)> boxedValue) {
output.Write("object(");
WriteValue(output, boxedValue.Type, boxedValue.Value);
output.Write(")");
} else if (value is ImmutableArray<CustomAttributeTypedArgument<(PrimitiveTypeCode, string)>> arrayValue) {
string elementType = type.Name != null && !type.Name.StartsWith("enum ", StringComparison.Ordinal)
? type.Name.Remove(type.Name.Length - 2) : PrimitiveTypeCodeToString(type.Code);
output.Write(elementType);
output.Write("[");
output.Write(arrayValue.Length.ToString());
output.Write("](");
bool first = true;
foreach (var item in (object[])value) {
if (!first) output.Write(' ');
WriteValue(output, item, typeInfo);
foreach (var item in arrayValue) {
if (!first) output.Write(" ");
if (item.Value is CustomAttributeTypedArgument<(PrimitiveTypeCode, string)> boxedItem) {
WriteValue(output, boxedItem.Type, boxedItem.Value);
} else {
WriteSimpleValue(output, item.Value, elementType);
}
first = false;
}
output.Write(')');
output.Write(")");
} else {
output.Write('(');
WriteValue(output, value, typeInfo);
output.Write(')');
}
string typeName = type.Name != null && !type.Name.StartsWith("enum ", StringComparison.Ordinal)
? type.Name : PrimitiveTypeCodeToString(type.Code);
if (typeInfo.IsBoxed) {
output.Write(')');
output.Write(typeName);
output.Write("(");
WriteSimpleValue(output, value, typeName);
output.Write(")");
}
}
static void WriteValue(ITextOutput output, object value, TypeInfo typeInfo)
private static void WriteSimpleValue(ITextOutput output, object value, string typeName)
{
switch (typeInfo.Kind) {
case TypeKind.Primitive:
if (value is string) {
output.Write("'" + DisassemblerHelpers.EscapeString((string)value).Replace("'", "\'") + "'");
switch (typeName) {
case "string":
output.Write("'" + DisassemblerHelpers.EscapeString(value.ToString()).Replace("'", "\'") + "'");
break;
case "type":
var info = ((PrimitiveTypeCode Code, string Name))value;
if (info.Name.StartsWith("enum ", StringComparison.Ordinal)) {
output.Write(info.Name.Substring(5));
} else {
DisassemblerHelpers.WriteOperand(output, value);
output.Write(info.Name);
}
break;
case TypeKind.Type:
default:
output.Write(value.ToString());
break;
case TypeKind.Enum:
DisassemblerHelpers.WriteOperand(output, value);
break;
default:
throw new ArgumentOutOfRangeException(nameof(typeInfo.Kind));
}
}
static void WriteTypeInfo(ITextOutput output, PEFile currentModule, TypeInfo typeInfo, PEFile referencedModule, EntityHandle type, bool isValue = false)
static string PrimitiveTypeCodeToString(PrimitiveTypeCode typeCode)
{
if (typeInfo.IsBoxed && !isValue) {
output.Write("object");
return;
}
switch (typeInfo.Kind) {
case TypeKind.Primitive:
WritePrimitiveTypeCode(output, typeInfo.TypeCode);
break;
case TypeKind.Type:
output.Write("type");
break;
case TypeKind.Enum:
if (isValue) {
WritePrimitiveTypeCode(output, typeInfo.TypeCode);
} else {
output.Write("enum ");
if (type.IsNil) {
output.Write(DisassemblerHelpers.Escape(typeInfo.TypeName));
break;
}
if (referencedModule != currentModule) {
output.Write('[');
output.Write(referencedModule.Name);
output.Write(']');
output.WriteReference(referencedModule, type, type.GetFullTypeName(referencedModule.Metadata).ToString());
} else {
output.Write(DisassemblerHelpers.Escape(typeInfo.TypeName));
}
}
break;
switch (typeCode) {
case PrimitiveTypeCode.Boolean:
return "bool";
case PrimitiveTypeCode.Byte:
return "uint8";
case PrimitiveTypeCode.SByte:
return "int8";
case PrimitiveTypeCode.Char:
return "char";
case PrimitiveTypeCode.Int16:
return "int16";
case PrimitiveTypeCode.UInt16:
return "uint16";
case PrimitiveTypeCode.Int32:
return "int32";
case PrimitiveTypeCode.UInt32:
return "uint32";
case PrimitiveTypeCode.Int64:
return "int64";
case PrimitiveTypeCode.UInt64:
return "uint64";
case PrimitiveTypeCode.Single:
return "float32";
case PrimitiveTypeCode.Double:
return "float64";
case PrimitiveTypeCode.String:
return "string";
case PrimitiveTypeCode.Object:
return "object";
default:
break;
}
if (typeInfo.IsArray) {
if (isValue) {
output.Write('[');
} else {
output.Write("[]");
}
return "unknown";
}
}
#endregion
#region WriteMarshalInfo
@ -1168,7 +1039,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1168,7 +1039,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleField(PEFile module, FieldDefinitionHandle field)
{
mscorlib = null;
var metadata = module.Metadata;
var fieldDefinition = metadata.GetFieldDefinition(field);
output.WriteReference(module, field, ".field ", isDefinition: true);
@ -1219,7 +1089,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1219,7 +1089,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleProperty(PEFile module, PropertyDefinitionHandle property)
{
mscorlib = null;
var metadata = module.Metadata;
var propertyDefinition = metadata.GetPropertyDefinition(property);
output.WriteReference(module, property, ".property", true);
@ -1277,7 +1146,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1277,7 +1146,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleEvent(PEFile module, EventDefinitionHandle handle)
{
mscorlib = null;
var eventDefinition = module.Metadata.GetEventDefinition(handle);
var accessors = eventDefinition.GetAccessors();
TypeDefinitionHandle declaringType;
@ -1359,7 +1227,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1359,7 +1227,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleType(PEFile module, TypeDefinitionHandle type)
{
mscorlib = null;
var typeDefinition = module.Metadata.GetTypeDefinition(type);
output.WriteReference(module, type, ".class", true);
output.Write(" ");
@ -1623,7 +1490,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1623,7 +1490,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleNamespace(string nameSpace, PEFile module, IEnumerable<TypeDefinitionHandle> types)
{
mscorlib = null;
if (!string.IsNullOrEmpty(nameSpace)) {
output.Write(".namespace " + DisassemblerHelpers.Escape(nameSpace));
OpenBlock(false);

9
ICSharpCode.Decompiler/Metadata/CustomAttributeDecoder.cs

@ -18,11 +18,13 @@ namespace ICSharpCode.Decompiler.Metadata @@ -18,11 +18,13 @@ namespace ICSharpCode.Decompiler.Metadata
private readonly ICustomAttributeTypeProvider<TType> _provider;
private readonly MetadataReader _reader;
private readonly bool _provideBoxingTypeInfo;
public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, MetadataReader reader)
public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, MetadataReader reader, bool provideBoxingTypeInfo = false)
{
_reader = reader;
_provider = provider;
_provideBoxingTypeInfo = provideBoxingTypeInfo;
}
public ImmutableArray<CustomAttributeNamedArgument<TType>> DecodeNamedArguments(ref BlobReader valueReader, int count)
@ -109,6 +111,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -109,6 +111,7 @@ namespace ICSharpCode.Decompiler.Metadata
private CustomAttributeTypedArgument<TType> DecodeArgument(ref BlobReader valueReader, ArgumentTypeInfo info)
{
var outer = info;
if (info.TypeCode == SerializationTypeCode.TaggedObject) {
info = DecodeNamedArgumentType(ref valueReader);
}
@ -182,6 +185,10 @@ namespace ICSharpCode.Decompiler.Metadata @@ -182,6 +185,10 @@ namespace ICSharpCode.Decompiler.Metadata
throw new BadImageFormatException();
}
if (_provideBoxingTypeInfo && outer.TypeCode == SerializationTypeCode.TaggedObject) {
return new CustomAttributeTypedArgument<TType>(outer.Type, new CustomAttributeTypedArgument<TType>(info.Type, value));
}
return new CustomAttributeTypedArgument<TType>(info.Type, value);
}

4
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.Metadata
PEStreamOptions options = PEStreamOptions.Default)
{
this.options = options;
this.TargetFramework = targetFramework;
this.TargetFramework = targetFramework ?? string.Empty;
this.mainAssemblyFileName = mainAssemblyFileName;
this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
this.throwOnError = throwOnError;
@ -245,7 +245,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -245,7 +245,7 @@ namespace ICSharpCode.Decompiler.Metadata
static bool IsZero(Version version)
{
return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0;
return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0);
}
internal static Version ZeroVersion = new Version(0,0,0,0);

Loading…
Cancel
Save