mirror of https://github.com/icsharpcode/ILSpy.git
33 changed files with 1129 additions and 428 deletions
@ -0,0 +1,403 @@
@@ -0,0 +1,403 @@
|
||||
// Copyright (c) 2010-2013 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.Linq; |
||||
|
||||
using ICSharpCode.Decompiler.IL; |
||||
using ICSharpCode.Decompiler.Output; |
||||
using ICSharpCode.Decompiler.Tests.TypeSystem; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
using static ICSharpCode.Decompiler.Output.ConversionFlags; |
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.Output |
||||
{ |
||||
[TestFixture] |
||||
public class ILAmbienceTests |
||||
{ |
||||
ICompilation compilation; |
||||
ILAmbience ambience; |
||||
|
||||
[OneTimeSetUp] |
||||
public void FixtureSetUp() |
||||
{ |
||||
ambience = new ILAmbience(); |
||||
|
||||
compilation = new SimpleCompilation(TypeSystemLoaderTests.TestAssembly, |
||||
TypeSystemLoaderTests.Mscorlib.WithOptions(TypeSystemOptions.Default)); |
||||
} |
||||
|
||||
ITypeDefinition GetDefinition(Type type) |
||||
{ |
||||
if (type == null) |
||||
{ |
||||
throw new ArgumentNullException(nameof(type)); |
||||
} |
||||
|
||||
var foundType = compilation.FindType(type).GetDefinition(); |
||||
Assert.That(foundType, Is.Not.Null); |
||||
return foundType; |
||||
} |
||||
|
||||
const ConversionFlags ILSpyMainTreeViewTypeFlags = ShowTypeParameterList | PlaceReturnTypeAfterParameterList; |
||||
const ConversionFlags ILSpyMainTreeViewMemberFlags = ILSpyMainTreeViewTypeFlags | ShowParameterList | ShowReturnType | ShowParameterModifiers; |
||||
|
||||
#region ITypeDefinition tests
|
||||
[TestCase(None, "Dictionary`2")] |
||||
[TestCase(ShowDefinitionKeyword, ".class Dictionary`2")] |
||||
[TestCase(ShowAccessibility, "public Dictionary`2")] |
||||
[TestCase(ShowDefinitionKeyword | ShowAccessibility, ".class public Dictionary`2")] |
||||
[TestCase(ShowTypeParameterList, "Dictionary`2<TKey,TValue>")] |
||||
[TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public Dictionary`2<TKey,TValue>")] |
||||
[TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.Dictionary`2<TKey,TValue>")] |
||||
[TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public System.Collections.Generic.Dictionary`2<TKey,TValue>")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "Dictionary`2<TKey,TValue>")] |
||||
public void GenericType(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(Dictionary<,>)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "Object")] |
||||
[TestCase(ShowDefinitionKeyword, ".class Object")] |
||||
[TestCase(ShowAccessibility, "public Object")] |
||||
[TestCase(ShowDefinitionKeyword | ShowAccessibility, ".class public Object")] |
||||
[TestCase(ShowTypeParameterList, "Object")] |
||||
[TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public Object")] |
||||
[TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Object")] |
||||
[TestCase(All, ".class public serializable beforefieldinit System.Object")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "Object")] |
||||
public void SimpleType(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(object)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "IEnumerable`1")] |
||||
[TestCase(ShowTypeParameterList, "IEnumerable`1<T>")] |
||||
[TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "IEnumerable`1<+T>")] |
||||
[TestCase(All, ".class interface public abstract System.Collections.Generic.IEnumerable`1<+T>")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "IEnumerable`1<T>")] |
||||
public void GenericInterface(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(IEnumerable<>)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "Enumerator")] |
||||
[TestCase(ShowDefinitionKeyword, ".class Enumerator")] |
||||
[TestCase(ShowAccessibility, "nested public Enumerator")] |
||||
[TestCase(ShowDefinitionKeyword | ShowAccessibility, ".class nested public Enumerator")] |
||||
[TestCase(ShowTypeParameterList, "Enumerator<T>")] |
||||
[TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class nested public Enumerator<T>")] |
||||
[TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.List`1<T>.Enumerator<T>")] |
||||
[TestCase(ShowDeclaringType | ShowTypeParameterList, "List`1<T>.Enumerator<T>")] |
||||
[TestCase(All, ".class nested public sealed serializable beforefieldinit System.Collections.Generic.List`1<T>.Enumerator<T>")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "Enumerator<T>")] |
||||
public void GenericTypeWithNested(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(List<>.Enumerator)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "StaticClass")] |
||||
[TestCase(ShowDefinitionKeyword, ".class StaticClass")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword, ".class abstract sealed beforefieldinit StaticClass")] |
||||
[TestCase(ShowModifiers | ShowAccessibility, "private abstract sealed beforefieldinit StaticClass")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private abstract sealed beforefieldinit StaticClass")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList, "abstract sealed beforefieldinit StaticClass")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private abstract sealed beforefieldinit StaticClass")] |
||||
[TestCase(All, ".class private abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.StaticClass")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "StaticClass")] |
||||
public void StaticClassTest(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(StaticClass)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "SealedClass")] |
||||
[TestCase(ShowDefinitionKeyword, ".class SealedClass")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit SealedClass")] |
||||
[TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit SealedClass")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit SealedClass")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit SealedClass")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit SealedClass")] |
||||
[TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.SealedClass")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "SealedClass")] |
||||
public void SealedClassTest(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(SealedClass)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "RefStruct")] |
||||
[TestCase(ShowDefinitionKeyword, ".class RefStruct")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit RefStruct")] |
||||
[TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit RefStruct")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit RefStruct")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit RefStruct")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit RefStruct")] |
||||
[TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.RefStruct")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "RefStruct")] |
||||
public void RefStructTest(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(RefStruct)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "ReadonlyStruct")] |
||||
[TestCase(ShowDefinitionKeyword, ".class ReadonlyStruct")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit ReadonlyStruct")] |
||||
[TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit ReadonlyStruct")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyStruct")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit ReadonlyStruct")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyStruct")] |
||||
[TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.ReadonlyStruct")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "ReadonlyStruct")] |
||||
public void ReadonlyStructTest(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(ReadonlyStruct)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(None, "ReadonlyRefStruct")] |
||||
[TestCase(ShowDefinitionKeyword, ".class ReadonlyRefStruct")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword, ".class sealed beforefieldinit ReadonlyRefStruct")] |
||||
[TestCase(ShowModifiers | ShowAccessibility, "private sealed beforefieldinit ReadonlyRefStruct")] |
||||
[TestCase(ShowModifiers | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyRefStruct")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList, "sealed beforefieldinit ReadonlyRefStruct")] |
||||
[TestCase(ShowModifiers | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class private sealed beforefieldinit ReadonlyRefStruct")] |
||||
[TestCase(All, ".class private sealed beforefieldinit ICSharpCode.Decompiler.Tests.Output.ReadonlyRefStruct")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "ReadonlyRefStruct")] |
||||
public void ReadonlyRefStructTest(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var typeDef = GetDefinition(typeof(ReadonlyRefStruct)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(typeDef), Is.EqualTo(expectedOutput)); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Delegate tests
|
||||
[TestCase(None, "Func`2")] |
||||
[TestCase(ShowTypeParameterList, "Func`2<T,TResult>")] |
||||
[TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "Func`2<-T,+TResult>")] |
||||
[TestCase(All, ".class public sealed System.Func`2<-T,+TResult>")] |
||||
[TestCase(ILSpyMainTreeViewTypeFlags, "Func`2<T,TResult>")] |
||||
public void FuncDelegate(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var func = GetDefinition(typeof(Func<,>)); |
||||
ambience.ConversionFlags = flags; |
||||
Assert.That(ambience.ConvertSymbol(func), Is.EqualTo(expectedOutput)); |
||||
} |
||||
#endregion
|
||||
|
||||
#region IField tests
|
||||
[TestCase(All & ~PlaceReturnTypeAfterParameterList, ".field private instance int32 ICSharpCode.Decompiler.Tests.Output.Program::test")] |
||||
[TestCase(ILSpyMainTreeViewMemberFlags, "test : int32")] |
||||
[TestCase(All & ~(ShowDeclaringType | ShowModifiers | ShowAccessibility | PlaceReturnTypeAfterParameterList), ".field int32 ICSharpCode.Decompiler.Tests.Output.Program::test")] |
||||
public void SimpleField(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var field = GetDefinition(typeof(Program)).GetFields(f => f.Name == "test").Single(); |
||||
ambience.ConversionFlags = flags; |
||||
|
||||
Assert.That(ambience.ConvertSymbol(field), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(All & ~PlaceReturnTypeAfterParameterList, ".field private static literal int32 ICSharpCode.Decompiler.Tests.Output.Program::TEST2")] |
||||
[TestCase(ILSpyMainTreeViewMemberFlags, "TEST2 : int32")] |
||||
public void SimpleConstField(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var field = compilation.FindType(typeof(Program)).GetFields(f => f.Name == "TEST2").Single(); |
||||
ambience.ConversionFlags = flags; |
||||
|
||||
Assert.That(ambience.ConvertSymbol(field), Is.EqualTo(expectedOutput)); |
||||
} |
||||
#endregion
|
||||
|
||||
#region IEvent tests
|
||||
[Test] |
||||
public void EventWithDeclaringType() |
||||
{ |
||||
var ev = compilation.FindType(typeof(Program)).GetEvents(f => f.Name == "ProgramChanged").Single(); |
||||
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowDeclaringType; |
||||
string result = ambience.ConvertSymbol(ev); |
||||
|
||||
Assert.That(result, Is.EqualTo(".event instance EventHandler Program::ProgramChanged")); |
||||
} |
||||
|
||||
[Test] |
||||
public void CustomEvent() |
||||
{ |
||||
var ev = compilation.FindType(typeof(Program)).GetEvents(f => f.Name == "SomeEvent").Single(); |
||||
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags; |
||||
string result = ambience.ConvertSymbol(ev); |
||||
|
||||
Assert.That(result, Is.EqualTo(".event instance EventHandler SomeEvent")); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Property tests
|
||||
[TestCase(StandardConversionFlags, ".property instance int32 Test")] |
||||
[TestCase(ILSpyMainTreeViewMemberFlags, "Test : int32")] |
||||
public void AutomaticProperty(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var prop = compilation.FindType(typeof(Program)).GetProperties(p => p.Name == "Test").Single(); |
||||
ambience.ConversionFlags = flags; |
||||
|
||||
Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(StandardConversionFlags, ".property instance int32 Item(int32 index)")] |
||||
[TestCase(ILSpyMainTreeViewMemberFlags, "Item(int32) : int32")] |
||||
public void Indexer(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var prop = compilation.FindType(typeof(Program)).GetProperties(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation).Single(); |
||||
ambience.ConversionFlags = flags; |
||||
|
||||
Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(StandardConversionFlags, ".property instance int32 ICSharpCode.Decompiler.Tests.Output.Interface.Item(int32 index)")] |
||||
[TestCase(ILSpyMainTreeViewMemberFlags, "ICSharpCode.Decompiler.Tests.Output.Interface.Item(int32) : int32")] |
||||
public void ExplicitIndexer(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var prop = compilation.FindType(typeof(Program)).GetProperties(p => p.IsIndexer && p.IsExplicitInterfaceImplementation).Single(); |
||||
ambience.ConversionFlags = flags; |
||||
|
||||
Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); |
||||
} |
||||
#endregion
|
||||
|
||||
#region IMethod tests
|
||||
[TestCase(StandardConversionFlags, ".method public hidebysig specialname rtspecialname instance .ctor(int32 x)")] |
||||
[TestCase(ILSpyMainTreeViewMemberFlags, ".ctor(int32)")] |
||||
public void ConstructorTests(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var prop = compilation.FindType(typeof(Program)).GetConstructors().Single(); |
||||
ambience.ConversionFlags = flags; |
||||
|
||||
Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); |
||||
} |
||||
|
||||
[TestCase(StandardConversionFlags, ".method family hidebysig virtual instance void Finalize()")] |
||||
[TestCase(ILSpyMainTreeViewMemberFlags, "Finalize() : void")] |
||||
public void DestructorTests(ConversionFlags flags, string expectedOutput) |
||||
{ |
||||
var dtor = compilation.FindType(typeof(Program)) |
||||
.GetMembers(m => m.SymbolKind == SymbolKind.Destructor, GetMemberOptions.IgnoreInheritedMembers).Single(); |
||||
ambience.ConversionFlags = flags; |
||||
|
||||
Assert.That(ambience.ConvertSymbol(dtor), Is.EqualTo(expectedOutput)); |
||||
} |
||||
#endregion
|
||||
} |
||||
|
||||
#region Test types
|
||||
#pragma warning disable 169, 67
|
||||
|
||||
class Test { } |
||||
static class StaticClass { } |
||||
sealed class SealedClass { } |
||||
ref struct RefStruct { } |
||||
readonly struct ReadonlyStruct { } |
||||
readonly ref struct ReadonlyRefStruct { } |
||||
|
||||
interface Interface |
||||
{ |
||||
int this[int x] { get; } |
||||
} |
||||
|
||||
class Program : Interface |
||||
{ |
||||
int test; |
||||
const int TEST2 = 2; |
||||
|
||||
public int Test { get; set; } |
||||
|
||||
public int this[int index] { |
||||
get { |
||||
return index; |
||||
} |
||||
} |
||||
|
||||
int Interface.this[int index] { |
||||
get { |
||||
return index; |
||||
} |
||||
} |
||||
|
||||
public event EventHandler ProgramChanged; |
||||
|
||||
public event EventHandler SomeEvent { |
||||
add { } |
||||
remove { } |
||||
} |
||||
|
||||
public static bool operator +(Program lhs, Program rhs) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public static implicit operator Test(Program lhs) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public static explicit operator int(Program lhs) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public Program(int x) |
||||
{ |
||||
|
||||
} |
||||
|
||||
~Program() |
||||
{ |
||||
|
||||
} |
||||
|
||||
public static void Main(string[] args) |
||||
{ |
||||
Console.WriteLine("Hello World!"); |
||||
|
||||
Console.Write("Press any key to continue . . . "); |
||||
Console.ReadKey(true); |
||||
} |
||||
|
||||
public static void InParameter(in int a) |
||||
{ |
||||
|
||||
} |
||||
} |
||||
#endregion
|
||||
} |
||||
@ -0,0 +1,520 @@
@@ -0,0 +1,520 @@
|
||||
// Copyright (c) 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; |
||||
using System.Reflection; |
||||
using System.Reflection.Metadata; |
||||
using System.Text; |
||||
|
||||
using ICSharpCode.Decompiler.Disassembler; |
||||
using ICSharpCode.Decompiler.Output; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
||||
|
||||
#nullable enable |
||||
|
||||
namespace ICSharpCode.Decompiler.IL |
||||
{ |
||||
public class ILAmbience : IAmbience |
||||
{ |
||||
public ConversionFlags ConversionFlags { get; set; } |
||||
|
||||
public string ConvertConstantValue(object constantValue) |
||||
{ |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
public string ConvertSymbol(ISymbol symbol) |
||||
{ |
||||
StringWriter sw = new StringWriter(); |
||||
|
||||
ConvertSymbol(sw, symbol); |
||||
|
||||
return sw.ToString(); |
||||
} |
||||
|
||||
void ConvertSymbol(StringWriter writer, ISymbol symbol) |
||||
{ |
||||
var metadata = (symbol as IEntity)?.ParentModule!.MetadataFile?.Metadata; |
||||
var token = (symbol as IEntity)?.MetadataToken ?? default; |
||||
|
||||
var output = new PlainTextOutput(writer); |
||||
|
||||
switch (symbol) |
||||
{ |
||||
case IField f: |
||||
Debug.Assert(metadata != null); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) |
||||
writer.Write(".field "); |
||||
var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)token); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowAccessibility)) |
||||
ReflectionDisassembler.WriteEnum(fd.Attributes & FieldAttributes.FieldAccessMask, ReflectionDisassembler.fieldVisibility, output); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) |
||||
{ |
||||
const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; |
||||
ReflectionDisassembler.WriteFlags(fd.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), ReflectionDisassembler.fieldAttributes, output); |
||||
if (!f.IsStatic) |
||||
{ |
||||
writer.Write("instance "); |
||||
} |
||||
} |
||||
break; |
||||
case IMethod m: |
||||
Debug.Assert(metadata != null); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) |
||||
writer.Write(".method "); |
||||
var md = metadata.GetMethodDefinition((MethodDefinitionHandle)token); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowAccessibility)) |
||||
ReflectionDisassembler.WriteEnum(md.Attributes & MethodAttributes.MemberAccessMask, ReflectionDisassembler.methodVisibility, output); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) |
||||
{ |
||||
ReflectionDisassembler.WriteFlags(md.Attributes & ~MethodAttributes.MemberAccessMask, ReflectionDisassembler.methodAttributeFlags, output); |
||||
if (!m.IsStatic) |
||||
{ |
||||
writer.Write("instance "); |
||||
} |
||||
} |
||||
break; |
||||
case IProperty p: |
||||
Debug.Assert(metadata != null); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) |
||||
writer.Write(".property "); |
||||
var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)token); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) |
||||
{ |
||||
ReflectionDisassembler.WriteFlags(pd.Attributes, ReflectionDisassembler.propertyAttributes, output); |
||||
if (!p.IsStatic) |
||||
{ |
||||
writer.Write("instance "); |
||||
} |
||||
} |
||||
break; |
||||
case IEvent e: |
||||
Debug.Assert(metadata != null); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) |
||||
writer.Write(".event "); |
||||
var ed = metadata.GetEventDefinition((EventDefinitionHandle)token); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) |
||||
{ |
||||
ReflectionDisassembler.WriteFlags(ed.Attributes, ReflectionDisassembler.eventAttributes, output); |
||||
if (!e.IsStatic) |
||||
{ |
||||
writer.Write("instance "); |
||||
} |
||||
} |
||||
break; |
||||
case ITypeDefinition: |
||||
Debug.Assert(metadata != null); |
||||
var td = metadata.GetTypeDefinition((TypeDefinitionHandle)token); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowDefinitionKeyword)) |
||||
{ |
||||
writer.Write(".class "); |
||||
if (td.Attributes.HasFlag(TypeAttributes.Interface)) |
||||
writer.Write("interface "); |
||||
} |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowAccessibility)) |
||||
ReflectionDisassembler.WriteEnum(td.Attributes & TypeAttributes.VisibilityMask, ReflectionDisassembler.typeVisibility, output); |
||||
const TypeAttributes masks = TypeAttributes.ClassSemanticsMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowModifiers)) |
||||
ReflectionDisassembler.WriteFlags(td.Attributes & ~masks, ReflectionDisassembler.typeAttributes, output); |
||||
break; |
||||
} |
||||
|
||||
bool showReturnTypeBefore = ConversionFlags.HasFlag(ConversionFlags.ShowReturnType) |
||||
&& !ConversionFlags.HasFlag(ConversionFlags.PlaceReturnTypeAfterParameterList); |
||||
bool showReturnTypeAfter = ConversionFlags.HasFlag(ConversionFlags.ShowReturnType) |
||||
&& ConversionFlags.HasFlag(ConversionFlags.PlaceReturnTypeAfterParameterList); |
||||
|
||||
if (showReturnTypeBefore && symbol is IMember { SymbolKind: not SymbolKind.Constructor }) |
||||
{ |
||||
switch (symbol) |
||||
{ |
||||
case IField f: |
||||
writer.Write(ConvertType(f.ReturnType)); |
||||
break; |
||||
case IMethod m: |
||||
writer.Write(ConvertType(m.ReturnType)); |
||||
break; |
||||
case IProperty p: |
||||
writer.Write(ConvertType(p.ReturnType)); |
||||
break; |
||||
case IEvent e: |
||||
writer.Write(ConvertType(e.ReturnType)); |
||||
break; |
||||
} |
||||
|
||||
writer.Write(' '); |
||||
} |
||||
|
||||
void WriteTypeDefinition(ITypeDefinition typeDef) |
||||
{ |
||||
if ((ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedEntityNames) |
||||
|| ConversionFlags.HasFlag(ConversionFlags.ShowDeclaringType)) |
||||
&& typeDef.DeclaringTypeDefinition != null) |
||||
{ |
||||
WriteTypeDefinition(typeDef.DeclaringTypeDefinition); |
||||
writer.Write('.'); |
||||
} |
||||
else if (ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedEntityNames) |
||||
&& !string.IsNullOrEmpty(typeDef.Namespace)) |
||||
{ |
||||
writer.Write(typeDef.Namespace); |
||||
writer.Write('.'); |
||||
} |
||||
writer.Write(typeDef.Name); |
||||
WriteTypeParameters(typeDef.TypeParameters, typeDef); |
||||
} |
||||
|
||||
void WriteTypeParameters(IReadOnlyList<ITypeParameter> typeParameters, IEntity owner) |
||||
{ |
||||
if (typeParameters.Count > 0) |
||||
{ |
||||
int typeParameterCount = typeParameters.Count - (owner.DeclaringTypeDefinition?.TypeParameterCount ?? 0); |
||||
if (typeParameterCount > 0) |
||||
{ |
||||
switch (owner) |
||||
{ |
||||
case IType: |
||||
writer.Write("`"); |
||||
break; |
||||
case IMethod _: |
||||
writer.Write("``"); |
||||
break; |
||||
} |
||||
|
||||
writer.Write(typeParameterCount); |
||||
} |
||||
|
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowTypeParameterList)) |
||||
{ |
||||
int i = 0; |
||||
writer.Write('<'); |
||||
foreach (var tp in typeParameters) |
||||
{ |
||||
if (i > 0) |
||||
writer.Write(","); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowTypeParameterVarianceModifier)) |
||||
{ |
||||
switch (tp.Variance) |
||||
{ |
||||
case VarianceModifier.Covariant: |
||||
writer.Write('+'); |
||||
break; |
||||
case VarianceModifier.Contravariant: |
||||
writer.Write('-'); |
||||
break; |
||||
} |
||||
} |
||||
writer.Write(tp.Name); |
||||
i++; |
||||
} |
||||
writer.Write('>'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
switch (symbol) |
||||
{ |
||||
case ITypeDefinition definition: |
||||
WriteTypeDefinition(definition); |
||||
break; |
||||
case IMember member: |
||||
if ((ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedTypeNames) |
||||
|| ConversionFlags.HasFlag(ConversionFlags.ShowDeclaringType)) && member.DeclaringTypeDefinition != null) |
||||
{ |
||||
WriteTypeDefinition(member.DeclaringTypeDefinition); |
||||
writer.Write("::"); |
||||
} |
||||
writer.Write(member.Name); |
||||
if (member is IMethod method) |
||||
{ |
||||
WriteTypeParameters(method.TypeParameters, member); |
||||
} |
||||
break; |
||||
} |
||||
|
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowParameterList) && symbol is IParameterizedMember { SymbolKind: not SymbolKind.Property } pm) |
||||
{ |
||||
writer.Write('('); |
||||
int i = 0; |
||||
foreach (var parameter in pm.Parameters) |
||||
{ |
||||
if (i > 0) |
||||
writer.Write(", "); |
||||
writer.Write(ConvertType(parameter.Type)); |
||||
if (ConversionFlags.HasFlag(ConversionFlags.ShowParameterNames)) |
||||
writer.Write(" " + parameter.Name); |
||||
i++; |
||||
} |
||||
writer.Write(')'); |
||||
} |
||||
|
||||
if (showReturnTypeAfter && symbol is IMember { SymbolKind: not SymbolKind.Constructor }) |
||||
{ |
||||
writer.Write(" : "); |
||||
|
||||
switch (symbol) |
||||
{ |
||||
case IField f: |
||||
writer.Write(ConvertType(f.ReturnType)); |
||||
break; |
||||
case IMethod m: |
||||
writer.Write(ConvertType(m.ReturnType)); |
||||
break; |
||||
case IProperty p: |
||||
writer.Write(ConvertType(p.ReturnType)); |
||||
break; |
||||
case IEvent e: |
||||
writer.Write(ConvertType(e.ReturnType)); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public string ConvertType(IType type) |
||||
{ |
||||
var visitor = new TypeToStringVisitor(ConversionFlags); |
||||
type.AcceptVisitor(visitor); |
||||
return visitor.ToString(); |
||||
} |
||||
|
||||
class TypeToStringVisitor : TypeVisitor |
||||
{ |
||||
readonly ConversionFlags flags; |
||||
readonly StringBuilder builder; |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return builder.ToString(); |
||||
} |
||||
|
||||
public TypeToStringVisitor(ConversionFlags flags) |
||||
{ |
||||
this.flags = flags; |
||||
this.builder = new StringBuilder(); |
||||
} |
||||
|
||||
public override IType VisitArrayType(ArrayType type) |
||||
{ |
||||
base.VisitArrayType(type); |
||||
builder.Append('['); |
||||
builder.Append(',', type.Dimensions - 1); |
||||
builder.Append(']'); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitByReferenceType(ByReferenceType type) |
||||
{ |
||||
base.VisitByReferenceType(type); |
||||
builder.Append('&'); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitModOpt(ModifiedType type) |
||||
{ |
||||
type.ElementType.AcceptVisitor(this); |
||||
builder.Append(" modopt("); |
||||
type.Modifier.AcceptVisitor(this); |
||||
builder.Append(")"); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitModReq(ModifiedType type) |
||||
{ |
||||
type.ElementType.AcceptVisitor(this); |
||||
builder.Append(" modreq("); |
||||
type.Modifier.AcceptVisitor(this); |
||||
builder.Append(")"); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitPointerType(PointerType type) |
||||
{ |
||||
base.VisitPointerType(type); |
||||
builder.Append('*'); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitTypeParameter(ITypeParameter type) |
||||
{ |
||||
base.VisitTypeParameter(type); |
||||
EscapeName(builder, type.Name); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitParameterizedType(ParameterizedType type) |
||||
{ |
||||
type.GenericType.AcceptVisitor(this); |
||||
builder.Append('<'); |
||||
for (int i = 0; i < type.TypeArguments.Count; i++) |
||||
{ |
||||
if (i > 0) |
||||
builder.Append(','); |
||||
type.TypeArguments[i].AcceptVisitor(this); |
||||
} |
||||
builder.Append('>'); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitTupleType(TupleType type) |
||||
{ |
||||
type.UnderlyingType.AcceptVisitor(this); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitFunctionPointerType(FunctionPointerType type) |
||||
{ |
||||
builder.Append("method "); |
||||
if (type.CallingConvention != SignatureCallingConvention.Default) |
||||
{ |
||||
builder.Append(type.CallingConvention.ToILSyntax()); |
||||
builder.Append(' '); |
||||
} |
||||
type.ReturnType.AcceptVisitor(this); |
||||
builder.Append(" *("); |
||||
bool first = true; |
||||
foreach (var p in type.ParameterTypes) |
||||
{ |
||||
if (first) |
||||
first = false; |
||||
else |
||||
builder.Append(", "); |
||||
|
||||
p.AcceptVisitor(this); |
||||
} |
||||
builder.Append(')'); |
||||
return type; |
||||
} |
||||
|
||||
public override IType VisitOtherType(IType type) |
||||
{ |
||||
WriteType(type); |
||||
return type; |
||||
} |
||||
|
||||
private void WriteType(IType type) |
||||
{ |
||||
if (flags.HasFlag(ConversionFlags.UseFullyQualifiedTypeNames)) |
||||
EscapeName(builder, type.FullName); |
||||
else |
||||
EscapeName(builder, type.Name); |
||||
if (type.TypeParameterCount > 0) |
||||
{ |
||||
builder.Append('`'); |
||||
builder.Append(type.TypeParameterCount); |
||||
} |
||||
} |
||||
|
||||
public override IType VisitTypeDefinition(ITypeDefinition type) |
||||
{ |
||||
switch (type.KnownTypeCode) |
||||
{ |
||||
case KnownTypeCode.Object: |
||||
builder.Append("object"); |
||||
break; |
||||
case KnownTypeCode.Boolean: |
||||
builder.Append("bool"); |
||||
break; |
||||
case KnownTypeCode.Char: |
||||
builder.Append("char"); |
||||
break; |
||||
case KnownTypeCode.SByte: |
||||
builder.Append("int8"); |
||||
break; |
||||
case KnownTypeCode.Byte: |
||||
builder.Append("uint8"); |
||||
break; |
||||
case KnownTypeCode.Int16: |
||||
builder.Append("int16"); |
||||
break; |
||||
case KnownTypeCode.UInt16: |
||||
builder.Append("uint16"); |
||||
break; |
||||
case KnownTypeCode.Int32: |
||||
builder.Append("int32"); |
||||
break; |
||||
case KnownTypeCode.UInt32: |
||||
builder.Append("uint32"); |
||||
break; |
||||
case KnownTypeCode.Int64: |
||||
builder.Append("int64"); |
||||
break; |
||||
case KnownTypeCode.UInt64: |
||||
builder.Append("uint64"); |
||||
break; |
||||
case KnownTypeCode.Single: |
||||
builder.Append("float32"); |
||||
break; |
||||
case KnownTypeCode.Double: |
||||
builder.Append("float64"); |
||||
break; |
||||
case KnownTypeCode.String: |
||||
builder.Append("string"); |
||||
break; |
||||
case KnownTypeCode.Void: |
||||
builder.Append("void"); |
||||
break; |
||||
case KnownTypeCode.IntPtr: |
||||
builder.Append("native int"); |
||||
break; |
||||
case KnownTypeCode.UIntPtr: |
||||
builder.Append("native uint"); |
||||
break; |
||||
case KnownTypeCode.TypedReference: |
||||
builder.Append("typedref"); |
||||
break; |
||||
default: |
||||
WriteType(type); |
||||
break; |
||||
} |
||||
return type; |
||||
} |
||||
} |
||||
|
||||
public string WrapComment(string comment) |
||||
{ |
||||
return "// " + comment; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Escape characters that cannot be displayed in the UI.
|
||||
/// </summary>
|
||||
public static StringBuilder EscapeName(StringBuilder sb, string name) |
||||
{ |
||||
foreach (char ch in name) |
||||
{ |
||||
if (char.IsWhiteSpace(ch) || char.IsControl(ch) || char.IsSurrogate(ch)) |
||||
sb.AppendFormat("\\u{0:x4}", (int)ch); |
||||
else |
||||
sb.Append(ch); |
||||
} |
||||
return sb; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Escape characters that cannot be displayed in the UI.
|
||||
/// </summary>
|
||||
public static string EscapeName(string name) |
||||
{ |
||||
return EscapeName(new StringBuilder(name.Length), name).ToString(); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue