Browse Source

Decompile TupleElementNamesAttribute into tuple type syntax.

pull/1134/head
Daniel Grunwald 7 years ago
parent
commit
395bc185a3
  1. 16
      ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
  2. 3
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  3. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  5. 48
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs
  6. 91
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il
  7. 92
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il
  8. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  9. 28
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  10. 15
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  11. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  12. 94
      ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs
  13. 12
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  14. 68
      ICSharpCode.Decompiler/DecompilerSettings.cs
  15. 177
      ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs
  16. 23
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  17. 84
      ICSharpCode.Decompiler/TypeSystem/TupleType.cs

16
ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs

@ -6,9 +6,9 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Tests.Helpers namespace ICSharpCode.Decompiler.Tests.Helpers
{ {
class RemoveCompilerAttribute : DepthFirstAstVisitor<object, object>, 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; var section = (AttributeSection)attribute.Parent;
SimpleType type = attribute.Type as SimpleType; SimpleType type = attribute.Type as SimpleType;
@ -25,16 +25,15 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
if (section.Attributes.Count == 0) if (section.Attributes.Count == 0)
section.Remove(); section.Remove();
} }
return null;
} }
public void Run(AstNode rootNode, TransformContext context) public void Run(AstNode rootNode, TransformContext context)
{ {
rootNode.AcceptVisitor(this, null); rootNode.AcceptVisitor(this);
} }
} }
public class RemoveEmbeddedAtttributes : DepthFirstAstVisitor<object, object>, IAstTransform public class RemoveEmbeddedAtttributes : DepthFirstAstVisitor, IAstTransform
{ {
HashSet<string> attributeNames = new HashSet<string>() { HashSet<string> attributeNames = new HashSet<string>() {
"System.Runtime.CompilerServices.IsReadOnlyAttribute", "System.Runtime.CompilerServices.IsReadOnlyAttribute",
@ -42,21 +41,20 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
"Microsoft.CodeAnalysis.EmbeddedAttribute", "Microsoft.CodeAnalysis.EmbeddedAttribute",
}; };
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{ {
var typeDefinition = typeDeclaration.GetSymbol() as ITypeDefinition; var typeDefinition = typeDeclaration.GetSymbol() as ITypeDefinition;
if (typeDefinition == null || !attributeNames.Contains(typeDefinition.FullName)) if (typeDefinition == null || !attributeNames.Contains(typeDefinition.FullName))
return null; return;
if (typeDeclaration.Parent is NamespaceDeclaration ns && ns.Members.Count == 1) if (typeDeclaration.Parent is NamespaceDeclaration ns && ns.Members.Count == 1)
ns.Remove(); ns.Remove();
else else
typeDeclaration.Remove(); typeDeclaration.Remove();
return null;
} }
public void Run(AstNode rootNode, TransformContext context) public void Run(AstNode rootNode, TransformContext context)
{ {
rootNode.AcceptVisitor(this, null); rootNode.AcceptVisitor(this);
} }
} }
} }

3
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, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.dll")), MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Core.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)
}; };
}); });

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -70,6 +70,7 @@
<Compile Include="TestCases\Pretty\Issue1080.cs" /> <Compile Include="TestCases\Pretty\Issue1080.cs" />
<Compile Include="TestCases\Pretty\QualifierTests.cs" /> <Compile Include="TestCases\Pretty\QualifierTests.cs" />
<Compile Include="TestCases\Pretty\RefLocalsAndReturns.cs" /> <Compile Include="TestCases\Pretty\RefLocalsAndReturns.cs" />
<Compile Include="TestCases\Pretty\TupleTypes.cs" />
<Compile Include="TestCases\Pretty\WellKnownConstants.cs" /> <Compile Include="TestCases\Pretty\WellKnownConstants.cs" />
<Compile Include="TypeSystem\TypeSystemHelper.cs" /> <Compile Include="TypeSystem\TypeSystemHelper.cs" />
<Compile Include="TypeSystem\TypeSystemLoaderTests.cs" /> <Compile Include="TypeSystem\TypeSystemLoaderTests.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -279,6 +279,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions); RunForLibrary(cscOptions: cscOptions);
} }
[Test]
public void TupleTypes([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test] [Test]
public void Issue1080([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions) public void Issue1080([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{ {

48
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<int> VT1;
public ValueTuple<int, int, int, int, int, int, int, 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<int> 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;
}
}

91
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<int32> VT1
.field public valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple> VT7EmptyRest
.field public valuetype [mscorlib]System.ValueTuple`2<int32,uint32> Unnamed2
.field public valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32> Unnamed3
.field public valuetype [mscorlib]System.ValueTuple`4<int32,int32,int32,int32> Unnamed4
.field public valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32> Unnamed5
.field public valuetype [mscorlib]System.ValueTuple`6<int32,int32,int32,int32,int32,int32> Unnamed6
.field public valuetype [mscorlib]System.ValueTuple`7<int32,int32,int32,int32,int32,int32,int32> Unnamed7
.field public valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> Unnamed8
.field public valuetype [mscorlib]System.ValueTuple`2<int32,uint32> 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<int32,uint32>[] 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<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> 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<int32,int32,int32,int32,int32> 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<int32,int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>> 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<valuetype [mscorlib]System.ValueTuple`2<object,object>,object,valuetype [mscorlib]System.ValueTuple`2<object,object>> 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,valuetype [mscorlib]System.ValueTuple`2<int32,int32>,valuetype [mscorlib]System.ValueTuple`1<int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>> 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<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`2<int32,valuetype [mscorlib]System.ValueTuple`2<int32,int32>>> 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 ***********************

92
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<int32> VT1
.field public valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple> VT7EmptyRest
.field public valuetype [mscorlib]System.ValueTuple`2<int32,uint32> Unnamed2
.field public valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32> Unnamed3
.field public valuetype [mscorlib]System.ValueTuple`4<int32,int32,int32,int32> Unnamed4
.field public valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32> Unnamed5
.field public valuetype [mscorlib]System.ValueTuple`6<int32,int32,int32,int32,int32,int32> Unnamed6
.field public valuetype [mscorlib]System.ValueTuple`7<int32,int32,int32,int32,int32,int32,int32> Unnamed7
.field public valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> Unnamed8
.field public valuetype [mscorlib]System.ValueTuple`2<int32,uint32> 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<int32,uint32>[] 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<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> 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<int32,int32,int32,int32,int32> 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<int32,int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>> 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<valuetype [mscorlib]System.ValueTuple`2<object,object>,object,valuetype [mscorlib]System.ValueTuple`2<object,object>> 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,valuetype [mscorlib]System.ValueTuple`2<int32,int32>,valuetype [mscorlib]System.ValueTuple`1<int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>,valuetype [mscorlib]System.ValueTuple`2<int32,int32>> 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<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`2<int32,valuetype [mscorlib]System.ValueTuple`2<int32,int32>>> 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 ***********************

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -187,7 +187,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
public CSharpDecompiler(ModuleDefinition module, DecompilerSettings settings) public CSharpDecompiler(ModuleDefinition module, DecompilerSettings settings)
: this(new DecompilerTypeSystem(module), settings) : this(new DecompilerTypeSystem(module, settings), settings)
{ {
} }

28
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -445,6 +445,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
foreach (CSharpModifierToken modifier in modifierTokens) { foreach (CSharpModifierToken modifier in modifierTokens) {
modifier.AcceptVisitor(this); modifier.AcceptVisitor(this);
Space();
} }
} }
@ -2283,26 +2284,23 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitTupleType(TupleAstType tupleType) public virtual void VisitTupleType(TupleAstType tupleType)
{ {
Debug.Assert(tupleType.ElementTypes.Count >= 2); Debug.Assert(tupleType.Elements.Count >= 2);
StartNode(tupleType); StartNode(tupleType);
LPar(); LPar();
if (tupleType.ElementNames.Any()) { WriteCommaSeparatedList(tupleType.Elements);
bool isFirst = true; RPar();
foreach (var (type, name) in tupleType.ElementTypes.Zip(tupleType.ElementNames)) { EndNode(tupleType);
if (isFirst) {
isFirst = false;
} else {
Comma(type);
} }
type.AcceptVisitor(this);
public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement)
{
StartNode(tupleTypeElement);
tupleTypeElement.Type.AcceptVisitor(this);
if (!tupleTypeElement.NameToken.IsNull) {
Space(); Space();
name.AcceptVisitor(this); tupleTypeElement.NameToken.AcceptVisitor(this);
}
} else {
WriteCommaSeparatedList(tupleType.ElementTypes);
} }
RPar(); EndNode(tupleTypeElement);
EndNode(tupleType);
} }
public virtual void VisitComposedType(ComposedType composedType) public virtual void VisitComposedType(ComposedType composedType)

15
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -121,6 +121,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
VisitChildren(tupleType); VisitChildren(tupleType);
} }
public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement)
{
VisitChildren (tupleTypeElement);
}
public virtual void VisitAttribute (Attribute attribute) public virtual void VisitAttribute (Attribute attribute)
{ {
VisitChildren (attribute); VisitChildren (attribute);
@ -763,6 +768,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (tupleType); return VisitChildren (tupleType);
} }
public virtual T VisitTupleTypeElement(TupleTypeElement tupleTypeElement)
{
return VisitChildren (tupleTypeElement);
}
public virtual T VisitAttribute (Attribute attribute) public virtual T VisitAttribute (Attribute attribute)
{ {
return VisitChildren (attribute); return VisitChildren (attribute);
@ -1405,6 +1415,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (tupleType, data); return VisitChildren (tupleType, data);
} }
public virtual S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data)
{
return VisitChildren (tupleTypeElement, data);
}
public virtual S VisitAttribute (Attribute attribute, T data) public virtual S VisitAttribute (Attribute attribute, T data)
{ {
return VisitChildren (attribute, data); return VisitChildren (attribute, data);

3
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -135,6 +135,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitSimpleType(SimpleType simpleType); void VisitSimpleType(SimpleType simpleType);
void VisitMemberType(MemberType memberType); void VisitMemberType(MemberType memberType);
void VisitTupleType(TupleAstType tupleType); void VisitTupleType(TupleAstType tupleType);
void VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
void VisitComposedType(ComposedType composedType); void VisitComposedType(ComposedType composedType);
void VisitArraySpecifier(ArraySpecifier arraySpecifier); void VisitArraySpecifier(ArraySpecifier arraySpecifier);
void VisitPrimitiveType(PrimitiveType primitiveType); void VisitPrimitiveType(PrimitiveType primitiveType);
@ -275,6 +276,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitSimpleType(SimpleType simpleType); S VisitSimpleType(SimpleType simpleType);
S VisitMemberType(MemberType memberType); S VisitMemberType(MemberType memberType);
S VisitTupleType(TupleAstType tupleType); S VisitTupleType(TupleAstType tupleType);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
S VisitComposedType(ComposedType composedType); S VisitComposedType(ComposedType composedType);
S VisitArraySpecifier(ArraySpecifier arraySpecifier); S VisitArraySpecifier(ArraySpecifier arraySpecifier);
S VisitPrimitiveType(PrimitiveType primitiveType); S VisitPrimitiveType(PrimitiveType primitiveType);
@ -415,6 +417,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitSimpleType(SimpleType simpleType, T data); S VisitSimpleType(SimpleType simpleType, T data);
S VisitMemberType(MemberType memberType, T data); S VisitMemberType(MemberType memberType, T data);
S VisitTupleType(TupleAstType tupleType, T data); S VisitTupleType(TupleAstType tupleType, T data);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data);
S VisitComposedType(ComposedType composedType, T data); S VisitComposedType(ComposedType composedType, T data);
S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data); S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data);
S VisitPrimitiveType(PrimitiveType primitiveType, T data); S VisitPrimitiveType(PrimitiveType primitiveType, T data);

94
ICSharpCode.Decompiler/CSharp/Syntax/TupleAstType.cs

@ -17,6 +17,8 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Immutable;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -25,12 +27,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public class TupleAstType : AstType public class TupleAstType : AstType
{ {
public AstNodeCollection<AstType> ElementTypes { public static readonly Role<TupleTypeElement> ElementRole = new Role<TupleTypeElement>("Element", TupleTypeElement.Null);
get { return GetChildrenByRole(Roles.TypeArgument); }
}
public AstNodeCollection<Identifier> ElementNames { public AstNodeCollection<TupleTypeElement> Elements {
get { return GetChildrenByRole(Roles.Identifier); } get { return GetChildrenByRole(ElementRole); }
} }
public override void AcceptVisitor(IAstVisitor visitor) public override void AcceptVisitor(IAstVisitor visitor)
@ -50,14 +50,90 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) 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<T>(IAstVisitor<T> visitor)
{
return visitor.VisitNullNode(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> 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<T>(IAstVisitor<T> visitor)
{
return visitor.VisitTupleTypeElement(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitTupleTypeElement(this, data);
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
{ {
return other is TupleAstType o return other is TupleTypeElement o
&& ElementTypes.DoMatch(o.ElementTypes, match) && Type.DoMatch(o.Type, match)
&& ElementNames.DoMatch(o.ElementNames, match); && MatchString(Name, o.Name);
} }
} }
} }

12
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -239,13 +239,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
if (type is TupleType tuple) { if (type is TupleType tuple) {
var astType = new TupleAstType(); var astType = new TupleAstType();
if (tuple.HasCustomElementNames) { foreach (var (etype, ename) in tuple.ElementTypes.Zip(tuple.ElementNames)) {
foreach (var (etype, ename) in tuple.TupleElementTypes.Zip(tuple.TupleElementNames)) { astType.Elements.Add(new TupleTypeElement {
astType.ElementTypes.Add(ConvertType(etype)); Type = ConvertType(etype),
astType.ElementNames.Add(Identifier.Create(ename)); Name = ename
} });
} else {
astType.ElementTypes.AddRange(tuple.TupleElementTypes.Select(ConvertType));
} }
return astType; return astType;
} }

68
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -59,7 +59,7 @@ namespace ICSharpCode.Decompiler
expressionTrees = false; expressionTrees = false;
} }
if (languageVersion < CSharp.LanguageVersion.CSharp4) { if (languageVersion < CSharp.LanguageVersion.CSharp4) {
// * dynamic (not supported yet) dynamic = false;
// * named and optional arguments (not supported yet) // * named and optional arguments (not supported yet)
} }
if (languageVersion < CSharp.LanguageVersion.CSharp5) { if (languageVersion < CSharp.LanguageVersion.CSharp5) {
@ -74,6 +74,8 @@ namespace ICSharpCode.Decompiler
} }
if (languageVersion < CSharp.LanguageVersion.CSharp7) { if (languageVersion < CSharp.LanguageVersion.CSharp7) {
outVariables = false; outVariables = false;
tupleTypes = false;
tupleConversions = false;
discards = false; discards = false;
} }
if (languageVersion < CSharp.LanguageVersion.CSharp7_2) { if (languageVersion < CSharp.LanguageVersion.CSharp7_2) {
@ -81,7 +83,7 @@ namespace ICSharpCode.Decompiler
} }
if (languageVersion < CSharp.LanguageVersion.CSharp7_3) { if (languageVersion < CSharp.LanguageVersion.CSharp7_3) {
//introduceUnmanagedTypeConstraint = false; //introduceUnmanagedTypeConstraint = false;
//... tupleComparisons = false;
} }
} }
@ -145,6 +147,21 @@ namespace ICSharpCode.Decompiler
} }
} }
bool dynamic = true;
/// <summary>
/// Decompile use of the 'dynamic' type.
/// </summary>
public bool Dynamic {
get { return dynamic; }
set {
if (dynamic != value) {
dynamic = value;
OnPropertyChanged();
}
}
}
bool asyncAwait = true; bool asyncAwait = true;
/// <summary> /// <summary>
@ -571,6 +588,53 @@ namespace ICSharpCode.Decompiler
} }
} }
bool tupleTypes = true;
/// <summary>
/// Gets/Sets whether tuple type syntax <c>(int, string)</c>
/// should be used for <c>System.ValueTuple</c>.
/// </summary>
public bool TupleTypes {
get { return tupleTypes; }
set {
if (tupleTypes != value) {
tupleTypes = value;
OnPropertyChanged();
}
}
}
bool tupleConversions = true;
/// <summary>
/// Gets/Sets whether implicit conversions between tuples
/// should be used in the decompiled output.
/// </summary>
public bool TupleConversions {
get { return tupleConversions; }
set {
if (tupleConversions != value) {
tupleConversions = value;
OnPropertyChanged();
}
}
}
bool tupleComparisons = true;
/// <summary>
/// Gets/Sets whether tuple comparisons should be detected.
/// </summary>
public bool TupleComparisons {
get { return tupleComparisons; }
set {
if (tupleComparisons != value) {
tupleComparisons = value;
OnPropertyChanged();
}
}
}
#region Options to aid VB decompilation #region Options to aid VB decompilation
bool assumeArrayLengthFitsIntoInt32 = true; bool assumeArrayLengthFitsIntoInt32 = true;

177
ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -38,13 +39,6 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// if you want to load multiple project contents in parallel.</remarks> /// if you want to load multiple project contents in parallel.</remarks>
public sealed class CecilLoader : AssemblyLoader public sealed class CecilLoader : AssemblyLoader
{ {
/// <summary>
/// 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.
/// </summary>
const int cecilLoaderVersion = 1;
#region Options #region Options
// Most options are defined in the AssemblyLoader base class // Most options are defined in the AssemblyLoader base class
@ -64,6 +58,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </remarks> /// </remarks>
public bool LazyLoad { get; set; } public bool LazyLoad { get; set; }
/// <summary>
/// Gets/Sets whether to use the <c>dynamic</c> type.
/// </summary>
public bool UseDynamicType { get; set; } = true;
/// <summary>
/// Gets/Sets whether to use the tuple types.
/// </summary>
public bool UseTupleTypes { get; set; } = true;
/// <summary> /// <summary>
/// This delegate gets executed whenever an entity was loaded. /// This delegate gets executed whenever an entity was loaded.
/// </summary> /// </summary>
@ -75,20 +79,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </remarks> /// </remarks>
public Action<IUnresolvedEntity, MemberReference> OnEntityLoaded { get; set; } public Action<IUnresolvedEntity, MemberReference> OnEntityLoaded { get; set; }
bool shortenInterfaceImplNames = true;
/// <summary> /// <summary>
/// Specifies whether method names of explicit interface-implementations should be shortened. /// Specifies whether method names of explicit interface-implementations should be shortened.
/// </summary> /// </summary>
/// <remarks>This is important when working with parser-initialized type-systems in order to be consistent.</remarks> /// <remarks>This is important when working with parser-initialized type-systems in order to be consistent.</remarks>
public bool ShortenInterfaceImplNames { public bool ShortenInterfaceImplNames { get; set; } = true;
get {
return shortenInterfaceImplNames;
}
set {
shortenInterfaceImplNames = value;
}
}
#endregion #endregion
ModuleDefinition currentModule; ModuleDefinition currentModule;
@ -108,6 +103,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
// use a shared typeSystemTranslationTable // use a shared typeSystemTranslationTable
this.IncludeInternalMembers = loader.IncludeInternalMembers; this.IncludeInternalMembers = loader.IncludeInternalMembers;
this.UseDynamicType = loader.UseDynamicType;
this.UseTupleTypes = loader.UseTupleTypes;
this.LazyLoad = loader.LazyLoad; this.LazyLoad = loader.LazyLoad;
this.OnEntityLoaded = loader.OnEntityLoaded; this.OnEntityLoaded = loader.OnEntityLoaded;
this.ShortenInterfaceImplNames = loader.ShortenInterfaceImplNames; this.ShortenInterfaceImplNames = loader.ShortenInterfaceImplNames;
@ -295,11 +292,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// IsValueType is set correctly.</param> /// IsValueType is set correctly.</param>
public ITypeReference ReadTypeReference(TypeReference type, ICustomAttributeProvider typeAttributes = null, bool isFromSignature = false) public ITypeReference ReadTypeReference(TypeReference type, ICustomAttributeProvider typeAttributes = null, bool isFromSignature = false)
{ {
int typeIndex = 0; int dynamicTypeIndex = 0;
return CreateType(type, typeAttributes, ref typeIndex, isFromSignature); 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) { if (type == null) {
return SpecialType.UnknownType; return SpecialType.UnknownType;
@ -307,90 +305,120 @@ namespace ICSharpCode.Decompiler.TypeSystem
switch (type.MetadataType) { switch (type.MetadataType) {
case MetadataType.Void: case MetadataType.Void:
return KnownTypeReference.Get(KnownTypeCode.Void); return KnownTypeReference.Void;
case MetadataType.Boolean: case MetadataType.Boolean:
return KnownTypeReference.Get(KnownTypeCode.Boolean); return KnownTypeReference.Boolean;
case MetadataType.Char: case MetadataType.Char:
return KnownTypeReference.Get(KnownTypeCode.Char); return KnownTypeReference.Char;
case MetadataType.SByte: case MetadataType.SByte:
return KnownTypeReference.Get(KnownTypeCode.SByte); return KnownTypeReference.SByte;
case MetadataType.Byte: case MetadataType.Byte:
return KnownTypeReference.Get(KnownTypeCode.Byte); return KnownTypeReference.Byte;
case MetadataType.Int16: case MetadataType.Int16:
return KnownTypeReference.Get(KnownTypeCode.Int16); return KnownTypeReference.Int16;
case MetadataType.UInt16: case MetadataType.UInt16:
return KnownTypeReference.Get(KnownTypeCode.UInt16); return KnownTypeReference.UInt16;
case MetadataType.Int32: case MetadataType.Int32:
return KnownTypeReference.Get(KnownTypeCode.Int32); return KnownTypeReference.Int32;
case MetadataType.UInt32: case MetadataType.UInt32:
return KnownTypeReference.Get(KnownTypeCode.UInt32); return KnownTypeReference.UInt32;
case MetadataType.Int64: case MetadataType.Int64:
return KnownTypeReference.Get(KnownTypeCode.Int64); return KnownTypeReference.Int64;
case MetadataType.UInt64: case MetadataType.UInt64:
return KnownTypeReference.Get(KnownTypeCode.UInt64); return KnownTypeReference.UInt64;
case MetadataType.Single: case MetadataType.Single:
return KnownTypeReference.Get(KnownTypeCode.Single); return KnownTypeReference.Single;
case MetadataType.Double: case MetadataType.Double:
return KnownTypeReference.Get(KnownTypeCode.Double); return KnownTypeReference.Double;
case MetadataType.String: case MetadataType.String:
return KnownTypeReference.Get(KnownTypeCode.String); return KnownTypeReference.String;
case MetadataType.Pointer: case MetadataType.Pointer:
typeIndex++; dynamicTypeIndex++;
return interningProvider.Intern( return interningProvider.Intern(
new PointerTypeReference( new PointerTypeReference(
CreateType( CreateType(
(type as Mono.Cecil.PointerType).ElementType, (type as Mono.Cecil.PointerType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true))); typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true)));
case MetadataType.ByReference: case MetadataType.ByReference:
typeIndex++; dynamicTypeIndex++;
return interningProvider.Intern( return interningProvider.Intern(
new ByReferenceTypeReference( new ByReferenceTypeReference(
CreateType( CreateType(
(type as Mono.Cecil.ByReferenceType).ElementType, (type as Mono.Cecil.ByReferenceType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true))); typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true)));
case MetadataType.Var: case MetadataType.Var:
return TypeParameterReference.Create(SymbolKind.TypeDefinition, ((GenericParameter)type).Position); return TypeParameterReference.Create(SymbolKind.TypeDefinition, ((GenericParameter)type).Position);
case MetadataType.MVar: case MetadataType.MVar:
return TypeParameterReference.Create(SymbolKind.Method, ((GenericParameter)type).Position); return TypeParameterReference.Create(SymbolKind.Method, ((GenericParameter)type).Position);
case MetadataType.Array: case MetadataType.Array:
typeIndex++; dynamicTypeIndex++;
return interningProvider.Intern( return interningProvider.Intern(
new ArrayTypeReference( new ArrayTypeReference(
CreateType( CreateType(
(type as Mono.Cecil.ArrayType).ElementType, (type as Mono.Cecil.ArrayType).ElementType,
typeAttributes, ref typeIndex, isFromSignature: true), typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true),
(type as Mono.Cecil.ArrayType).Rank)); (type as Mono.Cecil.ArrayType).Rank));
case MetadataType.GenericInstance: case MetadataType.GenericInstance:
GenericInstanceType gType = (GenericInstanceType)type; 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]; ITypeReference[] para = new ITypeReference[gType.GenericArguments.Count];
for (int i = 0; i < para.Length; ++i) { for (int i = 0; i < para.Length; ++i) {
typeIndex++; dynamicTypeIndex++;
para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref typeIndex, isFromSignature: true); para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true);
} }
return interningProvider.Intern(new ParameterizedTypeReference(baseType, para)); return interningProvider.Intern(new ParameterizedTypeReference(baseType, para));
case MetadataType.IntPtr: case MetadataType.IntPtr:
return KnownTypeReference.Get(KnownTypeCode.IntPtr); return KnownTypeReference.IntPtr;
case MetadataType.UIntPtr: case MetadataType.UIntPtr:
return KnownTypeReference.Get(KnownTypeCode.UIntPtr); return KnownTypeReference.UIntPtr;
case MetadataType.FunctionPointer: case MetadataType.FunctionPointer:
// C# and the NR typesystem don't support function pointer types. // C# and the NR typesystem don't support function pointer types.
// Function pointer types map to StackType.I, so we'll use IntPtr instead. // Function pointer types map to StackType.I, so we'll use IntPtr instead.
return KnownTypeReference.Get(KnownTypeCode.IntPtr); return KnownTypeReference.IntPtr;
case MetadataType.Object: case MetadataType.Object:
if (HasDynamicAttribute(typeAttributes, typeIndex)) { if (UseDynamicType && HasDynamicAttribute(typeAttributes, dynamicTypeIndex)) {
return SpecialType.Dynamic; return SpecialType.Dynamic;
} else { } else {
return KnownTypeReference.Get(KnownTypeCode.Object); return KnownTypeReference.Object;
} }
case MetadataType.RequiredModifier: case MetadataType.RequiredModifier:
case MetadataType.OptionalModifier: case MetadataType.OptionalModifier:
// we don't store modopts/modreqs in the NR type system // 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: case MetadataType.Sentinel:
return SpecialType.ArgList; return SpecialType.ArgList;
case MetadataType.Pinned: 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 // valuetype/class/typedbyreference
if (type is TypeDefinition) { if (type is TypeDefinition) {
@ -400,7 +428,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
// or if it's a TypeSpecification. // or if it's a TypeSpecification.
bool? isReferenceType = isFromSignature ? (bool?)!type.IsValueType : null; bool? isReferenceType = isFromSignature ? (bool?)!type.IsValueType : null;
if (type.IsNested) { 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; int partTypeParameterCount;
string namepart = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out partTypeParameterCount); string namepart = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out partTypeParameterCount);
namepart = interningProvider.Intern(namepart); namepart = interningProvider.Intern(namepart);
@ -411,7 +439,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
if (name == null) if (name == null)
throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString()); 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; return SpecialType.Dynamic;
} }
int typeParameterCount; int typeParameterCount;
@ -423,6 +451,45 @@ namespace ICSharpCode.Decompiler.TypeSystem
} }
} }
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<string> GetTupleElementNames(ICustomAttributeProvider attributeProvider, int tupleTypeIndex, int tupleCardinality)
{
if (attributeProvider == null || !attributeProvider.HasCustomAttributes)
return default(ImmutableArray<string>);
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<string>);
}
IAssemblyReference GetAssemblyReference(IMetadataScope scope) IAssemblyReference GetAssemblyReference(IMetadataScope scope)
{ {
if (scope == null || scope == currentModule) if (scope == null || scope == currentModule)
@ -827,7 +894,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
foreach (var cecilAttribute in attributes) { foreach (var cecilAttribute in attributes) {
TypeReference type = cecilAttribute.AttributeType; TypeReference type = cecilAttribute.AttributeType;
if (type.Namespace == "System.Runtime.CompilerServices") { 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; continue;
} else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") { } else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") {
continue; continue;

23
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -23,7 +23,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// CecilLoader used for converting cecil type references to ITypeReference. /// CecilLoader used for converting cecil type references to ITypeReference.
/// May only be accessed within lock(typeReferenceCecilLoader). /// May only be accessed within lock(typeReferenceCecilLoader).
/// </summary> /// </summary>
readonly CecilLoader typeReferenceCecilLoader = new CecilLoader(); readonly CecilLoader typeReferenceCecilLoader;
/// <summary> /// <summary>
/// Dictionary for NRefactory->Cecil lookup. /// Dictionary for NRefactory->Cecil lookup.
@ -36,12 +36,29 @@ namespace ICSharpCode.Decompiler.TypeSystem
Dictionary<MethodReference, IMethod> methodLookupCache = new Dictionary<MethodReference, IMethod>(); Dictionary<MethodReference, IMethod> methodLookupCache = new Dictionary<MethodReference, IMethod>();
Dictionary<EventReference, IEvent> eventLookupCache = new Dictionary<EventReference, IEvent>(); Dictionary<EventReference, IEvent> eventLookupCache = new Dictionary<EventReference, IEvent>();
public DecompilerTypeSystem(ModuleDefinition moduleDefinition) public DecompilerTypeSystem(ModuleDefinition moduleDefinition) : this(moduleDefinition, new DecompilerSettings())
{
}
public DecompilerTypeSystem(ModuleDefinition moduleDefinition, DecompilerSettings settings)
{ {
if (moduleDefinition == null) if (moduleDefinition == null)
throw new ArgumentNullException(nameof(moduleDefinition)); throw new ArgumentNullException(nameof(moduleDefinition));
if (settings == null)
throw new ArgumentNullException(nameof(settings));
this.moduleDefinition = moduleDefinition; 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); typeReferenceCecilLoader.SetCurrentModule(moduleDefinition);
IUnresolvedAssembly mainAssembly = cecilLoader.LoadModule(moduleDefinition); IUnresolvedAssembly mainAssembly = cecilLoader.LoadModule(moduleDefinition);
// Load referenced assemblies and type-forwarder references. // Load referenced assemblies and type-forwarder references.

84
ICSharpCode.Decompiler/TypeSystem/TupleType.cs

@ -41,31 +41,25 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary> /// <summary>
/// Gets the tuple elements. /// Gets the tuple elements.
/// </summary> /// </summary>
public ImmutableArray<IType> TupleElementTypes { get; } public ImmutableArray<IType> ElementTypes { get; }
/// <summary> /// <summary>
/// Gets the names of the tuple elements. /// Gets the names of the tuple elements.
/// </summary> /// </summary>
public ImmutableArray<string> TupleElementNames { get; } public ImmutableArray<string> ElementNames { get; }
public bool HasCustomElementNames { get; } public TupleType(ICompilation compilation, ImmutableArray<IType> elementTypes,
ImmutableArray<string> elementNames = default(ImmutableArray<string>))
public TupleType(ICompilation compilation, ImmutableArray<IType> elementTypes)
{ {
this.Compilation = compilation; this.Compilation = compilation;
this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes); this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes);
this.TupleElementTypes = elementTypes; this.ElementTypes = elementTypes;
this.TupleElementNames = Enumerable.Range(1, elementTypes.Length).Select(p => "Item" + p).ToImmutableArray(); if (elementNames.IsDefault) {
this.HasCustomElementNames = false; this.ElementNames = Enumerable.Repeat<string>(null, elementTypes.Length).ToImmutableArray();
} else {
Debug.Assert(elementNames.Length == elementTypes.Length);
this.ElementNames = elementNames;
} }
public TupleType(ICompilation compilation, ImmutableArray<IType> elementTypes, ImmutableArray<string> elementNames)
{
this.Compilation = compilation;
this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes);
this.TupleElementTypes = elementTypes;
this.TupleElementNames = elementNames;
this.HasCustomElementNames = true;
} }
static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray<IType> elementTypes) static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray<IType> elementTypes)
@ -94,7 +88,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
switch (type.Kind) { switch (type.Kind) {
case TypeKind.Tuple: case TypeKind.Tuple:
tupleCardinality = ((TupleType)type).TupleElementNames.Length; tupleCardinality = ((TupleType)type).ElementNames.Length;
return true; return true;
case TypeKind.Class: case TypeKind.Class:
case TypeKind.Struct: case TypeKind.Struct:
@ -120,7 +114,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{ {
switch (type.Kind) { switch (type.Kind) {
case TypeKind.Tuple: case TypeKind.Tuple:
output.AddRange(((TupleType)type).TupleElementTypes); output.AddRange(((TupleType)type).ElementTypes);
return true; return true;
case TypeKind.Class: case TypeKind.Class:
case TypeKind.Struct: case TypeKind.Struct:
@ -158,16 +152,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
if (!UnderlyingType.Equals(o.UnderlyingType)) if (!UnderlyingType.Equals(o.UnderlyingType))
return false; return false;
return UnderlyingType.Equals(o.UnderlyingType) return UnderlyingType.Equals(o.UnderlyingType)
&& TupleElementNames.SequenceEqual(o.TupleElementNames); && ElementNames.SequenceEqual(o.ElementNames);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
unchecked { unchecked {
int hash = UnderlyingType.GetHashCode(); int hash = UnderlyingType.GetHashCode();
foreach (string name in TupleElementNames) { foreach (string name in ElementNames) {
hash *= 31; hash *= 31;
hash += name.GetHashCode(); hash += name != null ? name.GetHashCode() : 0;
} }
return hash; return hash;
} }
@ -181,18 +175,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
public override IType VisitChildren(TypeVisitor visitor) public override IType VisitChildren(TypeVisitor visitor)
{ {
IType[] newElementTypes = null; IType[] newElementTypes = null;
for (int i = 0; i < TupleElementTypes.Length; i++) { for (int i = 0; i < ElementTypes.Length; i++) {
IType type = TupleElementTypes[i]; IType type = ElementTypes[i];
var newType = type.AcceptVisitor(visitor); var newType = type.AcceptVisitor(visitor);
if (newType != type) { if (newType != type) {
if (newElementTypes == null) { if (newElementTypes == null) {
newElementTypes = TupleElementTypes.ToArray(); newElementTypes = ElementTypes.ToArray();
} }
newElementTypes[i] = newType; newElementTypes[i] = newType;
} }
} }
if (newElementTypes != null) { if (newElementTypes != null) {
return new TupleType(this.Compilation, newElementTypes.ToImmutableArray(), this.TupleElementNames); return new TupleType(this.Compilation, newElementTypes.ToImmutableArray(), this.ElementNames);
} else { } else {
return this; return this;
} }
@ -225,12 +219,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
foreach (var field in UnderlyingType.GetFields(filter, options)) { foreach (var field in UnderlyingType.GetFields(filter, options)) {
yield return field; yield return field;
} }
for (int i = 0; i <= TupleElementTypes.Length; i++) { for (int i = 0; i < ElementTypes.Length; i++) {
var type = TupleElementTypes[i]; var type = ElementTypes[i];
var name = TupleElementNames[i]; var name = ElementNames[i];
int pos = i + 1; int pos = i + 1;
string itemName = "Item" + pos; string itemName = "Item" + pos;
if (name != itemName) if (name != itemName && name != null)
yield return MakeField(type, name); yield return MakeField(type, name);
if (pos >= RestPosition) if (pos >= RestPosition)
yield return MakeField(type, itemName); yield return MakeField(type, itemName);
@ -268,6 +262,38 @@ namespace ICSharpCode.Decompiler.TypeSystem
} }
} }
public class TupleTypeReference : ITypeReference
{
/// <summary>
/// Gets the types of the tuple elements.
/// </summary>
public ImmutableArray<ITypeReference> ElementTypes { get; }
/// <summary>
/// Gets the names of the tuple elements.
/// </summary>
public ImmutableArray<string> ElementNames { get; }
public TupleTypeReference(ImmutableArray<ITypeReference> elementTypes)
{
this.ElementTypes = elementTypes;
}
public TupleTypeReference(ImmutableArray<ITypeReference> elementTypes, ImmutableArray<string> 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 class TupleTypeExtensions
{ {
public static IType TupleUnderlyingTypeOrSelf(this IType type) public static IType TupleUnderlyingTypeOrSelf(this IType type)

Loading…
Cancel
Save