diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
index 8ce311797..9311ed1fd 100644
--- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
+++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
@@ -133,7 +133,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
await vswhereToolset.Fetch().ConfigureAwait(false);
await RefAssembliesToolset.Fetch("5.0.0", sourcePath: "ref/net5.0").ConfigureAwait(false);
- await RefAssembliesToolset.Fetch("10.0.0-preview.4.25258.110", sourcePath: "ref/net10.0").ConfigureAwait(false);
+ await RefAssembliesToolset.Fetch("10.0.0-rc.2.25502.107", sourcePath: "ref/net10.0").ConfigureAwait(false);
#if DEBUG
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index de9ba4ba4..faa9ac373 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -96,6 +96,8 @@
+
+
@@ -145,6 +147,8 @@
+
+
diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
index 50fdeb3d9..6ffefade4 100644
--- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
@@ -323,6 +323,22 @@ namespace ICSharpCode.Decompiler.Tests
await Run();
}
+ [Test]
+ public async Task ExtensionEncodingV1()
+ {
+ // uses Microsoft.Net.Compilers.Toolset 5.0.0-2.25380.108
+ // see ExtensionEncodingV1.il for details
+ await Run();
+ }
+
+ [Test]
+ public async Task ExtensionEncodingV2()
+ {
+ // uses Microsoft.Net.Compilers.Toolset 5.0.0-2.25451.107
+ // see ExtensionEncodingV2.il for details
+ await Run();
+ }
+
async Task Run([CallerMemberName] string testName = null, DecompilerSettings settings = null,
AssemblerOptions assemblerOptions = AssemblerOptions.Library)
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.cs
new file mode 100644
index 000000000..dbd903772
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
+{
+ internal static class ExtensionPropertiesV1
+ {
+ extension(ICollection collection) where T : notnull
+ {
+ public bool IsEmpty => collection.Count == 0;
+
+ public int Test {
+ get {
+ return 42;
+ }
+ set {
+ }
+ }
+
+ public void AddIfNotNull(T item)
+ {
+ if (item != null)
+ {
+ collection.Add(item);
+ }
+ }
+
+ public T2 Cast(int index) where T2 : T
+ {
+ return (T2)(object)collection.ElementAt(index);
+ }
+
+ public static void StaticExtension()
+ {
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.il
new file mode 100644
index 000000000..419dd097f
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV1.il
@@ -0,0 +1,253 @@
+// To extend use:
+// see https://lab.razor.fyi/#fVHNbhMxEFYRqrBP0CcYbpsDVlioVDU_UrSNUBCqImVPnHC8k8TgtRd7tiSqcuMh-gbceQBeihdA66bJNlK5WDPzfTPffGP-65TzqXdLL0uhwtndaR20XcJsEwjLHm9nInPGoCLtbBAf0KLX6j-MSVnWJOcGjzhXWi6tC6RVeBoRmStwZKXZBH1M-6Tt96NSvvIoC22XT9VFLsO30OPcyhJDJRXCJJutpK8aIXGFypWVNuhFjoFCfDMZMIipR6INv-VMW0JvpYFAkrQCZWQIMF4T2qCdnXpXoSeNgbNbzhg-AP18mEwOl-nnQ1D7rAM_VugRcrgE68jWxnAW-1lVz41WMHfOwCSMy4o2MGj3iszVlmAwgG6Ptzq0JWgMQBzDlvgQMY9Uewvv017Mt_ENezzm2_aoG6cLGBXFZHHt6Lo2JslBE5adhnPfpBeQNCV4PYBm_QjtMNZadlQUkddpaT_SylPIZKB-ng6TxoK2Ba7390nhEvKD6s5JkqedxM2_oqJOS2tssERLI0ruh0TJR2K7P4z-ZjHef2RyMNcsueVsy7f87NW56Irum1Sk5-8uuuJt9-LjM20-sxd3P__-_rN4-fzLyfrkHw
+// created using https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-tools/NuGet/Microsoft.Net.Compilers.Toolset/overview/5.0.0-2.25380.108
+
+.class private auto ansi abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1
+ extends [System.Runtime]System.Object
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 00 00 00
+ )
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
+ 01 00 00 00
+ )
+ // Nested Types
+ .class nested public auto ansi sealed specialname beforefieldinit '<>E__0`1'
+ extends [System.Runtime]System.Object
+ {
+ // Methods
+ .method private hidebysig specialname static
+ void '$' (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
+ 01 00 00 00
+ )
+ // Method begins at RVA 0x209d
+ // Header size: 1
+ // Code size: 1 (0x1)
+ .maxstack 8
+
+ IL_0000: ret
+ } // end of method '<>E__0`1'::'$'
+
+ .method public hidebysig specialname
+ instance bool get_IsEmpty () cil managed
+ {
+ // Method begins at RVA 0x209f
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '<>E__0`1'::get_IsEmpty
+
+ .method public hidebysig specialname
+ instance int32 get_Test () cil managed
+ {
+ // Method begins at RVA 0x209f
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '<>E__0`1'::get_Test
+
+ .method public hidebysig specialname
+ instance void set_Test (
+ int32 'value'
+ ) cil managed
+ {
+ // Method begins at RVA 0x209f
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '<>E__0`1'::set_Test
+
+ .method public hidebysig
+ instance void AddIfNotNull (
+ !T item
+ ) cil managed
+ {
+ // Method begins at RVA 0x209f
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '<>E__0`1'::AddIfNotNull
+
+ .method public hidebysig
+ instance !!T2 Cast<(!T) T2> (
+ int32 index
+ ) cil managed
+ {
+ .param type T2
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 00 00 00
+ )
+ // Method begins at RVA 0x209f
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '<>E__0`1'::Cast
+
+ .method public hidebysig static
+ void StaticExtension () cil managed
+ {
+ // Method begins at RVA 0x209f
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '<>E__0`1'::StaticExtension
+
+ // Properties
+ .property instance bool IsEmpty()
+ {
+ .get instance bool ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1/'<>E__0`1'::get_IsEmpty()
+ }
+ .property instance int32 Test()
+ {
+ .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1/'<>E__0`1'::get_Test()
+ .set instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1/'<>E__0`1'::set_Test(int32)
+ }
+
+ } // end of class <>E__0`1
+
+
+ // Methods
+ .method public hidebysig static
+ bool get_IsEmpty (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection
+ ) cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Header size: 1
+ // Code size: 10 (0xa)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: callvirt instance int32 class [System.Runtime]System.Collections.Generic.ICollection`1::get_Count()
+ IL_0006: ldc.i4.0
+ IL_0007: ceq
+ IL_0009: ret
+ } // end of method ExtensionProperties::get_IsEmpty
+
+ .method public hidebysig static
+ int32 get_Test (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection
+ ) cil managed
+ {
+ // Method begins at RVA 0x205b
+ // Header size: 1
+ // Code size: 4 (0x4)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ldc.i4.s 42
+ IL_0003: ret
+ } // end of method ExtensionProperties::get_Test
+
+ .method public hidebysig static
+ void set_Test (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection,
+ int32 'value'
+ ) cil managed
+ {
+ // Method begins at RVA 0x2060
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method ExtensionProperties::set_Test
+
+ .method public hidebysig static
+ void AddIfNotNull (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection,
+ !!T item
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
+ 01 00 00 00
+ )
+ // Method begins at RVA 0x2064
+ // Header size: 12
+ // Code size: 25 (0x19)
+ .maxstack 2
+ .locals init (
+ [0] bool
+ )
+
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: box !!T
+ IL_0007: ldnull
+ IL_0008: cgt.un
+ IL_000a: stloc.0
+ IL_000b: ldloc.0
+ IL_000c: brfalse.s IL_0018
+
+ IL_000e: nop
+ IL_000f: ldarg.0
+ IL_0010: ldarg.1
+ IL_0011: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0)
+ IL_0016: nop
+ IL_0017: nop
+
+ IL_0018: ret
+ } // end of method ExtensionProperties::AddIfNotNull
+
+ .method public hidebysig static
+ !!T2 Cast (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection,
+ int32 index
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
+ 01 00 00 00
+ )
+ // Method begins at RVA 0x2089
+ // Header size: 1
+ // Code size: 19 (0x13)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ldarg.0
+ IL_0002: ldarg.1
+ IL_0003: call !!0 [System.Linq]System.Linq.Enumerable::ElementAt(class [System.Runtime]System.Collections.Generic.IEnumerable`1, int32)
+ IL_0008: box !!T
+ IL_000d: unbox.any !!T2
+ IL_0012: ret
+ } // end of method ExtensionProperties::Cast
+
+ .method public hidebysig static
+ void StaticExtension () cil managed
+ {
+ // Method begins at RVA 0x2060
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method ExtensionProperties::StaticExtension
+
+} // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV1
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.cs
new file mode 100644
index 000000000..effd8aa5b
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using System.Linq;
+namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
+{
+ internal static class ExtensionPropertiesV2
+ {
+ extension(ICollection collection) where T : notnull
+ {
+ public bool IsEmpty => collection.Count == 0;
+ public int Test {
+ get {
+ return 42;
+ }
+ set {
+ }
+ }
+ public void AddIfNotNull(T item)
+ {
+ if (item != null)
+ {
+ collection.Add(item);
+ }
+ }
+ public T2 Cast(int index) where T2 : T
+ {
+ return (T2)(object)collection.ElementAt(index);
+ }
+ public static void StaticExtension()
+ {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.il
new file mode 100644
index 000000000..996199657
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/ExtensionEncodingV2.il
@@ -0,0 +1,328 @@
+// To extend use:
+// see https://lab.razor.fyi/#fVHBbtNAEFUQArwn6BcMPdkSrFyrFVKbVIrcgIKqKKKrHkBIbOyps3S9a3bHtFEViQvfwy9w4Qf4D66ckd00dSOVy2pn3nvz5u2y748ZmzpbOFnyzG_9fVR7ZQo4WXjC8oB1K55arTEjZY3nb9CgU9l_GOOyrEnONG5wjpQsjPWkMn8_wlOb49BIvfBqk3aszJeNlpg7lLkyxX19LqQ_3xz0rjakSuSn6LyyppWzD9J7LGd6sQ9CugLptZMlXlh3Hm7zyUik1uGwql6sRIOdmMfb0UfGjCzRVzJDGKcnc-mqJgI_wsyWldLouEBPvj1T6dHz8fHUIdGCXbFAGUJnpAZPklQGmZbew-iS0DQuU2crdKTQnyYsuGJBgDdQXxyG49t374tDyNZVBBdzdAgC9sFYMrXWLGj1QVXPtMpgZq2GsR-VFS1g0NXy1NaGYDCA-IB1FMoQNCGgHRMUeHMLHFLtDOwmB229bE-_xtt62R311aochnk-PptYmtRahwIUYRk1nGuROoOwacHzATTrt9AKCzrLDvO85UUd7zteIoFUeuqL5DBsIiiT4-X6fRLYB3HrukoSiiQK7ewzZhR1vEYaSzQ0pPB6SGt5x2z1i22-k_a-_srwNlyz5JIFS7ZkW8_2eMzjlwlP9nb3dvhO_OrtA6XfB09-__n14-fZ04efevPeZe9br_cP
+// created using https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-tools/NuGet/Microsoft.Net.Compilers.Toolset/overview/5.0.0-2.25451.107
+
+.assembly TestProject
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = {
+ }
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = {
+ int32(8)
+ }
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = {
+ property bool WrapNonExceptionThrows = bool(true)
+ }
+ .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = {
+ int32(263)
+ }
+ .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = {
+ string('.NETCoreApp,Version=10.0')
+ }
+ .permissionset reqmin = {
+ [System.Runtime]System.Security.Permissions.SecurityPermissionAttribute = {
+ property bool SkipVerification = bool(true)
+ }
+ }
+ .hash algorithm 0x00008004 // SHA1
+ .ver 0:0:0:0
+}
+
+.class private auto ansi ''
+{
+} // end of class
+
+.class private auto ansi abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2
+ extends [System.Runtime]System.Object
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = {
+ uint8(1)
+ }
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = {
+ uint8(0)
+ }
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = {
+ }
+ // Nested Types
+ .class nested public auto ansi sealed specialname '$847CB318C385471B1F4E7BD0A197DBCA'<$T0>
+ extends [System.Runtime]System.Object
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = {
+ }
+ // Nested Types
+ .class nested public auto ansi abstract sealed specialname '$6A79ECC07A7731C57E8EB4E3D9C8B38B'
+ extends [System.Runtime]System.Object
+ {
+ // Methods
+ .method public hidebysig specialname static
+ void '$' (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = {
+ }
+ // Method begins at RVA 0x20a0
+ // Header size: 1
+ // Code size: 1 (0x1)
+ .maxstack 8
+
+ IL_0000: ret
+ } // end of method '$6A79ECC07A7731C57E8EB4E3D9C8B38B'::'$'
+
+ } // end of class $6A79ECC07A7731C57E8EB4E3D9C8B38B
+
+
+ // Methods
+ .method public hidebysig specialname
+ instance bool get_IsEmpty () cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ // Method begins at RVA 0x209d
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::get_IsEmpty
+
+ .method public hidebysig specialname
+ instance int32 get_Test () cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ // Method begins at RVA 0x209d
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::get_Test
+
+ .method public hidebysig specialname
+ instance void set_Test (
+ int32 'value'
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ // Method begins at RVA 0x209d
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::set_Test
+
+ .method public hidebysig
+ instance void AddIfNotNull (
+ !$T0 item
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ // Method begins at RVA 0x209d
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::AddIfNotNull
+
+ .method public hidebysig
+ instance !!T2 Cast<(!$T0) T2> (
+ int32 index
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ .param type T2
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = {
+ uint8(0)
+ }
+ // Method begins at RVA 0x209d
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::Cast
+
+ .method public hidebysig static
+ void StaticExtension () cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ // Method begins at RVA 0x209d
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method '$847CB318C385471B1F4E7BD0A197DBCA'::StaticExtension
+
+ // Properties
+ .property instance bool IsEmpty()
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ .get instance bool ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2/'$847CB318C385471B1F4E7BD0A197DBCA'::get_IsEmpty()
+ }
+ .property instance int32 Test()
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = {
+ string('$6A79ECC07A7731C57E8EB4E3D9C8B38B')
+ }
+ .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2/'$847CB318C385471B1F4E7BD0A197DBCA'::get_Test()
+ .set instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2/'$847CB318C385471B1F4E7BD0A197DBCA'::set_Test(int32)
+ }
+
+ } // end of class $847CB318C385471B1F4E7BD0A197DBCA
+
+
+ // Methods
+ .method public hidebysig static
+ bool get_IsEmpty (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection
+ ) cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Header size: 1
+ // Code size: 10 (0xa)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: callvirt instance int32 class [System.Runtime]System.Collections.Generic.ICollection`1::get_Count()
+ IL_0006: ldc.i4.0
+ IL_0007: ceq
+ IL_0009: ret
+ } // end of method ExtensionPropertiesV2::get_IsEmpty
+
+ .method public hidebysig static
+ int32 get_Test (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection
+ ) cil managed
+ {
+ // Method begins at RVA 0x205b
+ // Header size: 1
+ // Code size: 4 (0x4)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ldc.i4.s 42
+ IL_0003: ret
+ } // end of method ExtensionPropertiesV2::get_Test
+
+ .method public hidebysig static
+ void set_Test (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection,
+ int32 'value'
+ ) cil managed
+ {
+ // Method begins at RVA 0x2060
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method ExtensionPropertiesV2::set_Test
+
+ .method public hidebysig static
+ void AddIfNotNull (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection,
+ !!T item
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = {
+ }
+ // Method begins at RVA 0x2064
+ // Header size: 12
+ // Code size: 25 (0x19)
+ .maxstack 2
+ .locals init (
+ [0] bool
+ )
+
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: box !!T
+ IL_0007: ldnull
+ IL_0008: cgt.un
+ IL_000a: stloc.0
+ IL_000b: ldloc.0
+ IL_000c: brfalse.s IL_0018
+
+ IL_000e: nop
+ IL_000f: ldarg.0
+ IL_0010: ldarg.1
+ IL_0011: callvirt instance void class [System.Runtime]System.Collections.Generic.ICollection`1::Add(!0)
+ IL_0016: nop
+ IL_0017: nop
+
+ IL_0018: ret
+ } // end of method ExtensionPropertiesV2::AddIfNotNull
+
+ .method public hidebysig static
+ !!T2 Cast (
+ class [System.Runtime]System.Collections.Generic.ICollection`1 collection,
+ int32 index
+ ) cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = {
+ }
+ // Method begins at RVA 0x2089
+ // Header size: 1
+ // Code size: 19 (0x13)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ldarg.0
+ IL_0002: ldarg.1
+ IL_0003: call !!0 [System.Linq]System.Linq.Enumerable::ElementAt(class [System.Runtime]System.Collections.Generic.IEnumerable`1, int32)
+ IL_0008: box !!T
+ IL_000d: unbox.any !!T2
+ IL_0012: ret
+ } // end of method ExtensionPropertiesV2::Cast
+
+ .method public hidebysig static
+ void StaticExtension () cil managed
+ {
+ // Method begins at RVA 0x2060
+ // Header size: 1
+ // Code size: 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method ExtensionPropertiesV2::StaticExtension
+
+} // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.ExtensionPropertiesV2
+
+// references
+.assembly extern System.Runtime
+{
+ .publickeytoken = (
+ b0 3f 5f 7f 11 d5 0a 3a
+ )
+ .ver 10:0:0:0
+}
+.assembly extern System.Linq
+{
+ .publickeytoken = (
+ b0 3f 5f 7f 11 d5 0a 3a
+ )
+ .ver 10:0:0:0
+}
diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index 2fdba305e..90436d94b 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -1367,13 +1367,16 @@ namespace ICSharpCode.Decompiler.CSharp
foreach (var group in typeDef.ExtensionInfo?.GetGroups() ?? [])
{
var ext = new ExtensionDeclaration();
- ext.TypeParameters.AddRange(group.Key.DeclaringTypeDefinition.TypeParameters.Select(tp => typeSystemAstBuilder.ConvertTypeParameter(tp)));
- ext.ReceiverParameters.Add(typeSystemAstBuilder.ConvertParameter(group.Key.Parameters.Single()));
- ext.Constraints.AddRange(group.Key.DeclaringTypeDefinition.TypeParameters.Select(c => typeSystemAstBuilder.ConvertTypeParameterConstraint(c)));
+ ITypeParameter[] typeParameters = group.Key.TypeParameters;
+ var subst = new TypeParameterSubstitution(typeParameters, null);
+ ext.TypeParameters.AddRange(typeParameters.Select(tp => typeSystemAstBuilder.ConvertTypeParameter(tp)));
+ var marker = group.Key.Marker.Specialize(subst);
+ ext.ReceiverParameters.Add(typeSystemAstBuilder.ConvertParameter(marker.Parameters.Single()));
+ ext.Constraints.AddRange(typeParameters.Select(c => typeSystemAstBuilder.ConvertTypeParameterConstraint(c)));
foreach (var member in group)
{
- IMember extMember = member.ExtensionMember;
+ IMember extMember = member.ExtensionMember.Specialize(subst);
if (member.ExtensionMember.IsAccessor)
{
extMember = member.ExtensionMember.AccessorOwner;
@@ -1508,9 +1511,15 @@ namespace ICSharpCode.Decompiler.CSharp
return;
}
- if (settings.ExtensionMembers && extensionInfo != null && entity is IMethod m && extensionInfo.InfoOfImplementationMember(m).HasValue)
+ if (settings.ExtensionMembers && extensionInfo != null)
{
- return;
+ switch (entity)
+ {
+ case ITypeDefinition td when extensionInfo.IsExtensionGroupingType(td):
+ return;
+ case IMethod m when extensionInfo.InfoOfImplementationMember(m).HasValue:
+ return;
+ }
}
EntityDeclaration entityDecl;
@@ -1584,9 +1593,21 @@ namespace ICSharpCode.Decompiler.CSharp
switch (extMember)
{
case IProperty p:
- return DoDecompile(p, decompileRun, decompilationContext.WithCurrentMember(p), info);
+ var prop = DoDecompile(p, decompileRun, decompilationContext.WithCurrentMember(p), info);
+ RemoveAttribute(prop, KnownAttribute.ExtensionMarker);
+ if (p.Getter != null)
+ {
+ RemoveAttribute(prop.GetChildByRole(PropertyDeclaration.GetterRole), KnownAttribute.ExtensionMarker);
+ }
+ if (p.Setter != null)
+ {
+ RemoveAttribute(prop.GetChildByRole(PropertyDeclaration.SetterRole), KnownAttribute.ExtensionMarker);
+ }
+ return prop;
case IMethod m:
- return DoDecompile(m, decompileRun, decompilationContext.WithCurrentMember(m), info);
+ var meth = DoDecompile(m, decompileRun, decompilationContext.WithCurrentMember(m), info);
+ RemoveAttribute(meth, KnownAttribute.ExtensionMarker);
+ return meth;
}
throw new NotSupportedException($"Extension member {extMember} is not supported for decompilation.");
@@ -1762,7 +1783,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
if (!method.IsStatic)
parameterOffset = 1; // implementation method has an additional receiver parameter
- method = extensionInfo.InfoOfExtensionMember(method).Value.ImplementationMethod;
+ method = extensionInfo.InfoOfExtensionMember((IMethod)method.MemberDefinition).Value.ImplementationMethod;
}
var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
diff --git a/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs b/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs
index dfe82d774..c2e28cac6 100644
--- a/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs
@@ -20,9 +20,12 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
+using ICSharpCode.Decompiler.TypeSystem.Implementation;
+
namespace ICSharpCode.Decompiler.TypeSystem
{
public class ExtensionInfo
@@ -59,6 +62,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IMethod? marker = null;
bool hasMultipleMarkers = false;
List extensionMethods = [];
+ ITypeParameter[] extensionGroupTypeParameters = extGroup.TypeParameters.ToArray();
// For easier access to accessors we use SRM
foreach (var h in td.GetMethods())
@@ -82,7 +86,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
if (marker == null || hasMultipleMarkers)
return false;
- CollectImplementationMethods(extGroup, marker, extensionMethods);
+ CollectImplementationMethods(extGroup, marker, extensionMethods, extensionGroupTypeParameters);
return true;
}
@@ -102,6 +106,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
TypeDefinition td = metadata.GetTypeDefinition((TypeDefinitionHandle)extGroup.MetadataToken);
List extensionMethods = [];
+ ITypeParameter[] extensionGroupTypeParameters = new ITypeParameter[extGroup.TypeParameterCount];
// For easier access to accessors we use SRM
foreach (var h in td.GetMethods())
@@ -121,12 +126,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
extensionMethods.Add(method);
}
- CollectImplementationMethods(extGroup, marker, extensionMethods);
+ CollectImplementationMethods(extGroup, marker, extensionMethods, extensionGroupTypeParameters);
return true;
}
- void CollectImplementationMethods(ITypeDefinition extGroup, IMethod marker, List extensionMethods)
+ void CollectImplementationMethods(ITypeDefinition extGroup, IMethod marker, List extensionMethods, ITypeParameter[] extensionGroupTypeParameters)
{
+ List<(IMethod extension, IMethod implementation)> implementations = [];
+
+ string[] typeParameterNames = new string[extGroup.TypeParameterCount];
+
foreach (var extension in extensionMethods)
{
int expectedTypeParameterCount = extension.TypeParameters.Count + extGroup.TypeParameterCount;
@@ -165,15 +174,62 @@ namespace ICSharpCode.Decompiler.TypeSystem
);
}
+ IMethod? foundImpl = null;
+
foreach (var impl in extensionContainer.Methods)
{
if (!IsMatchingImplementation(impl))
continue;
- var emi = new ExtensionMemberInfo(marker, extension, impl);
- extensionMemberMap[extension] = emi;
- implementationMemberMap[impl] = emi;
+ Debug.Assert(foundImpl == null, "Multiple matching implementations found");
+ foundImpl = impl;
+ }
+
+ Debug.Assert(foundImpl != null, "No matching implementation found");
+
+ implementations.Add((extension, foundImpl));
+ }
+
+ foreach (var (extension, implementation) in implementations)
+ {
+ for (int i = 0; i < extensionGroupTypeParameters.Length; i++)
+ {
+ if (typeParameterNames[i] == null)
+ {
+ typeParameterNames[i] = implementation.TypeParameters[i].Name;
+ }
+ else if (typeParameterNames[i] != implementation.TypeParameters[i].Name)
+ {
+ // TODO: Handle name conflicts properly
+ typeParameterNames[i] = $"T{i + 1}";
+ }
}
}
+
+ for (int i = 0; i < extensionGroupTypeParameters.Length; i++)
+ {
+ var originalTypeParameter = extGroup.TypeParameters[i];
+ if (extensionGroupTypeParameters[i] == null)
+ {
+ extensionGroupTypeParameters[i] = new DefaultTypeParameter(
+ extGroup, i, typeParameterNames[i],
+ VarianceModifier.Invariant,
+ attributes: originalTypeParameter.GetAttributes().ToArray(),
+ originalTypeParameter.HasValueTypeConstraint,
+ originalTypeParameter.HasReferenceTypeConstraint,
+ originalTypeParameter.HasDefaultConstructorConstraint,
+ originalTypeParameter.TypeConstraints.Select(c => c.Type).ToArray(),
+ originalTypeParameter.NullabilityConstraint
+ );
+ }
+ }
+
+ foreach (var (extension, implementation) in implementations)
+ {
+ var info = new ExtensionMemberInfo(marker, extension, implementation, extensionGroupTypeParameters);
+ this.extensionMemberMap[extension] = info;
+ this.implementationMemberMap[implementation] = info;
+ }
+
}
}
@@ -187,13 +243,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
return this.implementationMemberMap.TryGetValue(method, out var value) ? value : null;
}
- public IEnumerable> GetGroups()
+ public IEnumerable> GetGroups()
+ {
+ return this.extensionMemberMap.Values.GroupBy(x => (x.ExtensionMarkerMethod, x.ExtensionGroupingTypeParameters));
+ }
+
+ public bool IsExtensionGroupingType(ITypeDefinition type)
{
- return this.extensionMemberMap.Values.GroupBy(x => x.ExtensionMarkerMethod);
+ return this.extensionMemberMap.Values.Any(x => x.ExtensionGroupingType.Equals(type));
}
}
- public readonly struct ExtensionMemberInfo(IMethod marker, IMethod extension, IMethod implementation)
+ public readonly struct ExtensionMemberInfo(IMethod marker, IMethod extension, IMethod implementation, ITypeParameter[] extensionGroupingTypeParameters)
{
///
/// Metadata-only method called '<Extension>$'. Has the C# signature for the extension declaration.
@@ -223,6 +284,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
///
public ITypeDefinition ExtensionGroupingType => ExtensionMember.DeclaringTypeDefinition!;
+ ///
+ /// This is the array of type parameters for the extension declaration.
+ ///
+ public ITypeParameter[] ExtensionGroupingTypeParameters => extensionGroupingTypeParameters;
+
///
/// This class holds the type parameters for the extension declaration with full fidelity of C# constraints.
///