Browse Source

Add support for C# 7 tuple types:

* Use tuple literals instead of calling 'new ValueTuple<..>' constructor
 * Where available, use element names for field access
 * Make CallBuilder aware of tuple-name/dynamic type erasure, to avoid introducing casts when the types differ only in the tuple element names.
 * Make CallBuilder provide a ResolveResult with the correct C# return type for the resulting expression.
   Previously we were using the type-erased return type from the IL.
 * Fix a bug that caused us to introduce returning casts when accessing an indexer.
pull/1134/head
Daniel Grunwald 7 years ago
parent
commit
469501210c
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 2
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 33
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs
  4. 344
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.opt.roslyn.il
  5. 359
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il
  6. 91
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il
  7. 92
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il
  8. 10
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  9. 97
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  10. 47
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  11. 7
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs
  12. 8
      ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs
  13. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  14. 18
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs
  15. 93
      ICSharpCode.Decompiler/IL/Transforms/TupleTransform.cs
  16. 9
      ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs
  17. 11
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  18. 5
      ICSharpCode.Decompiler/TypeSystem/IMember.cs
  19. 5
      ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedMember.cs
  20. 8
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultMemberReference.cs
  21. 56
      ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs
  22. 9
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs
  23. 11
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  24. 45
      ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs
  25. 31
      ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs
  26. 76
      ICSharpCode.Decompiler/TypeSystem/TupleType.cs
  27. 29
      ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs
  28. 8
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

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

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

2
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -280,7 +280,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -280,7 +280,7 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void TupleTypes([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void TupleTests([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}

33
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs → ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs

@ -17,10 +17,12 @@ @@ -17,10 +17,12 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class TupleTypes
public class TupleTests
{
public ValueTuple VT0;
public ValueTuple<int> VT1;
@ -44,5 +46,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -44,5 +46,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
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;
public Dictionary<(int a, string b), (string c, int d)> TupleDict;
public int VT1Member => VT1.Item1;
public int AccessUnnamed8 => Unnamed8.Item8;
public int AccessNamed8 => Named8.h;
public int AccessPartiallyNamed => PartiallyNamed.a + PartiallyNamed.Item3;
public ValueTuple<int> NewTuple1 => new ValueTuple<int>(1);
public (int a, int b) NewTuple2 => (1, 2);
public object BoxedTuple10 => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
public (uint, int) SwapUnnamed => (Unnamed2.Item2, Unnamed2.Item1);
public (uint, int) SwapNamed2 => (Named2.b, Named2.a);
public int TupleHash => (1, 2, 3).GetHashCode();
public int TupleHash2 => Named2.GetHashCode();
public void UseDict()
{
if (TupleDict.Count > 10) {
TupleDict.Clear();
}
// TODO: it would be nice if we could infer the name 'c' for the local
string item = TupleDict[(1, "abc")].c;
Console.WriteLine(item);
Console.WriteLine(item);
Console.WriteLine(TupleDict.Values.ToList().First().d);
}
}
}

344
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.opt.roslyn.il

@ -0,0 +1,344 @@ @@ -0,0 +1,344 @@
// 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 TupleTests
{
.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 TupleTests.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.TupleTests
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..
.field public class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> TupleDict
.custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 04 00 00 00 01 61 01 62 01 63 01 64 00 00 ) // .......a.b.c.d..
.method public hidebysig specialname instance int32
get_VT1Member() cil managed
{
// Code size 12 (0xc)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`1<int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::VT1
IL_0006: ldfld !0 valuetype [mscorlib]System.ValueTuple`1<int32>::Item1
IL_000b: ret
} // end of method TupleTests::get_VT1Member
.method public hidebysig specialname instance int32
get_AccessUnnamed8() cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed8
IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>>::Rest
IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1<int32>::Item1
IL_0010: ret
} // end of method TupleTests::get_AccessUnnamed8
.method public hidebysig specialname instance int32
get_AccessNamed8() cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named8
IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>>::Rest
IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1<int32>::Item1
IL_0010: ret
} // end of method TupleTests::get_AccessNamed8
.method public hidebysig specialname instance int32
get_AccessPartiallyNamed() cil managed
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed
IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32>::Item2
IL_000b: ldarg.0
IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed
IL_0011: ldfld !2 valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32>::Item3
IL_0016: add
IL_0017: ret
} // end of method TupleTests::get_AccessPartiallyNamed
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`1<int32>
get_NewTuple1() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: newobj instance void valuetype [mscorlib]System.ValueTuple`1<int32>::.ctor(!0)
IL_0006: ret
} // end of method TupleTests::get_NewTuple1
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
get_NewTuple2() cil managed
{
.param [0]
.custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b..
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: newobj instance void valuetype [mscorlib]System.ValueTuple`2<int32,int32>::.ctor(!0,
!1)
IL_0007: ret
} // end of method TupleTests::get_NewTuple2
.method public hidebysig specialname instance object
get_BoxedTuple10() cil managed
{
// Code size 28 (0x1c)
.maxstack 10
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: ldc.i4.3
IL_0003: ldc.i4.4
IL_0004: ldc.i4.5
IL_0005: ldc.i4.6
IL_0006: ldc.i4.7
IL_0007: ldc.i4.8
IL_0008: ldc.i4.s 9
IL_000a: ldc.i4.s 10
IL_000c: newobj instance void valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>::.ctor(!0,
!1,
!2)
IL_0011: newobj instance void valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>>::.ctor(!0,
!1,
!2,
!3,
!4,
!5,
!6,
!7)
IL_0016: box valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>>
IL_001b: ret
} // end of method TupleTests::get_BoxedTuple10
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
get_SwapUnnamed() cil managed
{
// Code size 28 (0x1c)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2
IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item2
IL_000b: ldarg.0
IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2
IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item1
IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2<uint32,int32>::.ctor(!0,
!1)
IL_001b: ret
} // end of method TupleTests::get_SwapUnnamed
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
get_SwapNamed2() cil managed
{
// Code size 28 (0x1c)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2
IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item2
IL_000b: ldarg.0
IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2
IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item1
IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2<uint32,int32>::.ctor(!0,
!1)
IL_001b: ret
} // end of method TupleTests::get_SwapNamed2
.method public hidebysig specialname instance int32
get_TupleHash() cil managed
{
// Code size 23 (0x17)
.maxstack 3
.locals init (valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32> V_0)
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: ldc.i4.3
IL_0003: newobj instance void valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>::.ctor(!0,
!1,
!2)
IL_0008: stloc.0
IL_0009: ldloca.s V_0
IL_000b: constrained. valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>
IL_0011: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
IL_0016: ret
} // end of method TupleTests::get_TupleHash
.method public hidebysig specialname instance int32
get_TupleHash2() cil managed
{
// Code size 18 (0x12)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2
IL_0006: constrained. valuetype [mscorlib]System.ValueTuple`2<int32,uint32>
IL_000c: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
IL_0011: ret
} // end of method TupleTests::get_TupleHash2
.method public hidebysig instance void
UseDict() cil managed
{
// Code size 96 (0x60)
.maxstack 3
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_0006: callvirt instance int32 class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::get_Count()
IL_000b: ldc.i4.s 10
IL_000d: ble.s IL_001a
IL_000f: ldarg.0
IL_0010: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_0015: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::Clear()
IL_001a: ldarg.0
IL_001b: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_0020: ldc.i4.1
IL_0021: ldstr "abc"
IL_0026: newobj instance void valuetype [mscorlib]System.ValueTuple`2<int32,string>::.ctor(!0,
!1)
IL_002b: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::get_Item(!0)
IL_0030: ldfld !0 valuetype [mscorlib]System.ValueTuple`2<string,int32>::Item1
IL_0035: dup
IL_0036: call void [mscorlib]System.Console::WriteLine(string)
IL_003b: call void [mscorlib]System.Console::WriteLine(string)
IL_0040: ldarg.0
IL_0041: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_0046: callvirt instance class [mscorlib]System.Collections.Generic.Dictionary`2/ValueCollection<!0,!1> class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::get_Values()
IL_004b: call class [mscorlib]System.Collections.Generic.List`1<!!0> [System.Core]System.Linq.Enumerable::ToList<valuetype [mscorlib]System.ValueTuple`2<string,int32>>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0050: call !!0 [System.Core]System.Linq.Enumerable::First<valuetype [mscorlib]System.ValueTuple`2<string,int32>>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0055: ldfld !1 valuetype [mscorlib]System.ValueTuple`2<string,int32>::Item2
IL_005a: call void [mscorlib]System.Console::WriteLine(int32)
IL_005f: ret
} // end of method TupleTests::UseDict
.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 TupleTests::.ctor
.property instance int32 VT1Member()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_VT1Member()
} // end of property TupleTests::VT1Member
.property instance int32 AccessUnnamed8()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessUnnamed8()
} // end of property TupleTests::AccessUnnamed8
.property instance int32 AccessNamed8()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessNamed8()
} // end of property TupleTests::AccessNamed8
.property instance int32 AccessPartiallyNamed()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessPartiallyNamed()
} // end of property TupleTests::AccessPartiallyNamed
.property instance valuetype [mscorlib]System.ValueTuple`1<int32>
NewTuple1()
{
.get instance valuetype [mscorlib]System.ValueTuple`1<int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_NewTuple1()
} // end of property TupleTests::NewTuple1
.property instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
NewTuple2()
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b..
.get instance valuetype [mscorlib]System.ValueTuple`2<int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_NewTuple2()
} // end of property TupleTests::NewTuple2
.property instance object BoxedTuple10()
{
.get instance object ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_BoxedTuple10()
} // end of property TupleTests::BoxedTuple10
.property instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
SwapUnnamed()
{
.get instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_SwapUnnamed()
} // end of property TupleTests::SwapUnnamed
.property instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
SwapNamed2()
{
.get instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_SwapNamed2()
} // end of property TupleTests::SwapNamed2
.property instance int32 TupleHash()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_TupleHash()
} // end of property TupleTests::TupleHash
.property instance int32 TupleHash2()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_TupleHash2()
} // end of property TupleTests::TupleHash2
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

359
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il

@ -0,0 +1,359 @@ @@ -0,0 +1,359 @@
// 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 TupleTests
{
.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 TupleTests.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.TupleTests
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..
.field public class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> TupleDict
.custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 04 00 00 00 01 61 01 62 01 63 01 64 00 00 ) // .......a.b.c.d..
.method public hidebysig specialname instance int32
get_VT1Member() cil managed
{
// Code size 12 (0xc)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`1<int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::VT1
IL_0006: ldfld !0 valuetype [mscorlib]System.ValueTuple`1<int32>::Item1
IL_000b: ret
} // end of method TupleTests::get_VT1Member
.method public hidebysig specialname instance int32
get_AccessUnnamed8() cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed8
IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>>::Rest
IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1<int32>::Item1
IL_0010: ret
} // end of method TupleTests::get_AccessUnnamed8
.method public hidebysig specialname instance int32
get_AccessNamed8() cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named8
IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`1<int32>>::Rest
IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1<int32>::Item1
IL_0010: ret
} // end of method TupleTests::get_AccessNamed8
.method public hidebysig specialname instance int32
get_AccessPartiallyNamed() cil managed
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed
IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32>::Item2
IL_000b: ldarg.0
IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed
IL_0011: ldfld !2 valuetype [mscorlib]System.ValueTuple`5<int32,int32,int32,int32,int32>::Item3
IL_0016: add
IL_0017: ret
} // end of method TupleTests::get_AccessPartiallyNamed
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`1<int32>
get_NewTuple1() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: newobj instance void valuetype [mscorlib]System.ValueTuple`1<int32>::.ctor(!0)
IL_0006: ret
} // end of method TupleTests::get_NewTuple1
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
get_NewTuple2() cil managed
{
.param [0]
.custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b..
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: newobj instance void valuetype [mscorlib]System.ValueTuple`2<int32,int32>::.ctor(!0,
!1)
IL_0007: ret
} // end of method TupleTests::get_NewTuple2
.method public hidebysig specialname instance object
get_BoxedTuple10() cil managed
{
// Code size 28 (0x1c)
.maxstack 10
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: ldc.i4.3
IL_0003: ldc.i4.4
IL_0004: ldc.i4.5
IL_0005: ldc.i4.6
IL_0006: ldc.i4.7
IL_0007: ldc.i4.8
IL_0008: ldc.i4.s 9
IL_000a: ldc.i4.s 10
IL_000c: newobj instance void valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>::.ctor(!0,
!1,
!2)
IL_0011: newobj instance void valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>>::.ctor(!0,
!1,
!2,
!3,
!4,
!5,
!6,
!7)
IL_0016: box valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>>
IL_001b: ret
} // end of method TupleTests::get_BoxedTuple10
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
get_SwapUnnamed() cil managed
{
// Code size 28 (0x1c)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2
IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item2
IL_000b: ldarg.0
IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2
IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item1
IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2<uint32,int32>::.ctor(!0,
!1)
IL_001b: ret
} // end of method TupleTests::get_SwapUnnamed
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
get_SwapNamed2() cil managed
{
// Code size 28 (0x1c)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2
IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item2
IL_000b: ldarg.0
IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2
IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2<int32,uint32>::Item1
IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2<uint32,int32>::.ctor(!0,
!1)
IL_001b: ret
} // end of method TupleTests::get_SwapNamed2
.method public hidebysig specialname instance int32
get_TupleHash() cil managed
{
// Code size 23 (0x17)
.maxstack 3
.locals init (valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32> V_0)
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: ldc.i4.3
IL_0003: newobj instance void valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>::.ctor(!0,
!1,
!2)
IL_0008: stloc.0
IL_0009: ldloca.s V_0
IL_000b: constrained. valuetype [mscorlib]System.ValueTuple`3<int32,int32,int32>
IL_0011: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
IL_0016: ret
} // end of method TupleTests::get_TupleHash
.method public hidebysig specialname instance int32
get_TupleHash2() cil managed
{
// Code size 18 (0x12)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2<int32,uint32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2
IL_0006: constrained. valuetype [mscorlib]System.ValueTuple`2<int32,uint32>
IL_000c: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
IL_0011: ret
} // end of method TupleTests::get_TupleHash2
.method public hidebysig instance void
UseDict() cil managed
{
// Code size 109 (0x6d)
.maxstack 3
.locals init (string V_0,
bool V_1)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_0007: callvirt instance int32 class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::get_Count()
IL_000c: ldc.i4.s 10
IL_000e: cgt
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: brfalse.s IL_0022
IL_0014: nop
IL_0015: ldarg.0
IL_0016: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_001b: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::Clear()
IL_0020: nop
IL_0021: nop
IL_0022: ldarg.0
IL_0023: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_0028: ldc.i4.1
IL_0029: ldstr "abc"
IL_002e: newobj instance void valuetype [mscorlib]System.ValueTuple`2<int32,string>::.ctor(!0,
!1)
IL_0033: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::get_Item(!0)
IL_0038: ldfld !0 valuetype [mscorlib]System.ValueTuple`2<string,int32>::Item1
IL_003d: stloc.0
IL_003e: ldloc.0
IL_003f: call void [mscorlib]System.Console::WriteLine(string)
IL_0044: nop
IL_0045: ldloc.0
IL_0046: call void [mscorlib]System.Console::WriteLine(string)
IL_004b: nop
IL_004c: ldarg.0
IL_004d: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict
IL_0052: callvirt instance class [mscorlib]System.Collections.Generic.Dictionary`2/ValueCollection<!0,!1> class [mscorlib]System.Collections.Generic.Dictionary`2<valuetype [mscorlib]System.ValueTuple`2<int32,string>,valuetype [mscorlib]System.ValueTuple`2<string,int32>>::get_Values()
IL_0057: call class [mscorlib]System.Collections.Generic.List`1<!!0> [System.Core]System.Linq.Enumerable::ToList<valuetype [mscorlib]System.ValueTuple`2<string,int32>>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_005c: call !!0 [System.Core]System.Linq.Enumerable::First<valuetype [mscorlib]System.ValueTuple`2<string,int32>>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0061: ldfld !1 valuetype [mscorlib]System.ValueTuple`2<string,int32>::Item2
IL_0066: call void [mscorlib]System.Console::WriteLine(int32)
IL_006b: nop
IL_006c: ret
} // end of method TupleTests::UseDict
.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 TupleTests::.ctor
.property instance int32 VT1Member()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_VT1Member()
} // end of property TupleTests::VT1Member
.property instance int32 AccessUnnamed8()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessUnnamed8()
} // end of property TupleTests::AccessUnnamed8
.property instance int32 AccessNamed8()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessNamed8()
} // end of property TupleTests::AccessNamed8
.property instance int32 AccessPartiallyNamed()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessPartiallyNamed()
} // end of property TupleTests::AccessPartiallyNamed
.property instance valuetype [mscorlib]System.ValueTuple`1<int32>
NewTuple1()
{
.get instance valuetype [mscorlib]System.ValueTuple`1<int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_NewTuple1()
} // end of property TupleTests::NewTuple1
.property instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
NewTuple2()
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b..
.get instance valuetype [mscorlib]System.ValueTuple`2<int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_NewTuple2()
} // end of property TupleTests::NewTuple2
.property instance object BoxedTuple10()
{
.get instance object ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_BoxedTuple10()
} // end of property TupleTests::BoxedTuple10
.property instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
SwapUnnamed()
{
.get instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_SwapUnnamed()
} // end of property TupleTests::SwapUnnamed
.property instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32>
SwapNamed2()
{
.get instance valuetype [mscorlib]System.ValueTuple`2<uint32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_SwapNamed2()
} // end of property TupleTests::SwapNamed2
.property instance int32 TupleHash()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_TupleHash()
} // end of property TupleTests::TupleHash
.property instance int32 TupleHash2()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_TupleHash2()
} // end of property TupleTests::TupleHash2
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

91
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il

@ -1,91 +0,0 @@ @@ -1,91 +0,0 @@
// 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

@ -1,92 +0,0 @@ @@ -1,92 +0,0 @@
// 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 ***********************

10
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1374,6 +1374,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1374,6 +1374,16 @@ namespace ICSharpCode.Decompiler.CSharp
HasNullableSpecifier = true
};
}
if (CecilLoader.IsValueTuple(gType, out int tupleCardinality) && tupleCardinality > 1 && tupleCardinality < TupleType.RestPosition) {
var tupleType = new TupleAstType();
foreach (var typeArgument in gType.GenericArguments) {
typeIndex++;
tupleType.Elements.Add(new TupleTypeElement {
Type = ConvertType(typeArgument, typeAttributes, ref typeIndex, options)
});
}
return tupleType;
}
AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions);
List<AstType> typeArguments = new List<AstType>();
foreach (var typeArgument in gType.GenericArguments) {

97
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -56,6 +56,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -56,6 +56,19 @@ namespace ICSharpCode.Decompiler.CSharp
if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj, true)) {
return HandleDelegateConstruction(newobj);
}
if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2) {
var tupleType = TupleType.FromUnderlyingType(typeSystem.Compilation, inst.Method.DeclaringType);
Debug.Assert(tupleType != null, "MatchTupleConstruction should not success unless we got a valid tuple type.");
Debug.Assert(tupleType.ElementTypes.Length == tupleElements.Length);
var tuple = new TupleExpression();
foreach (var (element, elementType) in tupleElements.Zip(tupleType.ElementTypes)) {
tuple.Elements.Add(
expressionBuilder.Translate(element, elementType)
.ConvertTo(elementType, expressionBuilder)
);
}
return tuple.WithRR(new ResolveResult(tupleType)).WithILInstruction(inst);
}
return Build(inst.OpCode, inst.Method, inst.Arguments, inst.ConstrainedTo).WithILInstruction(inst);
}
@ -70,7 +83,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -70,7 +83,11 @@ namespace ICSharpCode.Decompiler.CSharp
if (callOpCode == OpCode.NewObj) {
target = default(TranslatedExpression); // no target
} else {
target = expressionBuilder.TranslateTarget(method, callArguments.FirstOrDefault(), callOpCode == OpCode.Call, constrainedTo);
target = expressionBuilder.TranslateTarget(
callArguments.FirstOrDefault(),
nonVirtualInvocation: callOpCode == OpCode.Call,
memberStatic: method.IsStatic,
memberDeclaringType: constrainedTo ?? method.DeclaringType);
if (constrainedTo == null
&& target.Expression is CastExpression cast
&& target.ResolveResult is ConversionResolveResult conversion
@ -116,7 +133,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -116,7 +133,7 @@ namespace ICSharpCode.Decompiler.CSharp
expandedArguments.Add(expressionBuilder.GetDefaultValueExpression(elementType).WithoutILInstruction());
}
}
if (IsUnambiguousCall(expectedTargetDetails, method, target.ResolveResult, Empty<IType>.Array, expandedArguments) == OverloadResolutionErrors.None) {
if (IsUnambiguousCall(expectedTargetDetails, method, target.ResolveResult, Empty<IType>.Array, expandedArguments, out _) == OverloadResolutionErrors.None) {
isExpandedForm = true;
expectedParameters = expandedParameters;
arguments = expandedArguments.SelectList(a => new TranslatedExpression(a.Expression.Detach()));
@ -155,9 +172,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -155,9 +172,8 @@ namespace ICSharpCode.Decompiler.CSharp
var argumentResolveResults = arguments.Select(arg => arg.ResolveResult).ToList();
ResolveResult rr = new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm);
if (callOpCode == OpCode.NewObj) {
ResolveResult rr = new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm);
if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType()) {
var argumentExpressions = arguments.SelectArray(arg => arg.Expression);
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
@ -175,7 +191,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -175,7 +191,7 @@ namespace ICSharpCode.Decompiler.CSharp
return atce
.WithRR(rr);
} else {
if (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array, arguments) != OverloadResolutionErrors.None) {
if (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array, arguments, out _) != OverloadResolutionErrors.None) {
for (int i = 0; i < arguments.Count; i++) {
if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) {
if (arguments[i].Expression is LambdaExpression lambda) {
@ -194,10 +210,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -194,10 +210,11 @@ namespace ICSharpCode.Decompiler.CSharp
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) {
return HandleAccessorCall(expectedTargetDetails, method, target, arguments.ToList());
} else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) {
return new InvocationExpression(target, arguments.Select(arg => arg.Expression)).WithRR(rr);
return new InvocationExpression(target, arguments.Select(arg => arg.Expression))
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm));
} else if (IsDelegateEqualityComparison(method, arguments)) {
return HandleDelegateEqualityComparison(method, arguments)
.WithRR(rr);
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm));
} else if (method.IsOperator && method.Name == "op_Implicit" && arguments.Count == 1) {
return HandleImplicitConversion(method, arguments[0]);
} else {
@ -219,8 +236,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -219,8 +236,9 @@ namespace ICSharpCode.Decompiler.CSharp
bool argumentsCasted = false;
IType[] typeArguments = Empty<IType>.Array;
var targetResolveResult = requireTarget ? target.ResolveResult : null;
IParameterizedMember foundMethod;
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments)) != OverloadResolutionErrors.None) {
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, out foundMethod)) != OverloadResolutionErrors.None) {
switch (errors) {
case OverloadResolutionErrors.TypeInferenceFailed:
case OverloadResolutionErrors.WrongNumberOfTypeArguments:
@ -257,8 +275,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -257,8 +275,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
continue;
}
// We've given up.
foundMethod = method;
break;
}
// Note: after this loop, 'method' and 'foundMethod' may differ,
// but as far as allowed by IsAppropriateCallTarget().
Expression targetExpr;
string methodName = method.Name;
@ -282,7 +304,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -282,7 +304,8 @@ namespace ICSharpCode.Decompiler.CSharp
if (requireTypeArguments && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType())))
typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
var argumentExpressions = arguments.Select(arg => arg.Expression);
return new InvocationExpression(targetExpr, argumentExpressions).WithRR(rr);
return new InvocationExpression(targetExpr, argumentExpressions)
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod, argumentResolveResults, isExpandedForm: isExpandedForm));
}
}
}
@ -352,8 +375,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -352,8 +375,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
ResolveResult target, IType[] typeArguments, IList<TranslatedExpression> arguments)
ResolveResult target, IType[] typeArguments, IList<TranslatedExpression> arguments,
out IParameterizedMember foundMember)
{
foundMember = null;
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var or = new OverloadResolution(resolver.Compilation, arguments.SelectArray(a => a.ResolveResult), typeArguments: typeArguments);
if (expectedTargetDetails.CallOpCode == OpCode.NewObj) {
@ -375,7 +400,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -375,7 +400,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
if (or.BestCandidateErrors != OverloadResolutionErrors.None)
return or.BestCandidateErrors;
if (!IsAppropriateCallTarget(expectedTargetDetails, method, or.GetBestCandidateWithSubstitutedTypeArguments()))
if (or.IsAmbiguous)
return OverloadResolutionErrors.AmbiguousMatch;
foundMember = or.GetBestCandidateWithSubstitutedTypeArguments();
if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMember))
return OverloadResolutionErrors.AmbiguousMatch;
return OverloadResolutionErrors.None;
}
@ -398,16 +426,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -398,16 +426,31 @@ namespace ICSharpCode.Decompiler.CSharp
return true;
}
bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method)
bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, out IMember foundMember)
{
foundMember = null;
MemberResolveResult result;
if (target == null) {
var result = resolver.ResolveSimpleName(method.AccessorOwner.Name, EmptyList<IType>.Instance, isInvocationTarget: false) as MemberResolveResult;
return !(result == null || result.IsError || !IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, result.Member));
result = resolver.ResolveSimpleName(method.AccessorOwner.Name, EmptyList<IType>.Instance, isInvocationTarget: false) as MemberResolveResult;
} else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target, method.AccessorOwner.Name, EmptyList<IType>.Instance, isInvocation: false) as MemberResolveResult;
return !(result == null || result.IsError || !IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, result.Member));
if (method.AccessorOwner.SymbolKind == SymbolKind.Indexer) {
// TODO: use OR here, etc.
result = null;
foreach (var methodList in lookup.LookupIndexers(target)) {
foreach (var indexer in methodList) {
if (IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, indexer)) {
foundMember = indexer;
return true;
}
}
}
} else {
result = lookup.Lookup(target, method.AccessorOwner.Name, EmptyList<IType>.Instance, isInvocation: false) as MemberResolveResult;
}
}
foundMember = result?.Member;
return !(result == null || result.IsError || !IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, result.Member));
}
ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, TranslatedExpression target, IList<TranslatedExpression> arguments)
@ -417,7 +460,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -417,7 +460,8 @@ namespace ICSharpCode.Decompiler.CSharp
bool targetCasted = false;
var targetResolveResult = requireTarget ? target.ResolveResult : null;
while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method)) {
IMember foundMember;
while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method, out foundMember)) {
if (!requireTarget) {
requireTarget = true;
targetResolveResult = target.ResolveResult;
@ -426,11 +470,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -426,11 +470,12 @@ namespace ICSharpCode.Decompiler.CSharp
target = target.ConvertTo(method.AccessorOwner.DeclaringType, expressionBuilder);
targetResolveResult = target.ResolveResult;
} else {
foundMember = method.AccessorOwner;
break;
}
}
var rr = new MemberResolveResult(target.ResolveResult, method.AccessorOwner);
var rr = new MemberResolveResult(target.ResolveResult, foundMember);
if (method.ReturnType.IsKnownType(KnownTypeCode.Void)) {
var value = arguments.Last();
@ -473,16 +518,23 @@ namespace ICSharpCode.Decompiler.CSharp @@ -473,16 +518,23 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
static readonly NormalizeTypeVisitor typeErasure = new NormalizeTypeVisitor {
ReplaceClassTypeParametersWithDummy = false,
ReplaceMethodTypeParametersWithDummy = false,
DynamicAndObject = true,
TupleToUnderlyingType = true
};
bool IsAppropriateCallTarget(ExpectedTargetDetails expectedTargetDetails, IMember expectedTarget, IMember actualTarget)
{
if (expectedTarget.Equals(actualTarget))
if (expectedTarget.Equals(actualTarget, typeErasure))
return true;
if (expectedTargetDetails.CallOpCode == OpCode.CallVirt && actualTarget.IsOverride) {
if (expectedTargetDetails.NeedsBoxingConversion && actualTarget.DeclaringType.IsReferenceType != true)
return false;
foreach (var possibleTarget in InheritanceHelper.GetBaseMembers(actualTarget, false)) {
if (expectedTarget.Equals(possibleTarget))
if (expectedTarget.Equals(possibleTarget, typeErasure))
return true;
if (!possibleTarget.IsOverride)
break;
@ -516,7 +568,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -516,7 +568,10 @@ namespace ICSharpCode.Decompiler.CSharp
requireTarget = true;
} else {
targetType = method.DeclaringType;
target = expressionBuilder.TranslateTarget(method, inst.Arguments[0], func.OpCode == OpCode.LdFtn);
target = expressionBuilder.TranslateTarget(inst.Arguments[0],
nonVirtualInvocation: func.OpCode == OpCode.LdFtn,
memberStatic: method.IsStatic,
memberDeclaringType: method.DeclaringType);
target = ExpressionBuilder.UnwrapBoxingConversion(target);
requireTarget = expressionBuilder.HidesVariableWithName(method.Name)
|| (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression));

47
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -194,7 +194,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -194,7 +194,10 @@ namespace ICSharpCode.Decompiler.CSharp
ExpressionWithResolveResult ConvertField(IField field, ILInstruction targetInstruction = null)
{
var target = TranslateTarget(field, targetInstruction, true);
var target = TranslateTarget(targetInstruction,
nonVirtualInvocation: true,
memberStatic: field.IsStatic,
memberDeclaringType: field.DeclaringType);
bool requireTarget = HidesVariableWithName(field.Name)
|| (field.IsStatic ? !IsCurrentOrContainingType(field.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression || target.Expression is BaseReferenceExpression));
bool targetCasted = false;
@ -1610,20 +1613,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1610,20 +1613,21 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
internal TranslatedExpression TranslateTarget(IMember member, ILInstruction target, bool nonVirtualInvocation, IType constrainedTo = null)
internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation,
bool memberStatic, IType memberDeclaringType)
{
// If references are missing member.IsStatic might not be set correctly.
// Additionally check target for null, in order to avoid a crash.
if (!member.IsStatic && target != null) {
if (nonVirtualInvocation && target.MatchLdThis() && member.DeclaringTypeDefinition != resolver.CurrentTypeDefinition) {
if (!memberStatic && target != null) {
if (nonVirtualInvocation && target.MatchLdThis() && memberDeclaringType.GetDefinition() != resolver.CurrentTypeDefinition) {
return new BaseReferenceExpression()
.WithILInstruction(target)
.WithRR(new ThisResolveResult(member.DeclaringType, nonVirtualInvocation));
.WithRR(new ThisResolveResult(memberDeclaringType, nonVirtualInvocation));
} else {
var translatedTarget = Translate(target, constrainedTo ?? member.DeclaringType);
if (CallInstruction.ExpectedTypeForThisPointer(constrainedTo ?? member.DeclaringType) == StackType.Ref && translatedTarget.Type.GetStackType().IsIntegerType()) {
var translatedTarget = Translate(target, memberDeclaringType);
if (CallInstruction.ExpectedTypeForThisPointer(memberDeclaringType) == StackType.Ref && translatedTarget.Type.GetStackType().IsIntegerType()) {
// when accessing members on value types, ensure we use a reference and not a pointer
translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(constrainedTo ?? member.DeclaringType), this);
translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(memberDeclaringType), this);
}
if (translatedTarget.Expression is DirectionExpression) {
// (ref x).member => x.member
@ -1642,9 +1646,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1642,9 +1646,9 @@ namespace ICSharpCode.Decompiler.CSharp
return translatedTarget;
}
} else {
return new TypeReferenceExpression(ConvertType(member.DeclaringType))
return new TypeReferenceExpression(ConvertType(memberDeclaringType))
.WithoutILInstruction()
.WithRR(new TypeResolveResult(member.DeclaringType));
.WithRR(new TypeResolveResult(memberDeclaringType));
}
}
@ -1752,7 +1756,26 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1752,7 +1756,26 @@ namespace ICSharpCode.Decompiler.CSharp
return result;
}
}
var expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst);
TranslatedExpression expr;
if (TupleTransform.MatchTupleFieldAccess(inst, out IType underlyingTupleType, out var target, out int position)) {
var translatedTarget = TranslateTarget(target,
nonVirtualInvocation: true,
memberStatic: false,
memberDeclaringType: underlyingTupleType);
if (translatedTarget.Type is TupleType tupleType && tupleType.UnderlyingType.Equals(underlyingTupleType) && position <= tupleType.ElementNames.Length) {
string elementName = tupleType.ElementNames[position - 1];
if (elementName == null) {
elementName = "Item" + position;
}
expr = new MemberReferenceExpression(translatedTarget, elementName)
.WithRR(new MemberResolveResult(translatedTarget.ResolveResult, inst.Field))
.WithILInstruction(inst);
} else {
expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst);
}
} else {
expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst);
}
if (inst.ResultType == StackType.I) {
// ldflda producing native pointer
return new UnaryOperatorExpression(UnaryOperatorType.AddressOf, expr)
@ -1760,7 +1783,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1760,7 +1783,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
// ldflda producing managed pointer
return new DirectionExpression(FieldDirection.Ref, expr)
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false));
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false));
}
}

7
ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs

@ -255,6 +255,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -255,6 +255,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
b.Append(')');
return b.ToString();
}
bool IMember.Equals(IMember obj, TypeVisitor typeNormalization)
{
return this == obj;
}
}
#endregion
@ -1063,7 +1068,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1063,7 +1068,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (IsComparisonOperator(nonLiftedMethod))
this.ReturnType = nonLiftedMethod.ReturnType;
else
this.ReturnType = nonLiftedMethod.ReturnType.AcceptVisitor(substitution);
this.ReturnType = NullableType.Create(compilation, nonLiftedMethod.ReturnType.AcceptVisitor(substitution));
}
public IReadOnlyList<IParameter> NonLiftedParameters => nonLiftedOperator.Parameters;

8
ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs

@ -45,6 +45,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -45,6 +45,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
this.baseMethod = baseMethod;
}
public bool Equals(IMember obj, TypeVisitor typeNormalization)
{
var other = obj as ReducedExtensionMethod;
if (other == null)
return false;
return baseMethod.Equals(other.baseMethod, typeNormalization);
}
public override bool Equals(object obj)
{
var other = obj as ReducedExtensionMethod;

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -305,6 +305,7 @@ @@ -305,6 +305,7 @@
<Compile Include="IL\Transforms\SwitchOnNullableTransform.cs" />
<Compile Include="IL\Transforms\SwitchOnStringTransform.cs" />
<Compile Include="IL\Transforms\TransformExpressionTrees.cs" />
<Compile Include="IL\Transforms\TupleTransform.cs" />
<Compile Include="IL\Transforms\UsingTransform.cs" />
<Compile Include="IL\ControlFlow\AsyncAwaitDecompiler.cs" />
<Compile Include="IL\ControlFlow\ControlFlowGraph.cs" />
@ -325,6 +326,7 @@ @@ -325,6 +326,7 @@
<Compile Include="IL\Transforms\StatementTransform.cs" />
<Compile Include="IL\Transforms\TransformCollectionAndObjectInitializers.cs" />
<Compile Include="Output\TextTokenWriter.cs" />
<Compile Include="TypeSystem\NormalizeTypeVisitor.cs" />
<Compile Include="TypeSystem\TupleType.cs" />
<Compile Include="Util\GraphVizGraph.cs" />
<Compile Include="Util\KeyComparer.cs" />

18
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -283,24 +283,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -283,24 +283,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return block;
}
static bool CompareTypes(IType a, IType b)
{
IType type1 = DummyTypeParameter.NormalizeAllTypeParameters(a);
IType type2 = DummyTypeParameter.NormalizeAllTypeParameters(b);
return type1.Equals(type2);
}
static bool CompareSignatures(IList<IParameter> parameters, IList<IParameter> otherParameters)
{
if (otherParameters.Count != parameters.Count)
return false;
for (int i = 0; i < otherParameters.Count; i++) {
if (!CompareTypes(otherParameters[i].Type, parameters[i].Type))
return false;
}
return true;
}
internal static bool MatchNewArr(ILInstruction instruction, out IType arrayType, out int[] length)
{
NewArr newArr = instruction as NewArr;

93
ICSharpCode.Decompiler/IL/Transforms/TupleTransform.cs

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
class TupleTransform
{
/// <summary>
/// Matches an 'ldflda' instruction accessing a tuple element.
///
/// E.g. matches:
/// <c>ldflda Item1(ldflda Rest(target))</c>
/// </summary>
public static bool MatchTupleFieldAccess(LdFlda inst, out IType tupleType, out ILInstruction target, out int position)
{
tupleType = inst.Field.DeclaringType;
target = inst.Target;
if (!inst.Field.Name.StartsWith("Item", StringComparison.Ordinal)) {
position = 0;
return false;
}
if (!int.TryParse(inst.Field.Name.Substring(4), out position))
return false;
if (!TupleType.IsTupleCompatible(tupleType, out _))
return false;
while (target is LdFlda ldflda && ldflda.Field.Name == "Rest" && TupleType.IsTupleCompatible(ldflda.Field.DeclaringType, out _)) {
tupleType = ldflda.Field.DeclaringType;
target = ldflda.Target;
position += TupleType.RestPosition - 1;
}
return true;
}
/// <summary>
/// Matches 'newobj TupleType(...)'.
/// Takes care of flattening long tuples.
/// </summary>
public static bool MatchTupleConstruction(NewObj newobj, out ILInstruction[] arguments)
{
arguments = null;
if (newobj == null)
return false;
if (!TupleType.IsTupleCompatible(newobj.Method.DeclaringType, out int elementCount))
return false;
arguments = new ILInstruction[elementCount];
int outIndex = 0;
while (elementCount >= TupleType.RestPosition) {
if (newobj.Arguments.Count != TupleType.RestPosition)
return false;
for (int pos = 1; pos < TupleType.RestPosition; pos++) {
arguments[outIndex++] = newobj.Arguments[pos-1];
}
elementCount -= TupleType.RestPosition - 1;
Debug.Assert(outIndex + elementCount == arguments.Length);
newobj = newobj.Arguments.Last() as NewObj;
if (newobj == null)
return false;
if (!TupleType.IsTupleCompatible(newobj.Method.DeclaringType, out int restElementCount))
return false;
if (restElementCount != elementCount)
return false;
}
Debug.Assert(outIndex + elementCount == arguments.Length);
if (newobj.Arguments.Count != elementCount)
return false;
for (int i = 0; i < elementCount; i++) {
arguments[outIndex++] = newobj.Arguments[i];
}
return true;
}
}
}

9
ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs

@ -363,6 +363,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -363,6 +363,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
ITypeReference baseType = CreateType(gType.ElementType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true);
if (UseTupleTypes && IsValueTuple(gType, out int tupleCardinality)) {
if (tupleCardinality > 1) {
var assemblyRef = GetAssemblyReference(gType.ElementType.Scope);
var elementNames = GetTupleElementNames(typeAttributes, tupleTypeIndex, tupleCardinality);
tupleTypeIndex += tupleCardinality;
ITypeReference[] elementTypeRefs = new ITypeReference[tupleCardinality];
@ -385,7 +386,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -385,7 +386,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
gType = null;
}
} while (gType != null);
return new TupleTypeReference(elementTypeRefs.ToImmutableArray(), elementNames);
return new TupleTypeReference(
elementTypeRefs.ToImmutableArray(), elementNames,
assemblyRef);
} else {
// C# doesn't have syntax for tuples of cardinality <= 1
tupleTypeIndex += tupleCardinality;
@ -451,10 +454,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -451,10 +454,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
static bool IsValueTuple(GenericInstanceType gType, out int tupleCardinality)
static internal bool IsValueTuple(GenericInstanceType gType, out int tupleCardinality)
{
tupleCardinality = 0;
if (gType == null || !gType.Name.StartsWith("ValueTuple`", StringComparison.Ordinal) || gType.Namespace != "System")
if (gType == null || gType.DeclaringType != 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)) {

11
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -311,11 +311,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -311,11 +311,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return CreateFakeMethod(methodReference);
}
static readonly NormalizeTypeVisitor normalizeTypeVisitor = new NormalizeTypeVisitor {
ReplaceClassTypeParametersWithDummy = true,
ReplaceMethodTypeParametersWithDummy = true,
};
static bool CompareTypes(IType a, IType b)
{
IType type1 = DummyTypeParameter.NormalizeAllTypeParameters(a);
IType type2 = DummyTypeParameter.NormalizeAllTypeParameters(b);
IType type1 = a.AcceptVisitor(normalizeTypeVisitor);
IType type2 = b.AcceptVisitor(normalizeTypeVisitor);
return type1.Equals(type2);
}

5
ICSharpCode.Decompiler/TypeSystem/IMember.cs

@ -172,5 +172,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -172,5 +172,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// If this member is already specialized, the new substitution is composed with the existing substition.
/// </summary>
IMember Specialize(TypeParameterSubstitution substitution);
/// <summary>
/// Gets whether the members are considered equal when applying the specified type normalization.
/// </summary>
bool Equals(IMember obj, TypeVisitor typeNormalization);
}
}

5
ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedMember.cs

@ -111,6 +111,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -111,6 +111,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
get { return TypeParameterSubstitution.Identity; }
}
public virtual bool Equals(IMember obj, TypeVisitor typeNormalization)
{
return Equals(obj);
}
public abstract IMember Specialize(TypeParameterSubstitution substitution);
internal IMethod GetAccessor(ref IMethod accessorField, IUnresolvedMethod unresolvedAccessor)

8
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultMemberReference.cs

@ -57,7 +57,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -57,7 +57,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public ITypeReference DeclaringTypeReference {
get { return typeReference; }
}
static readonly NormalizeTypeVisitor normalizeTypeVisitor = new NormalizeTypeVisitor();
public IMember Resolve(ITypeResolveContext context)
{
IType type = typeReference.Resolve(context);
@ -85,8 +87,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -85,8 +87,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
} else if (parameterTypes.Count == parameterizedMember.Parameters.Count) {
bool signatureMatches = true;
for (int i = 0; i < parameterTypes.Count; i++) {
IType type1 = DummyTypeParameter.NormalizeAllTypeParameters(resolvedParameterTypes[i]);
IType type2 = DummyTypeParameter.NormalizeAllTypeParameters(parameterizedMember.Parameters[i].Type);
IType type1 = resolvedParameterTypes[i].AcceptVisitor(normalizeTypeVisitor);
IType type2 = parameterizedMember.Parameters[i].Type.AcceptVisitor(normalizeTypeVisitor);
if (!type1.Equals(type2)) {
signatureMatches = false;
break;

56
ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs

@ -93,62 +93,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -93,62 +93,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
return tps[length];
}
sealed class NormalizeMethodTypeParametersVisitor : TypeVisitor
{
public override IType VisitTypeParameter(ITypeParameter type)
{
if (type.OwnerType == SymbolKind.Method) {
return DummyTypeParameter.GetMethodTypeParameter(type.Index);
} else {
return base.VisitTypeParameter(type);
}
}
}
sealed class NormalizeClassTypeParametersVisitor : TypeVisitor
{
public override IType VisitTypeParameter(ITypeParameter type)
{
if (type.OwnerType == SymbolKind.TypeDefinition) {
return DummyTypeParameter.GetClassTypeParameter(type.Index);
} else {
return base.VisitTypeParameter(type);
}
}
}
static readonly NormalizeMethodTypeParametersVisitor normalizeMethodTypeParameters = new NormalizeMethodTypeParametersVisitor();
static readonly NormalizeClassTypeParametersVisitor normalizeClassTypeParameters = new NormalizeClassTypeParametersVisitor();
/// <summary>
/// Replaces all occurrences of method type parameters in the given type
/// by normalized type parameters. This allows comparing parameter types from different
/// generic methods.
/// </summary>
public static IType NormalizeMethodTypeParameters(IType type)
{
return type.AcceptVisitor(normalizeMethodTypeParameters);
}
/// <summary>
/// Replaces all occurrences of class type parameters in the given type
/// by normalized type parameters. This allows comparing parameter types from different
/// generic methods.
/// </summary>
public static IType NormalizeClassTypeParameters(IType type)
{
return type.AcceptVisitor(normalizeClassTypeParameters);
}
/// <summary>
/// Replaces all occurrences of class and method type parameters in the given type
/// by normalized type parameters. This allows comparing parameter types from different
/// generic methods.
/// </summary>
public static IType NormalizeAllTypeParameters(IType type)
{
return type.AcceptVisitor(normalizeClassTypeParameters).AcceptVisitor(normalizeMethodTypeParameters);
}
readonly SymbolKind ownerType;
readonly int index;

9
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs

@ -258,6 +258,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -258,6 +258,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return baseMember.Specialize(TypeParameterSubstitution.Compose(newSubstitution, this.substitution));
}
public virtual bool Equals(IMember obj, TypeVisitor typeNormalization)
{
SpecializedMember other = obj as SpecializedMember;
if (other == null)
return false;
return this.baseMember.Equals(other.baseMember, typeNormalization)
&& this.substitution.Equals(other.substitution, typeNormalization);
}
public override bool Equals(object obj)
{
SpecializedMember other = obj as SpecializedMember;

11
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs

@ -147,7 +147,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -147,7 +147,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
accessorOwner = value;
}
}
public override bool Equals(IMember obj, TypeVisitor typeNormalization)
{
SpecializedMethod other = obj as SpecializedMethod;
if (other == null)
return false;
return this.baseMember.Equals(other.baseMember, typeNormalization)
&& this.substitutionWithoutSpecializedTypeParameters.Equals(other.substitutionWithoutSpecializedTypeParameters, typeNormalization);
}
public override bool Equals(object obj)
{
SpecializedMethod other = obj as SpecializedMethod;

45
ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.TypeSystem
{
sealed class NormalizeTypeVisitor : TypeVisitor
{
public bool ReplaceClassTypeParametersWithDummy = true;
public bool ReplaceMethodTypeParametersWithDummy = true;
public bool DynamicAndObject = true;
public bool TupleToUnderlyingType = true;
public override IType VisitTypeParameter(ITypeParameter type)
{
if (type.OwnerType == SymbolKind.Method && ReplaceMethodTypeParametersWithDummy) {
return DummyTypeParameter.GetMethodTypeParameter(type.Index);
} else if (type.OwnerType == SymbolKind.TypeDefinition && ReplaceClassTypeParametersWithDummy) {
return DummyTypeParameter.GetClassTypeParameter(type.Index);
} else {
return base.VisitTypeParameter(type);
}
}
public override IType VisitTypeDefinition(ITypeDefinition type)
{
if (DynamicAndObject && type.KnownTypeCode == KnownTypeCode.Object) {
// Instead of normalizing dynamic->object,
// we do this the opposite direction, so that we don't need a compilation to find the object type.
return SpecialType.Dynamic;
}
return base.VisitTypeDefinition(type);
}
public override IType VisitTupleType(TupleType type)
{
if (TupleToUnderlyingType) {
return type.UnderlyingType.AcceptVisitor(this);
} else {
return base.VisitTupleType(type);
}
}
}
}

31
ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs

@ -34,32 +34,13 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -34,32 +34,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
public sealed class ParameterListComparer : IEqualityComparer<IReadOnlyList<IParameter>>
{
public static readonly ParameterListComparer Instance = new ParameterListComparer();
sealed class NormalizeTypeVisitor : TypeVisitor
{
public override IType VisitTypeParameter(ITypeParameter type)
{
if (type.OwnerType == SymbolKind.Method) {
return DummyTypeParameter.GetMethodTypeParameter(type.Index);
} else {
return base.VisitTypeParameter(type);
}
}
public override IType VisitTypeDefinition(ITypeDefinition type)
{
if (type.KnownTypeCode == KnownTypeCode.Object)
return SpecialType.Dynamic;
return base.VisitTypeDefinition(type);
}
public override IType VisitTupleType(TupleType type)
{
return type.UnderlyingType.AcceptVisitor(this);
}
}
static readonly NormalizeTypeVisitor normalizationVisitor = new NormalizeTypeVisitor();
static readonly NormalizeTypeVisitor normalizationVisitor = new NormalizeTypeVisitor {
ReplaceClassTypeParametersWithDummy = false,
ReplaceMethodTypeParametersWithDummy = true,
DynamicAndObject = true,
TupleToUnderlyingType = true,
};
public bool Equals(IReadOnlyList<IParameter> x, IReadOnlyList<IParameter> y)
{

76
ICSharpCode.Decompiler/TypeSystem/TupleType.cs

@ -23,9 +23,11 @@ using System.Diagnostics; @@ -23,9 +23,11 @@ using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.TypeSystem
{
[DebuggerDisplay("TupleType({ElementTypes}, {ElementNames})")]
public sealed class TupleType : AbstractType, ICompilationProvider
{
public const int RestPosition = 8;
@ -49,10 +51,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -49,10 +51,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
public ImmutableArray<string> ElementNames { get; }
public TupleType(ICompilation compilation, ImmutableArray<IType> elementTypes,
ImmutableArray<string> elementNames = default(ImmutableArray<string>))
ImmutableArray<string> elementNames = default(ImmutableArray<string>),
IAssembly valueTupleAssembly = null)
{
this.Compilation = compilation;
this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes);
this.UnderlyingType = CreateUnderlyingType(compilation, elementTypes, valueTupleAssembly);
this.ElementTypes = elementTypes;
if (elementNames.IsDefault) {
this.ElementNames = Enumerable.Repeat<string>(null, elementTypes.Length).ToImmutableArray();
@ -62,37 +65,48 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -62,37 +65,48 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray<IType> elementTypes)
static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray<IType> elementTypes, IAssembly valueTupleAssembly)
{
int remainder = (elementTypes.Length - 1) % (RestPosition - 1) + 1;
Debug.Assert(remainder >= 1 && remainder < RestPosition);
int pos = elementTypes.Length - remainder;
var type = new ParameterizedType(
compilation.FindType(new TopLevelTypeName("System", "ValueTuple", remainder)),
FindValueTupleType(compilation, valueTupleAssembly, remainder),
elementTypes.Slice(pos));
while (pos > 0) {
pos -= (RestPosition - 1);
type = new ParameterizedType(
compilation.FindType(new TopLevelTypeName("System", "ValueTuple", RestPosition)),
FindValueTupleType(compilation, valueTupleAssembly, RestPosition),
elementTypes.Slice(pos, RestPosition - 1).Concat(new[] { type }));
}
Debug.Assert(pos == 0);
return type;
}
private static IType FindValueTupleType(ICompilation compilation, IAssembly valueTupleAssembly, int tpc)
{
FullTypeName typeName = new TopLevelTypeName("System", "ValueTuple", tpc);
if (valueTupleAssembly != null) {
var typeDef = valueTupleAssembly.GetTypeDefinition(typeName);
if (typeDef != null)
return typeDef;
}
return compilation.FindType(typeName);
}
/// <summary>
/// Gets whether the specified type is a valid underlying type for a tuple.
/// Also returns type for tuple types themselves.
/// Also returns true for tuple types themselves.
/// </summary>
public static bool IsTupleCompatible(IType type, out int tupleCardinality)
{
switch (type.Kind) {
case TypeKind.Tuple:
tupleCardinality = ((TupleType)type).ElementNames.Length;
tupleCardinality = ((TupleType)type).ElementTypes.Length;
return true;
case TypeKind.Class:
case TypeKind.Struct:
if (type.Namespace == "System" && type.Name == "ValueType") {
if (type.Namespace == "System" && type.Name == "ValueTuple") {
int tpc = type.TypeParameterCount;
if (tpc > 0 && tpc < RestPosition) {
tupleCardinality = tpc;
@ -110,6 +124,24 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -110,6 +124,24 @@ namespace ICSharpCode.Decompiler.TypeSystem
return false;
}
/// <summary>
/// Construct a tuple type (without element names) from the given underlying type.
/// Returns null if the input is not a valid underlying type.
/// </summary>
public static TupleType FromUnderlyingType(ICompilation compilation, IType type)
{
var elementTypes = new List<IType>();
if (CollectTupleElementTypes(type, elementTypes)) {
return new TupleType(
compilation,
elementTypes.ToImmutableArray(),
valueTupleAssembly: type.GetDefinition()?.ParentAssembly
);
} else {
return null;
}
}
static bool CollectTupleElementTypes(IType type, List<IType> output)
{
switch (type.Kind) {
@ -118,7 +150,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -118,7 +150,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return true;
case TypeKind.Class:
case TypeKind.Struct:
if (type.Namespace == "System" && type.Name == "ValueType") {
if (type.Namespace == "System" && type.Name == "ValueTuple") {
int tpc = type.TypeParameterCount;
if (tpc > 0 && tpc < RestPosition) {
output.AddRange(type.TypeArguments);
@ -186,7 +218,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -186,7 +218,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
if (newElementTypes != null) {
return new TupleType(this.Compilation, newElementTypes.ToImmutableArray(), this.ElementNames);
return new TupleType(this.Compilation, newElementTypes.ToImmutableArray(), this.ElementNames,
this.GetDefinition()?.ParentAssembly);
} else {
return this;
}
@ -219,7 +252,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -219,7 +252,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
foreach (var field in UnderlyingType.GetFields(filter, options)) {
yield return field;
}
for (int i = 0; i < ElementTypes.Length; i++) {
/*for (int i = 0; i < ElementTypes.Length; i++) {
var type = ElementTypes[i];
var name = ElementNames[i];
int pos = i + 1;
@ -228,13 +261,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -228,13 +261,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
yield return MakeField(type, name);
if (pos >= RestPosition)
yield return MakeField(type, itemName);
}
}*/
}
private IField MakeField(IType type, string name)
/*private IField MakeField(IType type, string name)
{
throw new NotImplementedException();
}
var f = new DefaultUnresolvedField();
f.ReturnType = SpecialType.UnknownType;
f.Name = name;
return new TupleElementField(f, Compilation.TypeResolveContext);
}*/
public override IEnumerable<IMethod> GetMethods(Predicate<IUnresolvedMethod> filter = null, GetMemberOptions options = GetMemberOptions.None)
{
@ -274,13 +310,18 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -274,13 +310,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
public ImmutableArray<string> ElementNames { get; }
public IAssemblyReference ValueTupleAssembly { get; }
public TupleTypeReference(ImmutableArray<ITypeReference> elementTypes)
{
this.ElementTypes = elementTypes;
}
public TupleTypeReference(ImmutableArray<ITypeReference> elementTypes, ImmutableArray<string> elementNames)
public TupleTypeReference(ImmutableArray<ITypeReference> elementTypes,
ImmutableArray<string> elementNames = default(ImmutableArray<string>),
IAssemblyReference valueTupleAssembly = null)
{
this.ValueTupleAssembly = valueTupleAssembly;
this.ElementTypes = elementTypes;
this.ElementNames = elementNames;
}
@ -289,7 +330,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -289,7 +330,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
return new TupleType(context.Compilation,
ElementTypes.Select(t => t.Resolve(context)).ToImmutableArray(),
ElementNames
ElementNames,
ValueTupleAssembly?.Resolve(context)
);
}
}

29
ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs

@ -96,8 +96,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -96,8 +96,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
return result;
}
#endregion
#region Equals and GetHashCode implementation
public bool Equals(TypeParameterSubstitution other, TypeVisitor normalization)
{
if (other == null)
return false;
return TypeListEquals(classTypeArguments, other.classTypeArguments, normalization)
&& TypeListEquals(methodTypeArguments, other.methodTypeArguments, normalization);
}
public override bool Equals(object obj)
{
TypeParameterSubstitution other = obj as TypeParameterSubstitution;
@ -128,7 +136,24 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -128,7 +136,24 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return true;
}
static bool TypeListEquals(IReadOnlyList<IType> a, IReadOnlyList<IType> b, TypeVisitor normalization)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
if (a.Count != b.Count)
return false;
for (int i = 0; i < a.Count; i++) {
var an = a[i].AcceptVisitor(normalization);
var bn = b[i].AcceptVisitor(normalization);
if (!an.Equals(bn))
return false;
}
return true;
}
static int TypeListHashCode(IReadOnlyList<IType> obj)
{
if (obj == null)

8
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -63,7 +63,13 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -63,7 +63,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
return baseMethod.GetHashCode();
}
public bool Equals(IMember obj, TypeVisitor typeNormalization)
{
VarArgInstanceMethod other = obj as VarArgInstanceMethod;
return other != null && baseMethod.Equals(other.baseMethod, typeNormalization);
}
public override string ToString()
{
StringBuilder b = new StringBuilder("[");

Loading…
Cancel
Save