diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 112acb1e5..83b84a146 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -143,6 +143,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/Output/ILAmbienceTests.cs b/ICSharpCode.Decompiler.Tests/Output/ILAmbienceTests.cs new file mode 100644 index 000000000..ce6d35545 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/Output/ILAmbienceTests.cs @@ -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")] + [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public Dictionary`2")] + [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.Dictionary`2")] + [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class public System.Collections.Generic.Dictionary`2")] + [TestCase(ILSpyMainTreeViewTypeFlags, "Dictionary`2")] + 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")] + [TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "IEnumerable`1<+T>")] + [TestCase(All, ".class interface public abstract System.Collections.Generic.IEnumerable`1<+T>")] + [TestCase(ILSpyMainTreeViewTypeFlags, "IEnumerable`1")] + 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")] + [TestCase(ShowTypeParameterList | ShowDefinitionKeyword | ShowAccessibility, ".class nested public Enumerator")] + [TestCase(UseFullyQualifiedEntityNames | ShowTypeParameterList, "System.Collections.Generic.List`1.Enumerator")] + [TestCase(ShowDeclaringType | ShowTypeParameterList, "List`1.Enumerator")] + [TestCase(All, ".class nested public sealed serializable beforefieldinit System.Collections.Generic.List`1.Enumerator")] + [TestCase(ILSpyMainTreeViewTypeFlags, "Enumerator")] + 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")] + [TestCase(ShowTypeParameterList | ShowTypeParameterVarianceModifier, "Func`2<-T,+TResult>")] + [TestCase(All, ".class public sealed System.Func`2<-T,+TResult>")] + [TestCase(ILSpyMainTreeViewTypeFlags, "Func`2")] + 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 +} diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index 35036232e..6bd362ecb 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; using System.Collections.Generic; using System.IO; @@ -58,8 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor TypeSystemAstBuilder astBuilder = CreateAstBuilder(); AstNode node = astBuilder.ConvertSymbol(symbol); writer.StartNode(node); - EntityDeclaration entityDecl = node as EntityDeclaration; - if (entityDecl != null) + if (node is EntityDeclaration entityDecl) PrintModifiers(entityDecl.Modifiers, writer); if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyword) == ConversionFlags.ShowDefinitionKeyword) @@ -280,7 +281,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor ConvertType(member.DeclaringType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } - IType explicitInterfaceType = GetExplicitInterfaceType(member); + IType? explicitInterfaceType = GetExplicitInterfaceType(member); string name = member.Name; if (explicitInterfaceType != null) { @@ -297,11 +298,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.WriteKeyword(Roles.Identifier, "this"); break; case SymbolKind.Constructor: - WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + WriteQualifiedName(member.DeclaringType!.Name, writer, formattingPolicy); break; case SymbolKind.Destructor: writer.WriteToken(DestructorDeclaration.TildeRole, "~"); - WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + WriteQualifiedName(member.DeclaringType!.Name, writer, formattingPolicy); break; case SymbolKind.Operator: switch (name) @@ -431,7 +432,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return astType.ToString(); } - public void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions formattingPolicy) { TypeSystemAstBuilder astBuilder = CreateAstBuilder(); astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; @@ -439,7 +440,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); } - IType GetExplicitInterfaceType(IMember member) + IType? GetExplicitInterfaceType(IMember member) { if (member.IsExplicitInterfaceImplementation) { diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 47230c2b6..fba0fdaef 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -99,7 +99,7 @@ namespace ICSharpCode.Decompiler.Disassembler } #region Disassemble Method - EnumNameCollection methodAttributeFlags = new EnumNameCollection() { + internal static readonly EnumNameCollection methodAttributeFlags = new EnumNameCollection() { { MethodAttributes.Final, "final" }, { MethodAttributes.HideBySig, "hidebysig" }, { MethodAttributes.SpecialName, "specialname" }, @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.Disassembler { MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm }; - EnumNameCollection methodVisibility = new EnumNameCollection() { + internal static readonly EnumNameCollection methodVisibility = new EnumNameCollection() { { MethodAttributes.Private, "private" }, { MethodAttributes.FamANDAssem, "famandassem" }, { MethodAttributes.Assembly, "assembly" }, @@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.Disassembler { MethodAttributes.Public, "public" }, }; - EnumNameCollection callingConvention = new EnumNameCollection() { + internal static readonly EnumNameCollection callingConvention = new EnumNameCollection() { { SignatureCallingConvention.CDecl, "unmanaged cdecl" }, { SignatureCallingConvention.StdCall, "unmanaged stdcall" }, { SignatureCallingConvention.ThisCall, "unmanaged thiscall" }, @@ -133,14 +133,14 @@ namespace ICSharpCode.Decompiler.Disassembler { SignatureCallingConvention.Default, null }, }; - EnumNameCollection methodCodeType = new EnumNameCollection() { + internal static readonly EnumNameCollection methodCodeType = new EnumNameCollection() { { MethodImplAttributes.IL, "cil" }, { MethodImplAttributes.Native, "native" }, { MethodImplAttributes.OPTIL, "optil" }, { MethodImplAttributes.Runtime, "runtime" }, }; - EnumNameCollection methodImpl = new EnumNameCollection() { + internal static readonly EnumNameCollection methodImpl = new EnumNameCollection() { { MethodImplAttributes.Synchronized, "synchronized" }, { MethodImplAttributes.NoInlining, "noinlining" }, { MethodImplAttributes.NoOptimization, "nooptimization" }, @@ -180,8 +180,8 @@ namespace ICSharpCode.Decompiler.Disassembler // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed // //emit flags - WriteEnum(methodDefinition.Attributes & MethodAttributes.MemberAccessMask, methodVisibility); - WriteFlags(methodDefinition.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags); + WriteEnum(methodDefinition.Attributes & MethodAttributes.MemberAccessMask, methodVisibility, output); + WriteFlags(methodDefinition.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags, output); bool isCompilerControlled = (methodDefinition.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope; if (isCompilerControlled) output.Write("privatescope "); @@ -259,7 +259,7 @@ namespace ICSharpCode.Decompiler.Disassembler } //call convention - WriteEnum(signature.Value.Header.CallingConvention, callingConvention); + WriteEnum(signature.Value.Header.CallingConvention, callingConvention, output); //return type signature.Value.ReturnType(ILNameSyntax.Signature); @@ -307,12 +307,12 @@ namespace ICSharpCode.Decompiler.Disassembler } output.Write(") "); //cil managed - WriteEnum(methodDefinition.ImplAttributes & MethodImplAttributes.CodeTypeMask, methodCodeType); + WriteEnum(methodDefinition.ImplAttributes & MethodImplAttributes.CodeTypeMask, methodCodeType, output); if ((methodDefinition.ImplAttributes & MethodImplAttributes.ManagedMask) == MethodImplAttributes.Managed) output.Write("managed "); else output.Write("unmanaged "); - WriteFlags(methodDefinition.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl); + WriteFlags(methodDefinition.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl, output); output.Unindent(); } @@ -1267,7 +1267,7 @@ namespace ICSharpCode.Decompiler.Disassembler #endregion #region Disassemble Field - EnumNameCollection fieldVisibility = new EnumNameCollection() { + internal static readonly EnumNameCollection fieldVisibility = new EnumNameCollection() { { FieldAttributes.Private, "private" }, { FieldAttributes.FamANDAssem, "famandassem" }, { FieldAttributes.Assembly, "assembly" }, @@ -1276,7 +1276,7 @@ namespace ICSharpCode.Decompiler.Disassembler { FieldAttributes.Public, "public" }, }; - EnumNameCollection fieldAttributes = new EnumNameCollection() { + internal static readonly EnumNameCollection fieldAttributes = new EnumNameCollection() { { FieldAttributes.Static, "static" }, { FieldAttributes.Literal, "literal" }, { FieldAttributes.InitOnly, "initonly" }, @@ -1360,9 +1360,9 @@ namespace ICSharpCode.Decompiler.Disassembler { output.Write("[" + offset + "] "); } - WriteEnum(fieldDefinition.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); + WriteEnum(fieldDefinition.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility, output); const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; - WriteFlags(fieldDefinition.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes); + WriteFlags(fieldDefinition.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes, output); var signature = fieldDefinition.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new MetadataGenericContext(fieldDefinition.GetDeclaringType(), module)); @@ -1415,7 +1415,7 @@ namespace ICSharpCode.Decompiler.Disassembler #endregion #region Disassemble Property - EnumNameCollection propertyAttributes = new EnumNameCollection() { + internal static readonly EnumNameCollection propertyAttributes = new EnumNameCollection() { { PropertyAttributes.SpecialName, "specialname" }, { PropertyAttributes.RTSpecialName, "rtspecialname" }, { PropertyAttributes.HasDefault, "hasdefault" }, @@ -1450,7 +1450,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteReference(module, handle, ".property", isDefinition: true); WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle), spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); - WriteFlags(propertyDefinition.Attributes, propertyAttributes); + WriteFlags(propertyDefinition.Attributes, propertyAttributes, output); var accessors = propertyDefinition.GetAccessors(); var declaringType = metadata.GetMethodDefinition(accessors.GetAny()).GetDeclaringType(); var signature = propertyDefinition.DecodeSignature(new DisassemblerSignatureTypeProvider(module, output), new MetadataGenericContext(declaringType, module)); @@ -1489,7 +1489,7 @@ namespace ICSharpCode.Decompiler.Disassembler #endregion #region Disassemble Event - EnumNameCollection eventAttributes = new EnumNameCollection() { + internal static readonly EnumNameCollection eventAttributes = new EnumNameCollection() { { EventAttributes.SpecialName, "specialname" }, { EventAttributes.RTSpecialName, "rtspecialname" }, }; @@ -1536,7 +1536,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteReference(module, handle, ".event", isDefinition: true); WriteMetadataToken(output, module, handle, MetadataTokens.GetToken(handle), spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); - WriteFlags(eventDefinition.Attributes, eventAttributes); + WriteFlags(eventDefinition.Attributes, eventAttributes, output); var provider = new DisassemblerSignatureTypeProvider(module, output); Action signature; switch (eventDefinition.Type.Kind) @@ -1561,7 +1561,7 @@ namespace ICSharpCode.Decompiler.Disassembler #endregion #region Disassemble Type - EnumNameCollection typeVisibility = new EnumNameCollection() { + internal static readonly EnumNameCollection typeVisibility = new EnumNameCollection() { { TypeAttributes.Public, "public" }, { TypeAttributes.NotPublic, "private" }, { TypeAttributes.NestedPublic, "nested public" }, @@ -1584,7 +1584,7 @@ namespace ICSharpCode.Decompiler.Disassembler { TypeAttributes.UnicodeClass, "unicode" }, }; - EnumNameCollection typeAttributes = new EnumNameCollection() { + internal static readonly EnumNameCollection typeAttributes = new EnumNameCollection() { { TypeAttributes.Abstract, "abstract" }, { TypeAttributes.Sealed, "sealed" }, { TypeAttributes.SpecialName, "specialname" }, @@ -1730,11 +1730,11 @@ namespace ICSharpCode.Decompiler.Disassembler spaceAfter: true, spaceBefore: true, ShowMetadataTokens, ShowMetadataTokensInBase10); if ((typeDefinition.Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) output.Write("interface "); - WriteEnum(typeDefinition.Attributes & TypeAttributes.VisibilityMask, typeVisibility); - WriteEnum(typeDefinition.Attributes & TypeAttributes.LayoutMask, typeLayout); - WriteEnum(typeDefinition.Attributes & TypeAttributes.StringFormatMask, typeStringFormat); + WriteEnum(typeDefinition.Attributes & TypeAttributes.VisibilityMask, typeVisibility, output); + WriteEnum(typeDefinition.Attributes & TypeAttributes.LayoutMask, typeLayout, output); + WriteEnum(typeDefinition.Attributes & TypeAttributes.StringFormatMask, typeStringFormat, output); const TypeAttributes masks = TypeAttributes.ClassSemanticsMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; - WriteFlags(typeDefinition.Attributes & ~masks, typeAttributes); + WriteFlags(typeDefinition.Attributes & ~masks, typeAttributes, output); output.Write(typeDefinition.GetDeclaringType().IsNil ? typeDefinition.GetFullTypeName(module.Metadata).ToILNameString() : DisassemblerHelpers.Escape(module.Metadata.GetString(typeDefinition.Name))); WriteTypeParameters(output, module, genericContext, typeDefinition.GetGenericParameters()); @@ -1967,7 +1967,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } - void WriteFlags(T flags, EnumNameCollection flagNames) where T : struct + internal static void WriteFlags(T flags, EnumNameCollection flagNames, ITextOutput output) where T : struct { long val = Convert.ToInt64(flags); long tested = 0; @@ -1984,7 +1984,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write("flags({0:x4}) ", val & ~tested); } - void WriteEnum(T enumValue, EnumNameCollection enumNames) where T : struct + internal static void WriteEnum(T enumValue, EnumNameCollection enumNames, ITextOutput output) where T : struct { long val = Convert.ToInt64(enumValue); foreach (var pair in enumNames) @@ -2006,10 +2006,14 @@ namespace ICSharpCode.Decompiler.Disassembler } - sealed class EnumNameCollection : IEnumerable> where T : struct + internal struct EnumNameCollection : IEnumerable> where T : struct { List> names = new List>(); + public EnumNameCollection() + { + } + public void Add(T flag, string name) { this.names.Add(new KeyValuePair(Convert.ToInt64(flag), name)); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 20a1b5d0a..4066fee3f 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -109,6 +109,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ILAmbience.cs b/ICSharpCode.Decompiler/IL/ILAmbience.cs new file mode 100644 index 000000000..071145d46 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/ILAmbience.cs @@ -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 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; + } + + /// + /// Escape characters that cannot be displayed in the UI. + /// + 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; + } + + /// + /// Escape characters that cannot be displayed in the UI. + /// + public static string EscapeName(string name) + { + return EscapeName(new StringBuilder(name.Length), name).ToString(); + } + } +} diff --git a/ICSharpCode.ILSpyX/Abstractions/ILanguage.cs b/ICSharpCode.ILSpyX/Abstractions/ILanguage.cs index e230f4467..1d85e3aca 100644 --- a/ICSharpCode.ILSpyX/Abstractions/ILanguage.cs +++ b/ICSharpCode.ILSpyX/Abstractions/ILanguage.cs @@ -19,6 +19,7 @@ using System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Abstractions @@ -30,10 +31,7 @@ namespace ICSharpCode.ILSpyX.Abstractions string GetEntityName(MetadataFile module, System.Reflection.Metadata.EntityHandle handle, bool fullName, bool omitGenerics); string GetTooltip(IEntity entity); - string TypeToString(IType type, bool includeNamespace); - string MethodToString(IMethod method, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName); - string FieldToString(IField field, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName); - string PropertyToString(IProperty property, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName); - string EventToString(IEvent @event, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName); + string TypeToString(IType type, ConversionFlags conversionFlags = ConversionFlags.UseFullyQualifiedEntityNames | ConversionFlags.UseFullyQualifiedTypeNames); + string EntityToString(IEntity entity, ConversionFlags conversionFlags); } } diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs index a197ed0a2..9df55a061 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs @@ -20,6 +20,7 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; @@ -45,7 +46,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes public override object Icon => EventTreeNode.GetIcon(analyzedEvent); // TODO: This way of formatting is not suitable for events which explicitly implement interfaces. - public override object Text => prefix + Language.EventToString(analyzedEvent, includeDeclaringTypeName: true, includeNamespace: false, includeNamespaceOfDeclaringTypeName: true); + public override object Text => prefix + Language.EntityToString(analyzedEvent, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs index 48831a67c..64a508c57 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs @@ -19,6 +19,7 @@ using System; using System.Diagnostics; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; @@ -39,7 +40,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes public override object Icon => FieldTreeNode.GetIcon(analyzedField); - public override object Text => Language.FieldToString(analyzedField, true, false, true); + public override object Text => Language.EntityToString(analyzedField, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs index 421c9bf2d..2ce76fe51 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs @@ -19,6 +19,7 @@ using System; using System.Diagnostics; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; @@ -41,7 +42,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes public override object Icon => MethodTreeNode.GetIcon(analyzedMethod); - public override object Text => prefix + Language.MethodToString(analyzedMethod, true, false, true); + public override object Text => prefix + Language.EntityToString(analyzedMethod, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs index 4de437182..d6cb1df5e 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs @@ -19,6 +19,7 @@ using System; using System.Diagnostics; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; @@ -42,7 +43,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes public override object Icon => PropertyTreeNode.GetIcon(analyzedProperty); // TODO: This way of formatting is not suitable for properties which explicitly implement interfaces. - public override object Text => prefix + Language.PropertyToString(analyzedProperty, includeNamespace: false, includeDeclaringTypeName: true, includeNamespaceOfDeclaringTypeName: true); + public override object Text => prefix + Language.EntityToString(analyzedProperty, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames); protected override void LoadChildren() { diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs index c4cd300ce..67f37d6f9 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes public override object Icon => TypeTreeNode.GetIcon(analyzedType); - public override object Text => Language.TypeToString(analyzedType, includeNamespace: true); + public override object Text => Language.TypeToString(analyzedType); protected override void LoadChildren() { diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 7ec5bb06a..2e0ef396d 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -34,6 +34,7 @@ using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Solution; @@ -157,7 +158,7 @@ namespace ICSharpCode.ILSpy AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); - WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); + WriteCommentLine(output, TypeToString(method.DeclaringType)); var methodDefinition = decompiler.TypeSystem.MainModule.ResolveEntity(method.MetadataToken) as IMethod; if (methodDefinition.IsConstructor && methodDefinition.DeclaringType.IsReferenceType != false) { @@ -258,7 +259,7 @@ namespace ICSharpCode.ILSpy AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); - WriteCommentLine(output, TypeToString(property.DeclaringType, includeNamespace: true)); + WriteCommentLine(output, TypeToString(property.DeclaringType)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(property.MetadataToken), decompiler.TypeSystem); } @@ -269,7 +270,7 @@ namespace ICSharpCode.ILSpy AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); - WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true)); + WriteCommentLine(output, TypeToString(field.DeclaringType)); if (field.IsConst) { WriteCode(output, options.DecompilerSettings, decompiler.Decompile(field.MetadataToken), decompiler.TypeSystem); @@ -348,7 +349,7 @@ namespace ICSharpCode.ILSpy AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); - WriteCommentLine(output, TypeToString(@event.DeclaringType, includeNamespace: true)); + WriteCommentLine(output, TypeToString(@event.DeclaringType)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(@event.MetadataToken), decompiler.TypeSystem); } @@ -359,7 +360,7 @@ namespace ICSharpCode.ILSpy AddReferenceAssemblyWarningMessage(assembly, output); AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, assembly.FullName); - WriteCommentLine(output, TypeToString(type, includeNamespace: true)); + WriteCommentLine(output, TypeToString(type, ConversionFlags.UseFullyQualifiedTypeNames | ConversionFlags.UseFullyQualifiedEntityNames)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(type.MetadataToken), decompiler.TypeSystem); } @@ -456,7 +457,7 @@ namespace ICSharpCode.ILSpy if (globalType != null) { output.Write("// Global type: "); - output.WriteReference(globalType, EscapeName(globalType.FullName)); + output.WriteReference(globalType, ILAmbience.EscapeName(globalType.FullName)); output.WriteLine(); } var metadata = module.Metadata; @@ -470,7 +471,7 @@ namespace ICSharpCode.ILSpy if (entrypoint != null) { output.Write("// Entry point: "); - output.WriteReference(entrypoint, EscapeName(entrypoint.DeclaringType.FullName + "." + entrypoint.Name)); + output.WriteReference(entrypoint, ILAmbience.EscapeName(entrypoint.DeclaringType.FullName + "." + entrypoint.Name)); output.WriteLine(); } } @@ -595,40 +596,27 @@ namespace ICSharpCode.ILSpy return ambience; } - string EntityToString(IEntity entity, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) + public override string EntityToString(IEntity entity, ConversionFlags conversionFlags) { // Do not forget to update CSharpAmbienceTests, if this ever changes. var ambience = CreateAmbience(); - ambience.ConversionFlags |= ConversionFlags.ShowReturnType | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterModifiers; - if (includeDeclaringTypeName) - ambience.ConversionFlags |= ConversionFlags.ShowDeclaringType; - if (includeNamespace) - ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedTypeNames; - if (includeNamespaceOfDeclaringTypeName) - ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedEntityNames; + ambience.ConversionFlags |= conversionFlags + | ConversionFlags.ShowReturnType + | ConversionFlags.ShowParameterList + | ConversionFlags.ShowParameterModifiers; return ambience.ConvertSymbol(entity); } - public override string TypeToString(IType type, bool includeNamespace) + public override string TypeToString(IType type, ConversionFlags conversionFlags = ConversionFlags.UseFullyQualifiedEntityNames | ConversionFlags.UseFullyQualifiedTypeNames) { if (type == null) throw new ArgumentNullException(nameof(type)); var ambience = CreateAmbience(); // Do not forget to update CSharpAmbienceTests.ILSpyMainTreeViewFlags, if this ever changes. - if (includeNamespace) - { - ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedTypeNames; - ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedEntityNames; - } + ambience.ConversionFlags |= conversionFlags; if (type is ITypeDefinition definition) { return ambience.ConvertSymbol(definition); - // HACK : UnknownType is not supported by CSharpAmbience. - } - else if (type.Kind == TypeKind.Unknown) - { - return (includeNamespace ? type.FullName : type.Name) - + (type.TypeParameterCount > 0 ? "<" + string.Join(", ", type.TypeArguments.Select(t => t.Name)) + ">" : ""); } else { @@ -636,34 +624,6 @@ namespace ICSharpCode.ILSpy } } - public override string FieldToString(IField field, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) - { - if (field == null) - throw new ArgumentNullException(nameof(field)); - return EntityToString(field, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName); - } - - public override string PropertyToString(IProperty property, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) - { - if (property == null) - throw new ArgumentNullException(nameof(property)); - return EntityToString(property, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName); - } - - public override string MethodToString(IMethod method, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) - { - if (method == null) - throw new ArgumentNullException(nameof(method)); - return EntityToString(method, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName); - } - - public override string EventToString(IEvent @event, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) - { - if (@event == null) - throw new ArgumentNullException(nameof(@event)); - return EntityToString(@event, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName); - } - static string ToCSharpString(MetadataReader metadata, TypeDefinitionHandle handle, bool fullName, bool omitGenerics) { var currentTypeDefHandle = handle; diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index 072b2a9d4..3aa09dff3 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -21,14 +21,14 @@ using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; -using System.Text; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpyX; @@ -87,27 +87,27 @@ namespace ICSharpCode.ILSpy public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { - WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition, includeNamespace: true) + "." + method.Name); + WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition) + "." + method.Name); } public virtual void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) { - WriteCommentLine(output, TypeToString(property.DeclaringTypeDefinition, includeNamespace: true) + "." + property.Name); + WriteCommentLine(output, TypeToString(property.DeclaringTypeDefinition) + "." + property.Name); } public virtual void DecompileField(IField field, ITextOutput output, DecompilationOptions options) { - WriteCommentLine(output, TypeToString(field.DeclaringTypeDefinition, includeNamespace: true) + "." + field.Name); + WriteCommentLine(output, TypeToString(field.DeclaringTypeDefinition) + "." + field.Name); } public virtual void DecompileEvent(IEvent @event, ITextOutput output, DecompilationOptions options) { - WriteCommentLine(output, TypeToString(@event.DeclaringTypeDefinition, includeNamespace: true) + "." + @event.Name); + WriteCommentLine(output, TypeToString(@event.DeclaringTypeDefinition) + "." + @event.Name); } public virtual void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) { - WriteCommentLine(output, TypeToString(type, includeNamespace: true)); + WriteCommentLine(output, TypeToString(type)); } public virtual void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) @@ -158,204 +158,9 @@ namespace ICSharpCode.ILSpy /// /// Converts a type definition, reference or specification into a string. This method is used by tree nodes and search results. /// - public virtual string TypeToString(IType type, bool includeNamespace) + public virtual string TypeToString(IType type, ConversionFlags conversionFlags = ConversionFlags.UseFullyQualifiedTypeNames | ConversionFlags.UseFullyQualifiedEntityNames) { - var visitor = new TypeToStringVisitor(includeNamespace); - type.AcceptVisitor(visitor); - return visitor.ToString(); - } - - class TypeToStringVisitor : TypeVisitor - { - readonly bool includeNamespace; - readonly StringBuilder builder; - - public override string ToString() - { - return builder.ToString(); - } - - public TypeToStringVisitor(bool includeNamespace) - { - this.includeNamespace = includeNamespace; - 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 (includeNamespace) - 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; - } + return new ILAmbience() { ConversionFlags = conversionFlags }.ConvertType(type); } #endregion @@ -377,72 +182,92 @@ namespace ICSharpCode.ILSpy return GetTooltip(entity); } + [Obsolete("Use EntityToString instead")] public virtual string FieldToString(IField field, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (field == null) throw new ArgumentNullException(nameof(field)); - return GetDisplayName(field, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName) + " : " + TypeToString(field.ReturnType, includeNamespace); + var flags = ConversionFlags.ShowTypeParameterList + | ConversionFlags.PlaceReturnTypeAfterParameterList + | ConversionFlags.ShowReturnType + | ConversionFlags.ShowParameterList + | ConversionFlags.ShowParameterModifiers; + if (includeDeclaringTypeName) + flags |= ConversionFlags.ShowDeclaringType; + if (includeNamespace) + flags |= ConversionFlags.UseFullyQualifiedTypeNames; + if (includeNamespaceOfDeclaringTypeName) + flags |= ConversionFlags.UseFullyQualifiedEntityNames; + return EntityToString(field, flags); } + [Obsolete("Use EntityToString instead")] public virtual string PropertyToString(IProperty property, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (property == null) throw new ArgumentNullException(nameof(property)); - return GetDisplayName(property, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName) + " : " + TypeToString(property.ReturnType, includeNamespace); + var flags = ConversionFlags.ShowTypeParameterList + | ConversionFlags.PlaceReturnTypeAfterParameterList + | ConversionFlags.ShowReturnType + | ConversionFlags.ShowParameterList + | ConversionFlags.ShowParameterModifiers; + if (includeDeclaringTypeName) + flags |= ConversionFlags.ShowDeclaringType; + if (includeNamespace) + flags |= ConversionFlags.UseFullyQualifiedTypeNames; + if (includeNamespaceOfDeclaringTypeName) + flags |= ConversionFlags.UseFullyQualifiedEntityNames; + return EntityToString(property, flags); } + [Obsolete("Use EntityToString instead")] public virtual string MethodToString(IMethod method, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (method == null) throw new ArgumentNullException(nameof(method)); - - int i = 0; - var buffer = new StringBuilder(); - buffer.Append(GetDisplayName(method, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName)); - var typeParameters = method.TypeParameters; - if (typeParameters.Count > 0) - { - buffer.Append("``"); - buffer.Append(typeParameters.Count); - buffer.Append('<'); - foreach (var tp in typeParameters) - { - if (i > 0) - buffer.Append(", "); - buffer.Append(tp.Name); - i++; - } - buffer.Append('>'); - } - buffer.Append('('); - - i = 0; - var parameters = method.Parameters; - foreach (var param in parameters) - { - if (i > 0) - buffer.Append(", "); - buffer.Append(TypeToString(param.Type, includeNamespace)); - i++; - } - buffer.Append(')'); - if (!method.IsConstructor) - { - buffer.Append(" : "); - buffer.Append(TypeToString(method.ReturnType, includeNamespace)); - } - return buffer.ToString(); + var flags = ConversionFlags.ShowTypeParameterList + | ConversionFlags.PlaceReturnTypeAfterParameterList + | ConversionFlags.ShowReturnType + | ConversionFlags.ShowParameterList + | ConversionFlags.ShowParameterModifiers; + if (includeDeclaringTypeName) + flags |= ConversionFlags.ShowDeclaringType; + if (includeNamespace) + flags |= ConversionFlags.UseFullyQualifiedTypeNames; + if (includeNamespaceOfDeclaringTypeName) + flags |= ConversionFlags.UseFullyQualifiedEntityNames; + return EntityToString(method, flags); } + [Obsolete("Use EntityToString instead")] public virtual string EventToString(IEvent @event, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) { if (@event == null) throw new ArgumentNullException(nameof(@event)); - var buffer = new StringBuilder(); - buffer.Append(GetDisplayName(@event, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName)); - buffer.Append(" : "); - buffer.Append(TypeToString(@event.ReturnType, includeNamespace)); - return buffer.ToString(); + var flags = ConversionFlags.ShowTypeParameterList + | ConversionFlags.PlaceReturnTypeAfterParameterList + | ConversionFlags.ShowReturnType + | ConversionFlags.ShowParameterList + | ConversionFlags.ShowParameterModifiers; + if (includeDeclaringTypeName) + flags |= ConversionFlags.ShowDeclaringType; + if (includeNamespace) + flags |= ConversionFlags.UseFullyQualifiedTypeNames; + if (includeNamespaceOfDeclaringTypeName) + flags |= ConversionFlags.UseFullyQualifiedEntityNames; + return EntityToString(@event, flags); + } + + public virtual string EntityToString(IEntity entity, ConversionFlags conversionFlags) + { + var ambience = new ILAmbience(); + ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList + | ConversionFlags.PlaceReturnTypeAfterParameterList + | ConversionFlags.ShowReturnType + | ConversionFlags.ShowParameterList + | ConversionFlags.ShowParameterModifiers + | conversionFlags; + return ambience.ConvertSymbol(entity); } protected string GetDisplayName(IEntity entity, bool includeDeclaringTypeName, bool includeNamespace, bool includeNamespaceOfDeclaringTypeName) @@ -452,17 +277,17 @@ namespace ICSharpCode.ILSpy { MetadataReader metadata = t.ParentModule.MetadataFile.Metadata; var typeDef = metadata.GetTypeDefinition((TypeDefinitionHandle)t.MetadataToken); - entityName = EscapeName(metadata.GetString(typeDef.Name)); + entityName = ILAmbience.EscapeName(metadata.GetString(typeDef.Name)); } else { - entityName = EscapeName(entity.Name); + entityName = ILAmbience.EscapeName(entity.Name); } if (includeNamespace || includeDeclaringTypeName) { if (entity.DeclaringTypeDefinition != null) - return TypeToString(entity.DeclaringTypeDefinition, includeNamespaceOfDeclaringTypeName) + "." + entityName; - return EscapeName(entity.Namespace) + "." + entityName; + return TypeToString(entity.DeclaringTypeDefinition, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames) + "." + entityName; + return ILAmbience.EscapeName(entity.Namespace) + "." + entityName; } else { @@ -493,14 +318,14 @@ namespace ICSharpCode.ILSpy { case HandleKind.TypeDefinition: if (fullName) - return EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(omitGenerics)); + return ILAmbience.EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(omitGenerics)); var td = metadata.GetTypeDefinition((TypeDefinitionHandle)handle); - return EscapeName(metadata.GetString(td.Name)); + return ILAmbience.EscapeName(metadata.GetString(td.Name)); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); if (fullName) - return EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(fd.Name)); - return EscapeName(metadata.GetString(fd.Name)); + return ILAmbience.EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(fd.Name)); + return ILAmbience.EscapeName(metadata.GetString(fd.Name)); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); string methodName = metadata.GetString(md.Name); @@ -511,20 +336,20 @@ namespace ICSharpCode.ILSpy methodName += "``" + genericParamCount; } if (fullName) - return EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + methodName); - return EscapeName(methodName); + return ILAmbience.EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + methodName); + return ILAmbience.EscapeName(methodName); case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); var declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(ed.Name)); - return EscapeName(metadata.GetString(ed.Name)); + return ILAmbience.EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(ed.Name)); + return ILAmbience.EscapeName(metadata.GetString(ed.Name)); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name)); - return EscapeName(metadata.GetString(pd.Name)); + return ILAmbience.EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name)); + return ILAmbience.EscapeName(metadata.GetString(pd.Name)); default: return null; } @@ -573,28 +398,5 @@ namespace ICSharpCode.ILSpy { return module.Metadata.MetadataVersion; } - - /// - /// Escape characters that cannot be displayed in the UI. - /// - 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; - } - - /// - /// Escape characters that cannot be displayed in the UI. - /// - public static string EscapeName(string name) - { - return EscapeName(new StringBuilder(name.Length), name).ToString(); - } } } diff --git a/ILSpy/Search/SearchResultFactory.cs b/ILSpy/Search/SearchResultFactory.cs index 4c12095ff..7fd1f6220 100644 --- a/ILSpy/Search/SearchResultFactory.cs +++ b/ILSpy/Search/SearchResultFactory.cs @@ -16,11 +16,11 @@ // 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.Composition; using System.Windows.Media; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpyX.Abstractions; @@ -65,15 +65,15 @@ namespace ICSharpCode.ILSpy.Search switch (member) { case ITypeDefinition t: - return language.TypeToString(t, false); + return language.TypeToString(t, ConversionFlags.None); case IField f: - return language.FieldToString(f, true, false, false); + return language.EntityToString(f, ConversionFlags.ShowDeclaringType); case IProperty p: - return language.PropertyToString(p, true, false, false); + return language.EntityToString(p, ConversionFlags.ShowDeclaringType); case IMethod m: - return language.MethodToString(m, true, false, false); + return language.EntityToString(m, ConversionFlags.ShowDeclaringType); case IEvent e: - return language.EventToString(e, true, false, false); + return language.EntityToString(e, ConversionFlags.ShowDeclaringType); default: throw new NotSupportedException(member?.GetType() + " not supported!"); } @@ -105,7 +105,7 @@ namespace ICSharpCode.ILSpy.Search Member = entity, Fitness = CalculateFitness(entity), Name = GetLanguageSpecificName(entity), - Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : entity.Namespace, + Location = declaringType != null ? language.TypeToString(declaringType, ConversionFlags.UseFullyQualifiedEntityNames | ConversionFlags.UseFullyQualifiedTypeNames) : entity.Namespace, Assembly = entity.ParentModule.FullAssemblyName, ToolTip = entity.ParentModule.MetadataFile?.FileName, Image = GetIcon(entity), diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 41ab3e54f..b0e90b699 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -23,6 +23,7 @@ using System.Windows.Threading; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Themes; @@ -60,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public AssemblyReference AssemblyReference => r; public override object Text { - get { return Language.EscapeName(r.Name) + GetSuffixString(r.Handle); } + get { return ILAmbience.EscapeName(r.Name) + GetSuffixString(r.Handle); } } public override object NavigationText => $"{Text} ({Properties.Resources.References})"; diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 3d1b8c20d..2150edbbd 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -28,6 +28,7 @@ using System.Windows.Documents; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AssemblyTree; @@ -286,20 +287,20 @@ namespace ICSharpCode.ILSpy.TreeNodes int decimalIndex = @namespace.LastIndexOf('.'); if (decimalIndex < 0) { - var escapedNamespace = Language.EscapeName(@namespace); + var escapedNamespace = ILAmbience.EscapeName(@namespace); ns = new NamespaceTreeNode(escapedNamespace); } else { var parentNamespaceTreeNode = GetOrCreateNamespaceTreeNode(@namespace.Substring(0, decimalIndex)); - var escapedInnerNamespace = Language.EscapeName(@namespace.Substring(decimalIndex + 1)); + var escapedInnerNamespace = ILAmbience.EscapeName(@namespace.Substring(decimalIndex + 1)); ns = new NamespaceTreeNode(escapedInnerNamespace); parentNamespaceTreeNode.Children.Add(ns); } } else { - var escapedNamespace = Language.EscapeName(@namespace); + var escapedNamespace = ILAmbience.EscapeName(@namespace); ns = new NamespaceTreeNode(escapedNamespace); } namespaces.Add(@namespace, ns); diff --git a/ILSpy/TreeNodes/BaseTypesEntryNode.cs b/ILSpy/TreeNodes/BaseTypesEntryNode.cs index 7d6168aff..a8a1288ca 100644 --- a/ILSpy/TreeNodes/BaseTypesEntryNode.cs +++ b/ILSpy/TreeNodes/BaseTypesEntryNode.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.type = type; } - public override object Text => this.Language.TypeToString(type, includeNamespace: true); + public override object Text => this.Language.TypeToString(type); public override object NavigationText => $"{Text} ({Properties.Resources.BaseTypes})"; @@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); + language.WriteCommentLine(output, language.TypeToString(type)); } IEntity IMemberTreeNode.Member => type; diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index d19184ffa..488898297 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -46,7 +46,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Text => Properties.Resources.BaseTypes; - public override object NavigationText => $"{Text} ({this.Language.TypeToString(type, includeNamespace: true)})"; + public override object NavigationText => $"{Text} ({this.Language.TypeToString(type)})"; public override object Icon => Images.SuperTypes; diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index 3e511b704..290b6bc5c 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override bool ShowExpander => !type.IsSealed && base.ShowExpander; public override object Text { - get { return Language.TypeToString(type, includeNamespace: true) + GetSuffixString(type.MetadataToken); } + get { return Language.TypeToString(type) + GetSuffixString(type.MetadataToken); } } public override object NavigationText => $"{Text} ({Properties.Resources.DerivedTypes})"; @@ -98,7 +98,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); + language.WriteCommentLine(output, language.TypeToString(type)); } IEntity IMemberTreeNode.Member => type; diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index fdf250030..ddddf0644 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -46,7 +46,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Text => Resources.DerivedTypes; - public override object NavigationText => $"{Text} ({this.Language.TypeToString(type, includeNamespace: true)})"; + public override object NavigationText => $"{Text} ({this.Language.TypeToString(type)})"; public override object Icon => Images.SubTypes; diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 1c10674e9..434630af2 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -24,6 +24,7 @@ using ICSharpCode.Decompiler; namespace ICSharpCode.ILSpy.TreeNodes { + using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; @@ -60,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public static object GetText(IEvent ev, Language language, bool includeDeclaringTypeName = false) { - return language.EventToString(ev, includeDeclaringTypeName, false, false); + return language.EntityToString(ev, includeDeclaringTypeName ? ConversionFlags.ShowDeclaringType : ConversionFlags.None); } public override object Icon => GetIcon(GetEventDefinition()); diff --git a/ILSpy/TreeNodes/ExportedTypeTreeNode.cs b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs index 7be2765d9..7d765a3c5 100644 --- a/ILSpy/TreeNodes/ExportedTypeTreeNode.cs +++ b/ILSpy/TreeNodes/ExportedTypeTreeNode.cs @@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } public override object Text - => Language.TypeToString(resolvedType, includeNamespace: true) + GetSuffixString(r.Handle); + => Language.TypeToString(resolvedType) + GetSuffixString(r.Handle); public override object Icon => Images.ExportedType; @@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.WriteCommentLine(output, $"{Language.TypeToString(resolvedType, includeNamespace: true)} (Exported, IsForwarder: {r.IsForwarder}, Attributes: {(int)r.Attributes:X8})"); + language.WriteCommentLine(output, $"{Language.TypeToString(resolvedType)} (Exported, IsForwarder: {r.IsForwarder}, Attributes: {(int)r.Attributes:X8})"); } } } diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 5bf508aaf..271041bbe 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -24,6 +24,7 @@ using ICSharpCode.Decompiler; namespace ICSharpCode.ILSpy.TreeNodes { + using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; @@ -52,7 +53,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public static object GetText(IField field, Language language, bool includeDeclaringTypeName = false) { - return language.FieldToString(field, includeDeclaringTypeName, includeNamespace: false, includeNamespaceOfDeclaringTypeName: false); + return language.EntityToString(field, includeDeclaringTypeName ? ConversionFlags.ShowDeclaringType : ConversionFlags.None); } public override object Icon => GetIcon(GetFieldDefinition()); diff --git a/ILSpy/TreeNodes/MemberReferenceTreeNode.cs b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs index 4d4598a8d..e7f81e166 100644 --- a/ILSpy/TreeNodes/MemberReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/MemberReferenceTreeNode.cs @@ -23,6 +23,7 @@ using System.Reflection.Metadata; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.TreeNodes @@ -57,7 +58,7 @@ namespace ICSharpCode.ILSpy.TreeNodes _ => throw new NotSupportedException(), }; - public string Signature => resolvedMember is IMethod m ? Language.MethodToString(m, false, false, false) : Language.FieldToString((IField)resolvedMember, false, false, false); + public string Signature => Language.EntityToString(resolvedMember, ConversionFlags.None); public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index fc6d44ce6..c01355db1 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -24,6 +24,7 @@ using ICSharpCode.Decompiler; namespace ICSharpCode.ILSpy.TreeNodes { + using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; @@ -52,7 +53,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public static object GetText(IMethod method, Language language, bool includeDeclaringTypeName = false) { - return language.MethodToString(method, includeDeclaringTypeName, false, false); + return language.EntityToString(method, includeDeclaringTypeName ? ConversionFlags.ShowDeclaringType : ConversionFlags.None); } public override object Icon => GetIcon(GetMethodDefinition()); @@ -108,7 +109,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override string ToString() { - return LanguageService.ILLanguage.MethodToString(MethodDefinition, false, false, false); + return LanguageService.ILLanguage.EntityToString(MethodDefinition, ConversionFlags.None); } } } diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index 57e91cee8..09b1c8ff6 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -20,6 +20,7 @@ using System; using System.Reflection.Metadata; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; @@ -49,7 +50,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.module = module ?? throw new ArgumentNullException(nameof(module)); this.metadata = module.Metadata; this.reference = module.Metadata.GetModuleReference(r); - this.moduleName = Language.EscapeName(metadata.GetString(reference.Name)); + this.moduleName = ILAmbience.EscapeName(metadata.GetString(reference.Name)); foreach (var h in metadata.AssemblyFiles) { diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 255299f8c..5f9c2c07f 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -24,6 +24,7 @@ using ICSharpCode.Decompiler; namespace ICSharpCode.ILSpy.TreeNodes { + using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; @@ -62,7 +63,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public static object GetText(IProperty property, Language language, bool includeDeclaringTypeName = false) { - return language.PropertyToString(property, includeDeclaringTypeName, false, false); + return language.EntityToString(property, includeDeclaringTypeName ? ConversionFlags.ShowDeclaringType : ConversionFlags.None); } public override object Icon => GetIcon(GetPropertyDefinition()); @@ -106,7 +107,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override string ToString() { - return LanguageService.ILLanguage.PropertyToString(PropertyDefinition, false, false, false); + return LanguageService.ILLanguage.EntityToString(PropertyDefinition, ConversionFlags.None); } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index e20257537..bea90056d 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -21,6 +21,7 @@ using System.IO; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using Microsoft.Win32; @@ -35,7 +36,7 @@ namespace ICSharpCode.ILSpy.TreeNodes private readonly string key; private readonly Func openStream; - public override object Text => Language.EscapeName(key); + public override object Text => ILAmbience.EscapeName(key); public override object Icon => Images.Resource; diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index a0df8f29e..8e757f2f4 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -25,6 +25,7 @@ using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; +using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; @@ -51,7 +52,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public Resource Resource { get; } - public override object Text => Language.EscapeName(Resource.Name); + public override object Text => ILAmbience.EscapeName(Resource.Name); public override object Icon => Images.Resource; diff --git a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs index 6a47c4767..e05cabf19 100644 --- a/ILSpy/TreeNodes/TypeReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/TypeReferenceTreeNode.cs @@ -20,6 +20,7 @@ using System; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpy.TreeNodes @@ -43,7 +44,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } public override object Text - => Language.TypeToString(resolvedType, includeNamespace: false) + GetSuffixString(r.Handle); + => Language.TypeToString(resolvedType, ConversionFlags.None) + GetSuffixString(r.Handle); public override object NavigationText => $"{Text} ({Properties.Resources.ReferencedTypes})"; @@ -62,7 +63,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.WriteCommentLine(output, Language.TypeToString(resolvedType, includeNamespace: true)); + language.WriteCommentLine(output, Language.TypeToString(resolvedType)); EnsureLazyChildren(); foreach (ILSpyTreeNode child in Children) { diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 343f8f3df..5502cab46 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -26,6 +26,7 @@ using SRM = System.Reflection.Metadata; namespace ICSharpCode.ILSpy.TreeNodes { + using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; @@ -42,10 +43,10 @@ namespace ICSharpCode.ILSpy.TreeNodes public AssemblyTreeNode ParentAssemblyNode { get; } - public override object Text => this.Language.TypeToString(GetTypeDefinition(), includeNamespace: false) + public override object Text => this.Language.TypeToString(GetTypeDefinition(), ConversionFlags.None) + GetSuffixString(TypeDefinition.MetadataToken); - public override object NavigationText => this.Language.TypeToString(GetTypeDefinition(), includeNamespace: true) + public override object NavigationText => this.Language.TypeToString(GetTypeDefinition()) + GetSuffixString(TypeDefinition.MetadataToken); private ITypeDefinition GetTypeDefinition() diff --git a/ILSpy/ViewModels/CompareViewModel.cs b/ILSpy/ViewModels/CompareViewModel.cs index 163650ce8..9a038ba66 100644 --- a/ILSpy/ViewModels/CompareViewModel.cs +++ b/ILSpy/ViewModels/CompareViewModel.cs @@ -41,6 +41,7 @@ using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.ViewModels { + using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using TomsToolbox.Wpf; @@ -217,11 +218,8 @@ namespace ICSharpCode.ILSpy.ViewModels return result; string? GetEntityText(ISymbol? symbol) => symbol switch { - ITypeDefinition t => this.assemblyTreeModel.CurrentLanguage.TypeToString(t, includeNamespace: true), - IMethod m => this.assemblyTreeModel.CurrentLanguage.MethodToString(m, false, false, false), - IField f => this.assemblyTreeModel.CurrentLanguage.FieldToString(f, false, false, false), - IProperty p => this.assemblyTreeModel.CurrentLanguage.PropertyToString(p, false, false, false), - IEvent e => this.assemblyTreeModel.CurrentLanguage.EventToString(e, false, false, false), + ITypeDefinition t => this.assemblyTreeModel.CurrentLanguage.TypeToString(t), + IEntity m => this.assemblyTreeModel.CurrentLanguage.EntityToString(m, ConversionFlags.None), INamespace n => n.FullName, IModule m => m.FullAssemblyName, _ => null, @@ -301,7 +299,7 @@ namespace ICSharpCode.ILSpy.ViewModels var module = (MetadataModule)typeSystem.MainModule!; var metadata = module.MetadataFile.Metadata; var ambience = new CSharpAmbience(); - ambience.ConversionFlags = ICSharpCode.Decompiler.Output.ConversionFlags.All & ~ICSharpCode.Decompiler.Output.ConversionFlags.ShowDeclaringType; + ambience.ConversionFlags = ConversionFlags.All & ~ConversionFlags.ShowDeclaringType; List results = new(); Dictionary typeEntries = new(); @@ -651,11 +649,8 @@ namespace ICSharpCode.ILSpy.ViewModels return entityText + (otherText != null && entityText != otherText ? " -> " + otherText : ""); string? GetEntityText(ISymbol? symbol) => symbol switch { - ITypeDefinition t => this.Language.TypeToString(t, includeNamespace: false) + GetSuffixString(t.MetadataToken), - IMethod m => this.Language.MethodToString(m, false, false, false) + GetSuffixString(m.MetadataToken), - IField f => this.Language.FieldToString(f, false, false, false) + GetSuffixString(f.MetadataToken), - IProperty p => this.Language.PropertyToString(p, false, false, false) + GetSuffixString(p.MetadataToken), - IEvent e => this.Language.EventToString(e, false, false, false) + GetSuffixString(e.MetadataToken), + ITypeDefinition t => this.Language.TypeToString(t, ConversionFlags.None) + GetSuffixString(t.MetadataToken), + IEntity e => this.Language.EntityToString(e, ConversionFlags.All & ~(ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames | ConversionFlags.UseFullyQualifiedTypeNames)) + GetSuffixString(e.MetadataToken), INamespace n => n.FullName, IModule m => m.FullAssemblyName, _ => null,