diff --git a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs index 3e71a38b7..3231e7f96 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs @@ -6,9 +6,9 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Tests.Helpers { - class RemoveCompilerAttribute : DepthFirstAstVisitor, IAstTransform + class RemoveCompilerAttribute : DepthFirstAstVisitor, IAstTransform { - public override object VisitAttribute(CSharp.Syntax.Attribute attribute, object data) + public override void VisitAttribute(CSharp.Syntax.Attribute attribute) { var section = (AttributeSection)attribute.Parent; SimpleType type = attribute.Type as SimpleType; @@ -25,16 +25,15 @@ namespace ICSharpCode.Decompiler.Tests.Helpers if (section.Attributes.Count == 0) section.Remove(); } - return null; } public void Run(AstNode rootNode, TransformContext context) { - rootNode.AcceptVisitor(this, null); + rootNode.AcceptVisitor(this); } } - public class RemoveEmbeddedAtttributes : DepthFirstAstVisitor, IAstTransform + public class RemoveEmbeddedAtttributes : DepthFirstAstVisitor, IAstTransform { HashSet attributeNames = new HashSet() { "System.Runtime.CompilerServices.IsReadOnlyAttribute", @@ -42,21 +41,20 @@ namespace ICSharpCode.Decompiler.Tests.Helpers "Microsoft.CodeAnalysis.EmbeddedAttribute", }; - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { var typeDefinition = typeDeclaration.GetSymbol() as ITypeDefinition; if (typeDefinition == null || !attributeNames.Contains(typeDefinition.FullName)) - return null; + return; if (typeDeclaration.Parent is NamespaceDeclaration ns && ns.Members.Count == 1) ns.Remove(); else typeDeclaration.Remove(); - return null; } public void Run(AstNode rootNode, TransformContext context) { - rootNode.AcceptVisitor(this, null); + rootNode.AcceptVisitor(this); } } } diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index ebf27632d..f7927ac17 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -178,7 +178,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "mscorlib.dll")), MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.dll")), MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Core.dll")), - MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Xml.dll")) + MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Xml.dll")), + MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location) }; }); diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index f180b3e3f..8083f86e2 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -70,6 +70,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 3eb4a63c0..80c06925b 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -279,6 +279,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions); } + [Test] + public void TupleTypes([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void Issue1080([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs new file mode 100644 index 000000000..c0e6b6812 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// 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; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public class TupleTypes + { + public ValueTuple VT0; + public ValueTuple VT1; + public ValueTuple VT7EmptyRest; + + public (int, uint) Unnamed2; + public (int, int, int) Unnamed3; + public (int, int, int, int) Unnamed4; + public (int, int, int, int, int) Unnamed5; + public (int, int, int, int, int, int) Unnamed6; + public (int, int, int, int, int, int, int) Unnamed7; + public (int, int, int, int, int, int, int, int) Unnamed8; + + public (int a, uint b) Named2; + public (int a, uint b)[] Named2Array; + public (int a, int b, int c, int d, int e, int f, int g, int h) Named8; + + public (int, int a, int, int b, int) PartiallyNamed; + + public ((int a, int b) x, (int, int) y, (int c, int d) z) Nested1; + public ((object a, dynamic b), dynamic, (dynamic c, object d)) Nested2; + public (ValueTuple a, (int x1, int x2), ValueTuple b, (int y1, int y2), (int, int) c) Nested3; + public (int a, int b, int c, int d, int e, int f, int g, int h, (int i, int j)) Nested4; + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il new file mode 100644 index 000000000..4fee98e01 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il @@ -0,0 +1,91 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly TupleTypes +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module TupleTypes.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTypes + extends [mscorlib]System.Object +{ + .field public valuetype [mscorlib]System.ValueTuple VT0 + .field public valuetype [mscorlib]System.ValueTuple`1 VT1 + .field public valuetype [mscorlib]System.ValueTuple`8 VT7EmptyRest + .field public valuetype [mscorlib]System.ValueTuple`2 Unnamed2 + .field public valuetype [mscorlib]System.ValueTuple`3 Unnamed3 + .field public valuetype [mscorlib]System.ValueTuple`4 Unnamed4 + .field public valuetype [mscorlib]System.ValueTuple`5 Unnamed5 + .field public valuetype [mscorlib]System.ValueTuple`6 Unnamed6 + .field public valuetype [mscorlib]System.ValueTuple`7 Unnamed7 + .field public valuetype [mscorlib]System.ValueTuple`8> Unnamed8 + .field public valuetype [mscorlib]System.ValueTuple`2 Named2 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`2[] Named2Array + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`8> Named8 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF 00 00 ) // .f.g.h... + .field public valuetype [mscorlib]System.ValueTuple`5 PartiallyNamed + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 05 00 00 00 FF 01 61 FF 01 62 FF 00 00 ) // ........a..b... + .field public valuetype [mscorlib]System.ValueTuple`3,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested1 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 78 01 79 01 7A 01 61 01 62 // .......x.y.z.a.b + FF FF 01 63 01 64 00 00 ) // ...c.d.. + .field public valuetype [mscorlib]System.ValueTuple`3,object,valuetype [mscorlib]System.ValueTuple`2> Nested2 + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor(bool[]) = ( 01 00 08 00 00 00 00 00 00 01 01 00 01 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 07 00 00 00 FF FF FF 01 61 01 62 01 63 01 // ..........a.b.c. + 64 00 00 ) // d.. + .field public valuetype [mscorlib]System.ValueTuple`5,valuetype [mscorlib]System.ValueTuple`1,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested3 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0C 00 00 00 01 61 FF 01 62 FF 01 63 02 78 // .......a..b..c.x + 31 02 78 32 FF 02 79 31 02 79 32 FF FF 00 00 ) // 1.x2..y1.y2.... + .field public valuetype [mscorlib]System.ValueTuple`8>> Nested4 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0D 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF FF FF 01 69 01 6A 00 00 ) // .f.g.h....i.j.. + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method TupleTypes::.ctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTypes + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il new file mode 100644 index 000000000..08640c71e --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il @@ -0,0 +1,92 @@ + + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly TupleTypes +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module TupleTypes.dll +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTypes + extends [mscorlib]System.Object +{ + .field public valuetype [mscorlib]System.ValueTuple VT0 + .field public valuetype [mscorlib]System.ValueTuple`1 VT1 + .field public valuetype [mscorlib]System.ValueTuple`8 VT7EmptyRest + .field public valuetype [mscorlib]System.ValueTuple`2 Unnamed2 + .field public valuetype [mscorlib]System.ValueTuple`3 Unnamed3 + .field public valuetype [mscorlib]System.ValueTuple`4 Unnamed4 + .field public valuetype [mscorlib]System.ValueTuple`5 Unnamed5 + .field public valuetype [mscorlib]System.ValueTuple`6 Unnamed6 + .field public valuetype [mscorlib]System.ValueTuple`7 Unnamed7 + .field public valuetype [mscorlib]System.ValueTuple`8> Unnamed8 + .field public valuetype [mscorlib]System.ValueTuple`2 Named2 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`2[] Named2Array + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`8> Named8 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF 00 00 ) // .f.g.h... + .field public valuetype [mscorlib]System.ValueTuple`5 PartiallyNamed + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 05 00 00 00 FF 01 61 FF 01 62 FF 00 00 ) // ........a..b... + .field public valuetype [mscorlib]System.ValueTuple`3,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested1 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 78 01 79 01 7A 01 61 01 62 // .......x.y.z.a.b + FF FF 01 63 01 64 00 00 ) // ...c.d.. + .field public valuetype [mscorlib]System.ValueTuple`3,object,valuetype [mscorlib]System.ValueTuple`2> Nested2 + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor(bool[]) = ( 01 00 08 00 00 00 00 00 00 01 01 00 01 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 07 00 00 00 FF FF FF 01 61 01 62 01 63 01 // ..........a.b.c. + 64 00 00 ) // d.. + .field public valuetype [mscorlib]System.ValueTuple`5,valuetype [mscorlib]System.ValueTuple`1,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested3 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0C 00 00 00 01 61 FF 01 62 FF 01 63 02 78 // .......a..b..c.x + 31 02 78 32 FF 02 79 31 02 79 32 FF FF 00 00 ) // 1.x2..y1.y2.... + .field public valuetype [mscorlib]System.ValueTuple`8>> Nested4 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0D 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF FF FF 01 69 01 6A 00 00 ) // .f.g.h....i.j.. + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TupleTypes::.ctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTypes + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 5f2d2553e..923be81c0 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -187,7 +187,7 @@ namespace ICSharpCode.Decompiler.CSharp } public CSharpDecompiler(ModuleDefinition module, DecompilerSettings settings) - : this(new DecompilerTypeSystem(module), settings) + : this(new DecompilerTypeSystem(module, settings), settings) { } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 4b9263b7c..1e471358b 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -445,6 +445,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { foreach (CSharpModifierToken modifier in modifierTokens) { modifier.AcceptVisitor(this); + Space(); } } @@ -2283,28 +2284,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitTupleType(TupleAstType tupleType) { - Debug.Assert(tupleType.ElementTypes.Count >= 2); + Debug.Assert(tupleType.Elements.Count >= 2); StartNode(tupleType); LPar(); - if (tupleType.ElementNames.Any()) { - bool isFirst = true; - foreach (var (type, name) in tupleType.ElementTypes.Zip(tupleType.ElementNames)) { - if (isFirst) { - isFirst = false; - } else { - Comma(type); - } - type.AcceptVisitor(this); - Space(); - name.AcceptVisitor(this); - } - } else { - WriteCommaSeparatedList(tupleType.ElementTypes); - } + WriteCommaSeparatedList(tupleType.Elements); RPar(); EndNode(tupleType); } + public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement) + { + StartNode(tupleTypeElement); + tupleTypeElement.Type.AcceptVisitor(this); + if (!tupleTypeElement.NameToken.IsNull) { + Space(); + tupleTypeElement.NameToken.AcceptVisitor(this); + } + EndNode(tupleTypeElement); + } + public virtual void VisitComposedType(ComposedType composedType) { StartNode(composedType); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs index a281563c2..44add8be6 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs @@ -121,6 +121,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax VisitChildren(tupleType); } + public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement) + { + VisitChildren (tupleTypeElement); + } + public virtual void VisitAttribute (Attribute attribute) { VisitChildren (attribute); @@ -762,7 +767,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { return VisitChildren (tupleType); } - + + public virtual T VisitTupleTypeElement(TupleTypeElement tupleTypeElement) + { + return VisitChildren (tupleTypeElement); + } + public virtual T VisitAttribute (Attribute attribute) { return VisitChildren (attribute); @@ -1405,6 +1415,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return VisitChildren (tupleType, data); } + public virtual S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data) + { + return VisitChildren (tupleTypeElement, data); + } + public virtual S VisitAttribute (Attribute attribute, T data) { return VisitChildren (attribute, data); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs index 514213e7d..2d5bd8760 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs @@ -135,6 +135,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax void VisitSimpleType(SimpleType simpleType); void VisitMemberType(MemberType memberType); void VisitTupleType(TupleAstType tupleType); + void VisitTupleTypeElement(TupleTypeElement tupleTypeElement); void VisitComposedType(ComposedType composedType); void VisitArraySpecifier(ArraySpecifier arraySpecifier); void VisitPrimitiveType(PrimitiveType primitiveType); @@ -275,6 +276,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax S VisitSimpleType(SimpleType simpleType); S VisitMemberType(MemberType memberType); S VisitTupleType(TupleAstType tupleType); + S VisitTupleTypeElement(TupleTypeElement tupleTypeElement); S VisitComposedType(ComposedType composedType); S VisitArraySpecifier(ArraySpecifier arraySpecifier); S VisitPrimitiveType(PrimitiveType primitiveType); @@ -415,6 +417,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax S VisitSimpleType(SimpleType simpleType, T data); S VisitMemberType(MemberType memberType, T data); S VisitTupleType(TupleAstType tupleType, T data); + S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data); S VisitComposedType(ComposedType composedType, T data); S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data); S VisitPrimitiveType(PrimitiveType primitiveType, T data); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs index b8e0d08b5..4a34830aa 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Immutable; +using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.TypeSystem; @@ -25,14 +27,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { public class TupleAstType : AstType { - public AstNodeCollection ElementTypes { - get { return GetChildrenByRole(Roles.TypeArgument); } - } + public static readonly Role ElementRole = new Role("Element", TupleTypeElement.Null); - public AstNodeCollection ElementNames { - get { return GetChildrenByRole(Roles.Identifier); } + public AstNodeCollection Elements { + get { return GetChildrenByRole(ElementRole); } } - + public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitTupleType(this); @@ -50,14 +50,90 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) { - throw new NotSupportedException(); + return new TupleTypeReference( + this.Elements.Select(e => e.Type.ToTypeReference(lookupMode, interningProvider)).ToImmutableArray(), + this.Elements.Select(e => e.Name).ToImmutableArray() + ); + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + return other is TupleAstType o && Elements.DoMatch(o.Elements, match); + } + } + + public class TupleTypeElement : AstNode + { + #region Null + public new static readonly TupleTypeElement Null = new TupleTypeElement(); + + sealed class NullTupleTypeElement : TupleTypeElement + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + public AstType Type { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public string Name { + get { return GetChildByRole(Roles.Identifier).Name; } + set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } + } + + public Identifier NameToken { + get { return GetChildByRole(Roles.Identifier); } + set { SetChildByRole(Roles.Identifier, value); } + } + + public override NodeType NodeType => NodeType.Unknown; + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitTupleTypeElement(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitTupleTypeElement(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitTupleTypeElement(this, data); } protected internal override bool DoMatch(AstNode other, Match match) { - return other is TupleAstType o - && ElementTypes.DoMatch(o.ElementTypes, match) - && ElementNames.DoMatch(o.ElementNames, match); + return other is TupleTypeElement o + && Type.DoMatch(o.Type, match) + && MatchString(Name, o.Name); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 9f51a9f07..d8e7efc96 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -239,13 +239,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } if (type is TupleType tuple) { var astType = new TupleAstType(); - if (tuple.HasCustomElementNames) { - foreach (var (etype, ename) in tuple.TupleElementTypes.Zip(tuple.TupleElementNames)) { - astType.ElementTypes.Add(ConvertType(etype)); - astType.ElementNames.Add(Identifier.Create(ename)); - } - } else { - astType.ElementTypes.AddRange(tuple.TupleElementTypes.Select(ConvertType)); + foreach (var (etype, ename) in tuple.ElementTypes.Zip(tuple.ElementNames)) { + astType.Elements.Add(new TupleTypeElement { + Type = ConvertType(etype), + Name = ename + }); } return astType; } diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 8d8030f0c..416e8aa7e 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -59,7 +59,7 @@ namespace ICSharpCode.Decompiler expressionTrees = false; } if (languageVersion < CSharp.LanguageVersion.CSharp4) { - // * dynamic (not supported yet) + dynamic = false; // * named and optional arguments (not supported yet) } if (languageVersion < CSharp.LanguageVersion.CSharp5) { @@ -74,6 +74,8 @@ namespace ICSharpCode.Decompiler } if (languageVersion < CSharp.LanguageVersion.CSharp7) { outVariables = false; + tupleTypes = false; + tupleConversions = false; discards = false; } if (languageVersion < CSharp.LanguageVersion.CSharp7_2) { @@ -81,7 +83,7 @@ namespace ICSharpCode.Decompiler } if (languageVersion < CSharp.LanguageVersion.CSharp7_3) { //introduceUnmanagedTypeConstraint = false; - //... + tupleComparisons = false; } } @@ -145,6 +147,21 @@ namespace ICSharpCode.Decompiler } } + bool dynamic = true; + + /// + /// Decompile use of the 'dynamic' type. + /// + public bool Dynamic { + get { return dynamic; } + set { + if (dynamic != value) { + dynamic = value; + OnPropertyChanged(); + } + } + } + bool asyncAwait = true; /// @@ -571,6 +588,53 @@ namespace ICSharpCode.Decompiler } } + bool tupleTypes = true; + + /// + /// Gets/Sets whether tuple type syntax (int, string) + /// should be used for System.ValueTuple. + /// + public bool TupleTypes { + get { return tupleTypes; } + set { + if (tupleTypes != value) { + tupleTypes = value; + OnPropertyChanged(); + } + } + } + + bool tupleConversions = true; + + /// + /// Gets/Sets whether implicit conversions between tuples + /// should be used in the decompiled output. + /// + public bool TupleConversions { + get { return tupleConversions; } + set { + if (tupleConversions != value) { + tupleConversions = value; + OnPropertyChanged(); + } + } + } + + bool tupleComparisons = true; + + /// + /// Gets/Sets whether tuple comparisons should be detected. + /// + public bool TupleComparisons { + get { return tupleComparisons; } + set { + if (tupleComparisons != value) { + tupleComparisons = value; + OnPropertyChanged(); + } + } + } + #region Options to aid VB decompilation bool assumeArrayLengthFitsIntoInt32 = true; diff --git a/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs index 901bea2fd..0d3629220 100644 --- a/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -38,13 +39,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// if you want to load multiple project contents in parallel. public sealed class CecilLoader : AssemblyLoader { - /// - /// Version number of the cecil loader. - /// Should be incremented when fixing bugs in the cecil loader so that project contents cached on disk - /// (which might be incorrect due to the bug) are re-created. - /// - const int cecilLoaderVersion = 1; - #region Options // Most options are defined in the AssemblyLoader base class @@ -64,6 +58,16 @@ namespace ICSharpCode.Decompiler.TypeSystem /// public bool LazyLoad { get; set; } + /// + /// Gets/Sets whether to use the dynamic type. + /// + public bool UseDynamicType { get; set; } = true; + + /// + /// Gets/Sets whether to use the tuple types. + /// + public bool UseTupleTypes { get; set; } = true; + /// /// This delegate gets executed whenever an entity was loaded. /// @@ -75,20 +79,11 @@ namespace ICSharpCode.Decompiler.TypeSystem /// public Action OnEntityLoaded { get; set; } - bool shortenInterfaceImplNames = true; - /// /// Specifies whether method names of explicit interface-implementations should be shortened. /// /// This is important when working with parser-initialized type-systems in order to be consistent. - public bool ShortenInterfaceImplNames { - get { - return shortenInterfaceImplNames; - } - set { - shortenInterfaceImplNames = value; - } - } + public bool ShortenInterfaceImplNames { get; set; } = true; #endregion ModuleDefinition currentModule; @@ -108,6 +103,8 @@ namespace ICSharpCode.Decompiler.TypeSystem { // use a shared typeSystemTranslationTable this.IncludeInternalMembers = loader.IncludeInternalMembers; + this.UseDynamicType = loader.UseDynamicType; + this.UseTupleTypes = loader.UseTupleTypes; this.LazyLoad = loader.LazyLoad; this.OnEntityLoaded = loader.OnEntityLoaded; this.ShortenInterfaceImplNames = loader.ShortenInterfaceImplNames; @@ -295,11 +292,12 @@ namespace ICSharpCode.Decompiler.TypeSystem /// IsValueType is set correctly. public ITypeReference ReadTypeReference(TypeReference type, ICustomAttributeProvider typeAttributes = null, bool isFromSignature = false) { - int typeIndex = 0; - return CreateType(type, typeAttributes, ref typeIndex, isFromSignature); + int dynamicTypeIndex = 0; + int tupleTypeIndex = 0; + return CreateType(type, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature); } - ITypeReference CreateType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex, bool isFromSignature) + ITypeReference CreateType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int dynamicTypeIndex, ref int tupleTypeIndex, bool isFromSignature) { if (type == null) { return SpecialType.UnknownType; @@ -307,90 +305,120 @@ namespace ICSharpCode.Decompiler.TypeSystem switch (type.MetadataType) { case MetadataType.Void: - return KnownTypeReference.Get(KnownTypeCode.Void); + return KnownTypeReference.Void; case MetadataType.Boolean: - return KnownTypeReference.Get(KnownTypeCode.Boolean); + return KnownTypeReference.Boolean; case MetadataType.Char: - return KnownTypeReference.Get(KnownTypeCode.Char); + return KnownTypeReference.Char; case MetadataType.SByte: - return KnownTypeReference.Get(KnownTypeCode.SByte); + return KnownTypeReference.SByte; case MetadataType.Byte: - return KnownTypeReference.Get(KnownTypeCode.Byte); + return KnownTypeReference.Byte; case MetadataType.Int16: - return KnownTypeReference.Get(KnownTypeCode.Int16); + return KnownTypeReference.Int16; case MetadataType.UInt16: - return KnownTypeReference.Get(KnownTypeCode.UInt16); + return KnownTypeReference.UInt16; case MetadataType.Int32: - return KnownTypeReference.Get(KnownTypeCode.Int32); + return KnownTypeReference.Int32; case MetadataType.UInt32: - return KnownTypeReference.Get(KnownTypeCode.UInt32); + return KnownTypeReference.UInt32; case MetadataType.Int64: - return KnownTypeReference.Get(KnownTypeCode.Int64); + return KnownTypeReference.Int64; case MetadataType.UInt64: - return KnownTypeReference.Get(KnownTypeCode.UInt64); + return KnownTypeReference.UInt64; case MetadataType.Single: - return KnownTypeReference.Get(KnownTypeCode.Single); + return KnownTypeReference.Single; case MetadataType.Double: - return KnownTypeReference.Get(KnownTypeCode.Double); + return KnownTypeReference.Double; case MetadataType.String: - return KnownTypeReference.Get(KnownTypeCode.String); + return KnownTypeReference.String; case MetadataType.Pointer: - typeIndex++; + dynamicTypeIndex++; return interningProvider.Intern( new PointerTypeReference( CreateType( (type as Mono.Cecil.PointerType).ElementType, - typeAttributes, ref typeIndex, isFromSignature: true))); + typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true))); case MetadataType.ByReference: - typeIndex++; + dynamicTypeIndex++; return interningProvider.Intern( new ByReferenceTypeReference( CreateType( (type as Mono.Cecil.ByReferenceType).ElementType, - typeAttributes, ref typeIndex, isFromSignature: true))); + typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true))); case MetadataType.Var: return TypeParameterReference.Create(SymbolKind.TypeDefinition, ((GenericParameter)type).Position); case MetadataType.MVar: return TypeParameterReference.Create(SymbolKind.Method, ((GenericParameter)type).Position); case MetadataType.Array: - typeIndex++; + dynamicTypeIndex++; return interningProvider.Intern( new ArrayTypeReference( CreateType( (type as Mono.Cecil.ArrayType).ElementType, - typeAttributes, ref typeIndex, isFromSignature: true), + typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true), (type as Mono.Cecil.ArrayType).Rank)); case MetadataType.GenericInstance: GenericInstanceType gType = (GenericInstanceType)type; - ITypeReference baseType = CreateType(gType.ElementType, typeAttributes, ref typeIndex, isFromSignature: true); + ITypeReference baseType = CreateType(gType.ElementType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); + if (UseTupleTypes && IsValueTuple(gType, out int tupleCardinality)) { + if (tupleCardinality > 1) { + var elementNames = GetTupleElementNames(typeAttributes, tupleTypeIndex, tupleCardinality); + tupleTypeIndex += tupleCardinality; + ITypeReference[] elementTypeRefs = new ITypeReference[tupleCardinality]; + int outPos = 0; + do { + int normalArgCount = Math.Min(gType.GenericArguments.Count, TupleType.RestPosition - 1); + for (int i = 0; i < normalArgCount; i++) { + dynamicTypeIndex++; + elementTypeRefs[outPos++] = CreateType(gType.GenericArguments[i], typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); + } + if (gType.GenericArguments.Count == TupleType.RestPosition) { + gType = (GenericInstanceType)gType.GenericArguments.Last(); + dynamicTypeIndex++; + if (IsValueTuple(gType, out int nestedCardinality)) { + tupleTypeIndex += nestedCardinality; + } else { + Debug.Fail("TRest should be another value tuple"); + } + } else { + gType = null; + } + } while (gType != null); + return new TupleTypeReference(elementTypeRefs.ToImmutableArray(), elementNames); + } else { + // C# doesn't have syntax for tuples of cardinality <= 1 + tupleTypeIndex += tupleCardinality; + } + } ITypeReference[] para = new ITypeReference[gType.GenericArguments.Count]; for (int i = 0; i < para.Length; ++i) { - typeIndex++; - para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref typeIndex, isFromSignature: true); + dynamicTypeIndex++; + para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); } return interningProvider.Intern(new ParameterizedTypeReference(baseType, para)); case MetadataType.IntPtr: - return KnownTypeReference.Get(KnownTypeCode.IntPtr); + return KnownTypeReference.IntPtr; case MetadataType.UIntPtr: - return KnownTypeReference.Get(KnownTypeCode.UIntPtr); + return KnownTypeReference.UIntPtr; case MetadataType.FunctionPointer: // C# and the NR typesystem don't support function pointer types. // Function pointer types map to StackType.I, so we'll use IntPtr instead. - return KnownTypeReference.Get(KnownTypeCode.IntPtr); + return KnownTypeReference.IntPtr; case MetadataType.Object: - if (HasDynamicAttribute(typeAttributes, typeIndex)) { + if (UseDynamicType && HasDynamicAttribute(typeAttributes, dynamicTypeIndex)) { return SpecialType.Dynamic; } else { - return KnownTypeReference.Get(KnownTypeCode.Object); + return KnownTypeReference.Object; } case MetadataType.RequiredModifier: case MetadataType.OptionalModifier: // we don't store modopts/modreqs in the NR type system - return CreateType(((TypeSpecification)type).ElementType, typeAttributes, ref typeIndex, isFromSignature: true); + return CreateType(((TypeSpecification)type).ElementType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); case MetadataType.Sentinel: return SpecialType.ArgList; case MetadataType.Pinned: - return CreateType(((PinnedType)type).ElementType, typeAttributes, ref typeIndex, isFromSignature: true); + return CreateType(((PinnedType)type).ElementType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); } // valuetype/class/typedbyreference if (type is TypeDefinition) { @@ -400,7 +428,7 @@ namespace ICSharpCode.Decompiler.TypeSystem // or if it's a TypeSpecification. bool? isReferenceType = isFromSignature ? (bool?)!type.IsValueType : null; if (type.IsNested) { - ITypeReference typeRef = CreateType(type.DeclaringType, typeAttributes, ref typeIndex, isFromSignature); + ITypeReference typeRef = CreateType(type.DeclaringType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature); int partTypeParameterCount; string namepart = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out partTypeParameterCount); namepart = interningProvider.Intern(namepart); @@ -411,7 +439,7 @@ namespace ICSharpCode.Decompiler.TypeSystem if (name == null) throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString()); - if (name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, typeIndex)) { + if (UseDynamicType && name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, dynamicTypeIndex)) { return SpecialType.Dynamic; } int typeParameterCount; @@ -422,7 +450,46 @@ namespace ICSharpCode.Decompiler.TypeSystem isReferenceType)); } } - + + static bool IsValueTuple(GenericInstanceType gType, out int tupleCardinality) + { + tupleCardinality = 0; + if (gType == null || !gType.Name.StartsWith("ValueTuple`", StringComparison.Ordinal) || gType.Namespace != "System") + return false; + if (gType.GenericArguments.Count == TupleType.RestPosition) { + if (IsValueTuple(gType.GenericArguments.Last() as GenericInstanceType, out tupleCardinality)) { + tupleCardinality += TupleType.RestPosition - 1; + return true; + } + } + tupleCardinality = gType.GenericArguments.Count; + return tupleCardinality > 0 && tupleCardinality < TupleType.RestPosition; + } + + static ImmutableArray GetTupleElementNames(ICustomAttributeProvider attributeProvider, int tupleTypeIndex, int tupleCardinality) + { + if (attributeProvider == null || !attributeProvider.HasCustomAttributes) + return default(ImmutableArray); + foreach (CustomAttribute a in attributeProvider.CustomAttributes) { + TypeReference type = a.AttributeType; + if (type.Name == "TupleElementNamesAttribute" && type.Namespace == "System.Runtime.CompilerServices") { + if (a.ConstructorArguments.Count == 1) { + CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; + if (values != null) { + string[] extractedValues = new string[tupleCardinality]; + for (int i = 0; i < tupleCardinality; i++) { + if (tupleTypeIndex + i < values.Length) { + extractedValues[i] = values[tupleTypeIndex + i].Value as string; + } + } + return extractedValues.ToImmutableArray(); + } + } + } + } + return default(ImmutableArray); + } + IAssemblyReference GetAssemblyReference(IMetadataScope scope) { if (scope == null || scope == currentModule) @@ -827,7 +894,11 @@ namespace ICSharpCode.Decompiler.TypeSystem foreach (var cecilAttribute in attributes) { TypeReference type = cecilAttribute.AttributeType; if (type.Namespace == "System.Runtime.CompilerServices") { - if (type.Name == "DynamicAttribute" || type.Name == "ExtensionAttribute" || type.Name == "DecimalConstantAttribute") + if (type.Name == "ExtensionAttribute" || type.Name == "DecimalConstantAttribute") + continue; + if (UseDynamicType && type.Name == "DynamicAttribute") + continue; + if (UseTupleTypes && type.Name == "TupleElementNamesAttribute") continue; } else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") { continue; diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index a52e4f64e..f0b11b469 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -23,7 +23,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// CecilLoader used for converting cecil type references to ITypeReference. /// May only be accessed within lock(typeReferenceCecilLoader). /// - readonly CecilLoader typeReferenceCecilLoader = new CecilLoader(); + readonly CecilLoader typeReferenceCecilLoader; /// /// Dictionary for NRefactory->Cecil lookup. @@ -35,13 +35,30 @@ namespace ICSharpCode.Decompiler.TypeSystem Dictionary propertyLookupCache = new Dictionary(); Dictionary methodLookupCache = new Dictionary(); Dictionary eventLookupCache = new Dictionary(); - - public DecompilerTypeSystem(ModuleDefinition moduleDefinition) + + public DecompilerTypeSystem(ModuleDefinition moduleDefinition) : this(moduleDefinition, new DecompilerSettings()) + { + } + + public DecompilerTypeSystem(ModuleDefinition moduleDefinition, DecompilerSettings settings) { if (moduleDefinition == null) throw new ArgumentNullException(nameof(moduleDefinition)); + if (settings == null) + throw new ArgumentNullException(nameof(settings)); this.moduleDefinition = moduleDefinition; - CecilLoader cecilLoader = new CecilLoader { IncludeInternalMembers = true, LazyLoad = true, OnEntityLoaded = StoreMemberReference, ShortenInterfaceImplNames = false }; + typeReferenceCecilLoader = new CecilLoader { + UseDynamicType = settings.Dynamic, + UseTupleTypes = settings.TupleTypes, + }; + CecilLoader cecilLoader = new CecilLoader { + IncludeInternalMembers = true, + LazyLoad = true, + OnEntityLoaded = StoreMemberReference, + ShortenInterfaceImplNames = false, + UseDynamicType = settings.Dynamic, + UseTupleTypes = settings.TupleTypes, + }; typeReferenceCecilLoader.SetCurrentModule(moduleDefinition); IUnresolvedAssembly mainAssembly = cecilLoader.LoadModule(moduleDefinition); // Load referenced assemblies and type-forwarder references. diff --git a/ICSharpCode.Decompiler/TypeSystem/TupleType.cs b/ICSharpCode.Decompiler/TypeSystem/TupleType.cs index fa423cfb8..9051958a5 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TupleType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TupleType.cs @@ -41,31 +41,25 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Gets the tuple elements. /// - public ImmutableArray TupleElementTypes { get; } + public ImmutableArray ElementTypes { get; } /// /// Gets the names of the tuple elements. /// - public ImmutableArray TupleElementNames { get; } - - public bool HasCustomElementNames { get; } - - public TupleType(ICompilation compilation, ImmutableArray elementTypes) - { - this.Compilation = compilation; - this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes); - this.TupleElementTypes = elementTypes; - this.TupleElementNames = Enumerable.Range(1, elementTypes.Length).Select(p => "Item" + p).ToImmutableArray(); - this.HasCustomElementNames = false; - } - - public TupleType(ICompilation compilation, ImmutableArray elementTypes, ImmutableArray elementNames) + public ImmutableArray ElementNames { get; } + + public TupleType(ICompilation compilation, ImmutableArray elementTypes, + ImmutableArray elementNames = default(ImmutableArray)) { this.Compilation = compilation; this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes); - this.TupleElementTypes = elementTypes; - this.TupleElementNames = elementNames; - this.HasCustomElementNames = true; + this.ElementTypes = elementTypes; + if (elementNames.IsDefault) { + this.ElementNames = Enumerable.Repeat(null, elementTypes.Length).ToImmutableArray(); + } else { + Debug.Assert(elementNames.Length == elementTypes.Length); + this.ElementNames = elementNames; + } } static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray elementTypes) @@ -94,7 +88,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { switch (type.Kind) { case TypeKind.Tuple: - tupleCardinality = ((TupleType)type).TupleElementNames.Length; + tupleCardinality = ((TupleType)type).ElementNames.Length; return true; case TypeKind.Class: case TypeKind.Struct: @@ -120,7 +114,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { switch (type.Kind) { case TypeKind.Tuple: - output.AddRange(((TupleType)type).TupleElementTypes); + output.AddRange(((TupleType)type).ElementTypes); return true; case TypeKind.Class: case TypeKind.Struct: @@ -158,16 +152,16 @@ namespace ICSharpCode.Decompiler.TypeSystem if (!UnderlyingType.Equals(o.UnderlyingType)) return false; return UnderlyingType.Equals(o.UnderlyingType) - && TupleElementNames.SequenceEqual(o.TupleElementNames); + && ElementNames.SequenceEqual(o.ElementNames); } public override int GetHashCode() { unchecked { int hash = UnderlyingType.GetHashCode(); - foreach (string name in TupleElementNames) { + foreach (string name in ElementNames) { hash *= 31; - hash += name.GetHashCode(); + hash += name != null ? name.GetHashCode() : 0; } return hash; } @@ -181,18 +175,18 @@ namespace ICSharpCode.Decompiler.TypeSystem public override IType VisitChildren(TypeVisitor visitor) { IType[] newElementTypes = null; - for (int i = 0; i < TupleElementTypes.Length; i++) { - IType type = TupleElementTypes[i]; + for (int i = 0; i < ElementTypes.Length; i++) { + IType type = ElementTypes[i]; var newType = type.AcceptVisitor(visitor); if (newType != type) { if (newElementTypes == null) { - newElementTypes = TupleElementTypes.ToArray(); + newElementTypes = ElementTypes.ToArray(); } newElementTypes[i] = newType; } } if (newElementTypes != null) { - return new TupleType(this.Compilation, newElementTypes.ToImmutableArray(), this.TupleElementNames); + return new TupleType(this.Compilation, newElementTypes.ToImmutableArray(), this.ElementNames); } else { return this; } @@ -225,12 +219,12 @@ namespace ICSharpCode.Decompiler.TypeSystem foreach (var field in UnderlyingType.GetFields(filter, options)) { yield return field; } - for (int i = 0; i <= TupleElementTypes.Length; i++) { - var type = TupleElementTypes[i]; - var name = TupleElementNames[i]; + for (int i = 0; i < ElementTypes.Length; i++) { + var type = ElementTypes[i]; + var name = ElementNames[i]; int pos = i + 1; string itemName = "Item" + pos; - if (name != itemName) + if (name != itemName && name != null) yield return MakeField(type, name); if (pos >= RestPosition) yield return MakeField(type, itemName); @@ -268,6 +262,38 @@ namespace ICSharpCode.Decompiler.TypeSystem } } + public class TupleTypeReference : ITypeReference + { + /// + /// Gets the types of the tuple elements. + /// + public ImmutableArray ElementTypes { get; } + + /// + /// Gets the names of the tuple elements. + /// + public ImmutableArray ElementNames { get; } + + public TupleTypeReference(ImmutableArray elementTypes) + { + this.ElementTypes = elementTypes; + } + + public TupleTypeReference(ImmutableArray elementTypes, ImmutableArray elementNames) + { + this.ElementTypes = elementTypes; + this.ElementNames = elementNames; + } + + public IType Resolve(ITypeResolveContext context) + { + return new TupleType(context.Compilation, + ElementTypes.Select(t => t.Resolve(context)).ToImmutableArray(), + ElementNames + ); + } + } + public static class TupleTypeExtensions { public static IType TupleUnderlyingTypeOrSelf(this IType type)