diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 8083f86e2..089c486af 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -70,7 +70,7 @@ - + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 80c06925b..f80cc3498 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -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); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs similarity index 67% rename from ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs rename to ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs index c0e6b6812..4a5d79a1e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs @@ -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 VT1; @@ -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 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 NewTuple1 => new ValueTuple(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); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.opt.roslyn.il new file mode 100644 index 000000000..6267c588f --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.opt.roslyn.il @@ -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 VT1 + .field public valuetype [mscorlib]System.ValueTuple`8 VT7EmptyRest + .field public valuetype [mscorlib]System.ValueTuple`2 Unnamed2 + .field public valuetype [mscorlib]System.ValueTuple`3 Unnamed3 + .field public valuetype [mscorlib]System.ValueTuple`4 Unnamed4 + .field public valuetype [mscorlib]System.ValueTuple`5 Unnamed5 + .field public valuetype [mscorlib]System.ValueTuple`6 Unnamed6 + .field public valuetype [mscorlib]System.ValueTuple`7 Unnamed7 + .field public valuetype [mscorlib]System.ValueTuple`8> Unnamed8 + .field public valuetype [mscorlib]System.ValueTuple`2 Named2 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`2[] Named2Array + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`8> Named8 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF 00 00 ) // .f.g.h... + .field public valuetype [mscorlib]System.ValueTuple`5 PartiallyNamed + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 05 00 00 00 FF 01 61 FF 01 62 FF 00 00 ) // ........a..b... + .field public valuetype [mscorlib]System.ValueTuple`3,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested1 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 78 01 79 01 7A 01 61 01 62 // .......x.y.z.a.b + FF FF 01 63 01 64 00 00 ) // ...c.d.. + .field public valuetype [mscorlib]System.ValueTuple`3,object,valuetype [mscorlib]System.ValueTuple`2> Nested2 + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor(bool[]) = ( 01 00 08 00 00 00 00 00 00 01 01 00 01 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 07 00 00 00 FF FF FF 01 61 01 62 01 63 01 // ..........a.b.c. + 64 00 00 ) // d.. + .field public valuetype [mscorlib]System.ValueTuple`5,valuetype [mscorlib]System.ValueTuple`1,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested3 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0C 00 00 00 01 61 FF 01 62 FF 01 63 02 78 // .......a..b..c.x + 31 02 78 32 FF 02 79 31 02 79 32 FF FF 00 00 ) // 1.x2..y1.y2.... + .field public valuetype [mscorlib]System.ValueTuple`8>> Nested4 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0D 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF FF FF 01 69 01 6A 00 00 ) // .f.g.h....i.j.. + .field public class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2> 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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::VT1 + IL_0006: ldfld !0 valuetype [mscorlib]System.ValueTuple`1::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed8 + IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8>::Rest + IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named8 + IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8>::Rest + IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1::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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed + IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`5::Item2 + IL_000b: ldarg.0 + IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`5 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed + IL_0011: ldfld !2 valuetype [mscorlib]System.ValueTuple`5::Item3 + IL_0016: add + IL_0017: ret + } // end of method TupleTests::get_AccessPartiallyNamed + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`1 + 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::.ctor(!0) + IL_0006: ret + } // end of method TupleTests::get_NewTuple1 + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2 + 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::.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::.ctor(!0, + !1, + !2) + IL_0011: newobj instance void valuetype [mscorlib]System.ValueTuple`8>::.ctor(!0, + !1, + !2, + !3, + !4, + !5, + !6, + !7) + IL_0016: box valuetype [mscorlib]System.ValueTuple`8> + IL_001b: ret + } // end of method TupleTests::get_BoxedTuple10 + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2 + get_SwapUnnamed() cil managed + { + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2 + IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2::Item2 + IL_000b: ldarg.0 + IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2 + IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2::Item1 + IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2::.ctor(!0, + !1) + IL_001b: ret + } // end of method TupleTests::get_SwapUnnamed + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2 + get_SwapNamed2() cil managed + { + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2 + IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2::Item2 + IL_000b: ldarg.0 + IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2 + IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2::Item1 + IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2::.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 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::.ctor(!0, + !1, + !2) + IL_0008: stloc.0 + IL_0009: ldloca.s V_0 + IL_000b: constrained. valuetype [mscorlib]System.ValueTuple`3 + 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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2 + IL_0006: constrained. valuetype [mscorlib]System.ValueTuple`2 + 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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict + IL_0006: callvirt instance int32 class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict + IL_0015: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::Clear() + IL_001a: ldarg.0 + IL_001b: ldfld class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2> 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::.ctor(!0, + !1) + IL_002b: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::get_Item(!0) + IL_0030: ldfld !0 valuetype [mscorlib]System.ValueTuple`2::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict + IL_0046: callvirt instance class [mscorlib]System.Collections.Generic.Dictionary`2/ValueCollection class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::get_Values() + IL_004b: call class [mscorlib]System.Collections.Generic.List`1 [System.Core]System.Linq.Enumerable::ToList>(class [mscorlib]System.Collections.Generic.IEnumerable`1) + IL_0050: call !!0 [System.Core]System.Linq.Enumerable::First>(class [mscorlib]System.Collections.Generic.IEnumerable`1) + IL_0055: ldfld !1 valuetype [mscorlib]System.ValueTuple`2::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 + NewTuple1() + { + .get instance valuetype [mscorlib]System.ValueTuple`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_NewTuple1() + } // end of property TupleTests::NewTuple1 + .property instance valuetype [mscorlib]System.ValueTuple`2 + 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 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 + SwapUnnamed() + { + .get instance valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_SwapUnnamed() + } // end of property TupleTests::SwapUnnamed + .property instance valuetype [mscorlib]System.ValueTuple`2 + SwapNamed2() + { + .get instance valuetype [mscorlib]System.ValueTuple`2 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 *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il new file mode 100644 index 000000000..d48e3b6c9 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il @@ -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 VT1 + .field public valuetype [mscorlib]System.ValueTuple`8 VT7EmptyRest + .field public valuetype [mscorlib]System.ValueTuple`2 Unnamed2 + .field public valuetype [mscorlib]System.ValueTuple`3 Unnamed3 + .field public valuetype [mscorlib]System.ValueTuple`4 Unnamed4 + .field public valuetype [mscorlib]System.ValueTuple`5 Unnamed5 + .field public valuetype [mscorlib]System.ValueTuple`6 Unnamed6 + .field public valuetype [mscorlib]System.ValueTuple`7 Unnamed7 + .field public valuetype [mscorlib]System.ValueTuple`8> Unnamed8 + .field public valuetype [mscorlib]System.ValueTuple`2 Named2 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`2[] Named2Array + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. + .field public valuetype [mscorlib]System.ValueTuple`8> Named8 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF 00 00 ) // .f.g.h... + .field public valuetype [mscorlib]System.ValueTuple`5 PartiallyNamed + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 05 00 00 00 FF 01 61 FF 01 62 FF 00 00 ) // ........a..b... + .field public valuetype [mscorlib]System.ValueTuple`3,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested1 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 78 01 79 01 7A 01 61 01 62 // .......x.y.z.a.b + FF FF 01 63 01 64 00 00 ) // ...c.d.. + .field public valuetype [mscorlib]System.ValueTuple`3,object,valuetype [mscorlib]System.ValueTuple`2> Nested2 + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor(bool[]) = ( 01 00 08 00 00 00 00 00 00 01 01 00 01 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 07 00 00 00 FF FF FF 01 61 01 62 01 63 01 // ..........a.b.c. + 64 00 00 ) // d.. + .field public valuetype [mscorlib]System.ValueTuple`5,valuetype [mscorlib]System.ValueTuple`1,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested3 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0C 00 00 00 01 61 FF 01 62 FF 01 63 02 78 // .......a..b..c.x + 31 02 78 32 FF 02 79 31 02 79 32 FF FF 00 00 ) // 1.x2..y1.y2.... + .field public valuetype [mscorlib]System.ValueTuple`8>> Nested4 + .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0D 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e + 01 66 01 67 01 68 FF FF FF 01 69 01 6A 00 00 ) // .f.g.h....i.j.. + .field public class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2> 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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::VT1 + IL_0006: ldfld !0 valuetype [mscorlib]System.ValueTuple`1::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed8 + IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8>::Rest + IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named8 + IL_0006: ldflda !7 valuetype [mscorlib]System.ValueTuple`8>::Rest + IL_000b: ldfld !0 valuetype [mscorlib]System.ValueTuple`1::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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed + IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`5::Item2 + IL_000b: ldarg.0 + IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`5 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::PartiallyNamed + IL_0011: ldfld !2 valuetype [mscorlib]System.ValueTuple`5::Item3 + IL_0016: add + IL_0017: ret + } // end of method TupleTests::get_AccessPartiallyNamed + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`1 + 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::.ctor(!0) + IL_0006: ret + } // end of method TupleTests::get_NewTuple1 + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2 + 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::.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::.ctor(!0, + !1, + !2) + IL_0011: newobj instance void valuetype [mscorlib]System.ValueTuple`8>::.ctor(!0, + !1, + !2, + !3, + !4, + !5, + !6, + !7) + IL_0016: box valuetype [mscorlib]System.ValueTuple`8> + IL_001b: ret + } // end of method TupleTests::get_BoxedTuple10 + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2 + get_SwapUnnamed() cil managed + { + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2 + IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2::Item2 + IL_000b: ldarg.0 + IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Unnamed2 + IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2::Item1 + IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2::.ctor(!0, + !1) + IL_001b: ret + } // end of method TupleTests::get_SwapUnnamed + + .method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2 + get_SwapNamed2() cil managed + { + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2 + IL_0006: ldfld !1 valuetype [mscorlib]System.ValueTuple`2::Item2 + IL_000b: ldarg.0 + IL_000c: ldflda valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2 + IL_0011: ldfld !0 valuetype [mscorlib]System.ValueTuple`2::Item1 + IL_0016: newobj instance void valuetype [mscorlib]System.ValueTuple`2::.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 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::.ctor(!0, + !1, + !2) + IL_0008: stloc.0 + IL_0009: ldloca.s V_0 + IL_000b: constrained. valuetype [mscorlib]System.ValueTuple`3 + 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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::Named2 + IL_0006: constrained. valuetype [mscorlib]System.ValueTuple`2 + 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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict + IL_0007: callvirt instance int32 class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict + IL_001b: callvirt instance void class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::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> 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::.ctor(!0, + !1) + IL_0033: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::get_Item(!0) + IL_0038: ldfld !0 valuetype [mscorlib]System.ValueTuple`2::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> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::TupleDict + IL_0052: callvirt instance class [mscorlib]System.Collections.Generic.Dictionary`2/ValueCollection class [mscorlib]System.Collections.Generic.Dictionary`2,valuetype [mscorlib]System.ValueTuple`2>::get_Values() + IL_0057: call class [mscorlib]System.Collections.Generic.List`1 [System.Core]System.Linq.Enumerable::ToList>(class [mscorlib]System.Collections.Generic.IEnumerable`1) + IL_005c: call !!0 [System.Core]System.Linq.Enumerable::First>(class [mscorlib]System.Collections.Generic.IEnumerable`1) + IL_0061: ldfld !1 valuetype [mscorlib]System.ValueTuple`2::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 + NewTuple1() + { + .get instance valuetype [mscorlib]System.ValueTuple`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_NewTuple1() + } // end of property TupleTests::NewTuple1 + .property instance valuetype [mscorlib]System.ValueTuple`2 + 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 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 + SwapUnnamed() + { + .get instance valuetype [mscorlib]System.ValueTuple`2 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_SwapUnnamed() + } // end of property TupleTests::SwapUnnamed + .property instance valuetype [mscorlib]System.ValueTuple`2 + SwapNamed2() + { + .get instance valuetype [mscorlib]System.ValueTuple`2 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 *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il deleted file mode 100644 index 4fee98e01..000000000 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.opt.roslyn.il +++ /dev/null @@ -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 VT1 - .field public valuetype [mscorlib]System.ValueTuple`8 VT7EmptyRest - .field public valuetype [mscorlib]System.ValueTuple`2 Unnamed2 - .field public valuetype [mscorlib]System.ValueTuple`3 Unnamed3 - .field public valuetype [mscorlib]System.ValueTuple`4 Unnamed4 - .field public valuetype [mscorlib]System.ValueTuple`5 Unnamed5 - .field public valuetype [mscorlib]System.ValueTuple`6 Unnamed6 - .field public valuetype [mscorlib]System.ValueTuple`7 Unnamed7 - .field public valuetype [mscorlib]System.ValueTuple`8> Unnamed8 - .field public valuetype [mscorlib]System.ValueTuple`2 Named2 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. - .field public valuetype [mscorlib]System.ValueTuple`2[] Named2Array - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. - .field public valuetype [mscorlib]System.ValueTuple`8> Named8 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e - 01 66 01 67 01 68 FF 00 00 ) // .f.g.h... - .field public valuetype [mscorlib]System.ValueTuple`5 PartiallyNamed - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 05 00 00 00 FF 01 61 FF 01 62 FF 00 00 ) // ........a..b... - .field public valuetype [mscorlib]System.ValueTuple`3,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested1 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 78 01 79 01 7A 01 61 01 62 // .......x.y.z.a.b - FF FF 01 63 01 64 00 00 ) // ...c.d.. - .field public valuetype [mscorlib]System.ValueTuple`3,object,valuetype [mscorlib]System.ValueTuple`2> Nested2 - .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor(bool[]) = ( 01 00 08 00 00 00 00 00 00 01 01 00 01 00 00 00 ) - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 07 00 00 00 FF FF FF 01 61 01 62 01 63 01 // ..........a.b.c. - 64 00 00 ) // d.. - .field public valuetype [mscorlib]System.ValueTuple`5,valuetype [mscorlib]System.ValueTuple`1,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested3 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0C 00 00 00 01 61 FF 01 62 FF 01 63 02 78 // .......a..b..c.x - 31 02 78 32 FF 02 79 31 02 79 32 FF FF 00 00 ) // 1.x2..y1.y2.... - .field public valuetype [mscorlib]System.ValueTuple`8>> Nested4 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0D 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e - 01 66 01 67 01 68 FF FF FF 01 69 01 6A 00 00 ) // .f.g.h....i.j.. - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 7 (0x7) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } // end of method TupleTypes::.ctor - -} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTypes - - -// ============================================================= - -// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il deleted file mode 100644 index 08640c71e..000000000 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTypes.roslyn.il +++ /dev/null @@ -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 VT1 - .field public valuetype [mscorlib]System.ValueTuple`8 VT7EmptyRest - .field public valuetype [mscorlib]System.ValueTuple`2 Unnamed2 - .field public valuetype [mscorlib]System.ValueTuple`3 Unnamed3 - .field public valuetype [mscorlib]System.ValueTuple`4 Unnamed4 - .field public valuetype [mscorlib]System.ValueTuple`5 Unnamed5 - .field public valuetype [mscorlib]System.ValueTuple`6 Unnamed6 - .field public valuetype [mscorlib]System.ValueTuple`7 Unnamed7 - .field public valuetype [mscorlib]System.ValueTuple`8> Unnamed8 - .field public valuetype [mscorlib]System.ValueTuple`2 Named2 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. - .field public valuetype [mscorlib]System.ValueTuple`2[] Named2Array - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 02 00 00 00 01 61 01 62 00 00 ) // .......a.b.. - .field public valuetype [mscorlib]System.ValueTuple`8> Named8 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e - 01 66 01 67 01 68 FF 00 00 ) // .f.g.h... - .field public valuetype [mscorlib]System.ValueTuple`5 PartiallyNamed - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 05 00 00 00 FF 01 61 FF 01 62 FF 00 00 ) // ........a..b... - .field public valuetype [mscorlib]System.ValueTuple`3,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested1 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 09 00 00 00 01 78 01 79 01 7A 01 61 01 62 // .......x.y.z.a.b - FF FF 01 63 01 64 00 00 ) // ...c.d.. - .field public valuetype [mscorlib]System.ValueTuple`3,object,valuetype [mscorlib]System.ValueTuple`2> Nested2 - .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor(bool[]) = ( 01 00 08 00 00 00 00 00 00 01 01 00 01 00 00 00 ) - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 07 00 00 00 FF FF FF 01 61 01 62 01 63 01 // ..........a.b.c. - 64 00 00 ) // d.. - .field public valuetype [mscorlib]System.ValueTuple`5,valuetype [mscorlib]System.ValueTuple`1,valuetype [mscorlib]System.ValueTuple`2,valuetype [mscorlib]System.ValueTuple`2> Nested3 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0C 00 00 00 01 61 FF 01 62 FF 01 63 02 78 // .......a..b..c.x - 31 02 78 32 FF 02 79 31 02 79 32 FF FF 00 00 ) // 1.x2..y1.y2.... - .field public valuetype [mscorlib]System.ValueTuple`8>> Nested4 - .custom instance void [mscorlib]System.Runtime.CompilerServices.TupleElementNamesAttribute::.ctor(string[]) = ( 01 00 0D 00 00 00 01 61 01 62 01 63 01 64 01 65 // .......a.b.c.d.e - 01 66 01 67 01 68 FF FF FF 01 69 01 6A 00 00 ) // .f.g.h....i.j.. - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method TupleTypes::.ctor - -} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTypes - - -// ============================================================= - -// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 923be81c0..3667696c2 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -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 typeArguments = new List(); foreach (var typeArgument in gType.GenericArguments) { diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 7963c1e7f..ea5067e39 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -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 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 expandedArguments.Add(expressionBuilder.GetDefaultValueExpression(elementType).WithoutILInstruction()); } } - if (IsUnambiguousCall(expectedTargetDetails, method, target.ResolveResult, Empty.Array, expandedArguments) == OverloadResolutionErrors.None) { + if (IsUnambiguousCall(expectedTargetDetails, method, target.ResolveResult, Empty.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 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 return atce .WithRR(rr); } else { - if (IsUnambiguousCall(expectedTargetDetails, method, null, Empty.Array, arguments) != OverloadResolutionErrors.None) { + if (IsUnambiguousCall(expectedTargetDetails, method, null, Empty.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 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 bool argumentsCasted = false; IType[] typeArguments = Empty.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 } 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 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 } OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, - ResolveResult target, IType[] typeArguments, IList arguments) + ResolveResult target, IType[] typeArguments, IList 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 } 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 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.Instance, isInvocationTarget: false) as MemberResolveResult; - return !(result == null || result.IsError || !IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, result.Member)); + result = resolver.ResolveSimpleName(method.AccessorOwner.Name, EmptyList.Instance, isInvocationTarget: false) as MemberResolveResult; } else { var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly); - var result = lookup.Lookup(target, method.AccessorOwner.Name, EmptyList.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.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 arguments) @@ -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 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 } } + 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 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)); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 2d6d2c096..123fecff9 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -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 } } - 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 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 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 } 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)); } } diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs index a21abb861..aab64de06 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs @@ -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 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 NonLiftedParameters => nonLiftedOperator.Parameters; diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs b/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs index 2c4dc4241..cfcdb9517 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs @@ -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; diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 6e16fc3ae..5dc5169b1 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -305,6 +305,7 @@ + @@ -325,6 +326,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 2c2983685..05a81e4d3 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -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 parameters, IList 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; diff --git a/ICSharpCode.Decompiler/IL/Transforms/TupleTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/TupleTransform.cs new file mode 100644 index 000000000..6e61499f1 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/TupleTransform.cs @@ -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 + { + /// + /// Matches an 'ldflda' instruction accessing a tuple element. + /// + /// E.g. matches: + /// ldflda Item1(ldflda Rest(target)) + /// + 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; + } + + /// + /// Matches 'newobj TupleType(...)'. + /// Takes care of flattening long tuples. + /// + 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; + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs index 0d3629220..cfd360073 100644 --- a/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.Decompiler/TypeSystem/CecilLoader.cs @@ -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 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 } } - 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)) { diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index f0b11b469..74ab04225 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -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); } diff --git a/ICSharpCode.Decompiler/TypeSystem/IMember.cs b/ICSharpCode.Decompiler/TypeSystem/IMember.cs index ad1e46e51..cd8376c30 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IMember.cs @@ -172,5 +172,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// If this member is already specialized, the new substitution is composed with the existing substition. /// IMember Specialize(TypeParameterSubstitution substitution); + + /// + /// Gets whether the members are considered equal when applying the specified type normalization. + /// + bool Equals(IMember obj, TypeVisitor typeNormalization); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedMember.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedMember.cs index d930a6ec9..3a7eba2c6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedMember.cs @@ -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) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultMemberReference.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultMemberReference.cs index 1da5bd448..46210b199 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultMemberReference.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultMemberReference.cs @@ -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 } 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; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs index 306485ec2..cb0f2bd71 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs @@ -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(); - - /// - /// Replaces all occurrences of method type parameters in the given type - /// by normalized type parameters. This allows comparing parameter types from different - /// generic methods. - /// - public static IType NormalizeMethodTypeParameters(IType type) - { - return type.AcceptVisitor(normalizeMethodTypeParameters); - } - - /// - /// Replaces all occurrences of class type parameters in the given type - /// by normalized type parameters. This allows comparing parameter types from different - /// generic methods. - /// - public static IType NormalizeClassTypeParameters(IType type) - { - return type.AcceptVisitor(normalizeClassTypeParameters); - } - - /// - /// 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. - /// - public static IType NormalizeAllTypeParameters(IType type) - { - return type.AcceptVisitor(normalizeClassTypeParameters).AcceptVisitor(normalizeMethodTypeParameters); - } readonly SymbolKind ownerType; readonly int index; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs index 523446cc0..369c0c119 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMember.cs @@ -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; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs index fda5f6983..b5e23c2bc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs @@ -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; diff --git a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs new file mode 100644 index 000000000..4075e316d --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs @@ -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); + } + } + } +} diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs index ef7947113..2c50bef7b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs @@ -34,32 +34,13 @@ namespace ICSharpCode.Decompiler.TypeSystem public sealed class ParameterListComparer : IEqualityComparer> { 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 x, IReadOnlyList y) { diff --git a/ICSharpCode.Decompiler/TypeSystem/TupleType.cs b/ICSharpCode.Decompiler/TypeSystem/TupleType.cs index 9051958a5..4948bab5d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TupleType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TupleType.cs @@ -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 public ImmutableArray ElementNames { get; } public TupleType(ICompilation compilation, ImmutableArray elementTypes, - ImmutableArray elementNames = default(ImmutableArray)) + ImmutableArray elementNames = default(ImmutableArray), + 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(null, elementTypes.Length).ToImmutableArray(); @@ -62,37 +65,48 @@ namespace ICSharpCode.Decompiler.TypeSystem } } - static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray elementTypes) + static ParameterizedType CreateUnderlyingType(ICompilation compilation, ImmutableArray 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); + } + /// /// 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. /// 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 return false; } + /// + /// Construct a tuple type (without element names) from the given underlying type. + /// Returns null if the input is not a valid underlying type. + /// + public static TupleType FromUnderlyingType(ICompilation compilation, IType type) + { + var elementTypes = new List(); + if (CollectTupleElementTypes(type, elementTypes)) { + return new TupleType( + compilation, + elementTypes.ToImmutableArray(), + valueTupleAssembly: type.GetDefinition()?.ParentAssembly + ); + } else { + return null; + } + } + static bool CollectTupleElementTypes(IType type, List output) { switch (type.Kind) { @@ -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 } } 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 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 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 GetMethods(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) { @@ -274,13 +310,18 @@ namespace ICSharpCode.Decompiler.TypeSystem /// public ImmutableArray ElementNames { get; } + public IAssemblyReference ValueTupleAssembly { get; } + public TupleTypeReference(ImmutableArray elementTypes) { this.ElementTypes = elementTypes; } - public TupleTypeReference(ImmutableArray elementTypes, ImmutableArray elementNames) + public TupleTypeReference(ImmutableArray elementTypes, + ImmutableArray elementNames = default(ImmutableArray), + IAssemblyReference valueTupleAssembly = null) { + this.ValueTupleAssembly = valueTupleAssembly; this.ElementTypes = elementTypes; this.ElementNames = elementNames; } @@ -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) ); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs b/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs index a4235b448..8fc37bdd2 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeParameterSubstitution.cs @@ -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 } return true; } - + + static bool TypeListEquals(IReadOnlyList a, IReadOnlyList 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 obj) { if (obj == null) diff --git a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs index 8761bec35..99e0c06cd 100644 --- a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs @@ -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("[");