diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
index bc87db9fd..bfeaf5e85 100644
--- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
@@ -96,7 +96,13 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
- public void Patterns([ValueSource("defaultOptions")] CompilerOptions options)
+ public void Using([ValueSource("defaultOptions")] CompilerOptions options)
+ {
+ RunCS(options: options);
+ }
+
+ [Test]
+ public void Loops([ValueSource("defaultOptions")] CompilerOptions options)
{
RunCS(options: options);
}
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index a76a5ac6a..40842b724 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -57,12 +57,14 @@
+
-
+
+
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index 0c73158cc..ef5480f17 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -131,6 +131,12 @@ namespace ICSharpCode.Decompiler.Tests
}
}
+ [Test]
+ public void Generics([ValueSource("defaultOptions")] CompilerOptions cscOptions)
+ {
+ Run(cscOptions: cscOptions);
+ }
+
[Test]
public void Loops([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ControlFlow.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ControlFlow.cs
index 2a484bf43..6ea9abd7b 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ControlFlow.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ControlFlow.cs
@@ -41,7 +41,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
BreakUnlessContinue(true);
BreakUnlessContinue(false);
TestConditionals();
- ThisIsNotAUsingBlock();
return 0;
}
@@ -150,20 +149,5 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
byte answer = (byte)(value == 128 ? 255 : 0);
return answer;
}
-
- static void ThisIsNotAUsingBlock()
- {
- object obj = new System.IO.StringWriter();
- IDisposable disposable;
- try {
- (obj as System.IO.TextWriter).WriteLine("ThisIsNotAUsingBlock");
- } finally {
- disposable = (obj as IDisposable);
- if (disposable != null) {
- disposable.Dispose();
- }
- }
- Console.WriteLine(disposable);
- }
}
}
\ No newline at end of file
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Generics.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Generics.cs
index 1b9433ee2..0ff0b26a5 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Generics.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Generics.cs
@@ -47,16 +47,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
Console.WriteLine(typeof(T1) + " " + typeof(T2));
}
-
- public T CastToTypeParameter(DerivedClass d) where T: BaseClass
- {
- return (T)(BaseClass)d;
- }
-
- public T New() where T : new()
- {
- return new T();
- }
}
class GenericClass
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs
new file mode 100644
index 000000000..308d66528
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs
@@ -0,0 +1,94 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
+//
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
+{
+ class Loops
+ {
+ static void Main()
+ {
+ ForWithMultipleVariables();
+ DoubleForEachWithSameVariable(new[] { "a", "b", "c" });
+ ForeachExceptForNameCollision(new[] { 42, 43, 44, 45 });
+ }
+
+ public static void ForWithMultipleVariables()
+ {
+ int x, y;
+ Console.WriteLine("before for");
+ for (x = y = 0; x < 10; x++) {
+ y++;
+ Console.WriteLine("x = " + x + ", y = " + y);
+ }
+ Console.WriteLine("after for");
+ }
+
+ public static void DoubleForEachWithSameVariable(IEnumerable enumerable)
+ {
+ Console.WriteLine("DoubleForEachWithSameVariable:");
+ foreach (string current in enumerable) {
+ Console.WriteLine(current.ToLower());
+ }
+ Console.WriteLine("after first loop");
+ foreach (string current in enumerable) {
+ Console.WriteLine(current.ToUpper());
+ }
+ Console.WriteLine("after second loop");
+ }
+
+ public static void ForeachExceptForNameCollision(IEnumerable inputs)
+ {
+ Console.WriteLine("ForeachWithNameCollision:");
+ int current;
+ using (IEnumerator enumerator = inputs.GetEnumerator()) {
+ while (enumerator.MoveNext()) {
+ current = enumerator.Current;
+ Console.WriteLine(current);
+ }
+ }
+ current = 1;
+ Console.WriteLine(current);
+ }
+
+ public static void NonGenericForeachWithReturnFallbackTest(IEnumerable e)
+ {
+ Console.WriteLine("NonGenericForeachWithReturnFallback:");
+ IEnumerator enumerator = e.GetEnumerator();
+ try {
+ Console.WriteLine("MoveNext");
+ if (enumerator.MoveNext()) {
+ object current = enumerator.Current;
+ Console.WriteLine("current: " + current);
+ }
+ } finally {
+ IDisposable disposable = enumerator as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+ Console.WriteLine("After finally!");
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Patterns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Patterns.cs
deleted file mode 100644
index 57a4e479b..000000000
--- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Patterns.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
-{
- ///
- /// This file contains special cases of some patterns that cannot be tested in pretty tests.
- ///
- class Patterns
- {
- static void Main()
- {
- SimpleUsingNullStatement();
- ForWithMultipleVariables();
- DoubleForEachWithSameVariable(new[] { "a", "b", "c" });
- ForeachExceptForNameCollision(new[] { 42, 43, 44, 45 });
- }
-
- ///
- /// Special case: Roslyn eliminates the try-finally altogether.
- ///
- public static void SimpleUsingNullStatement()
- {
- Console.WriteLine("before using");
- using (null) {
- Console.WriteLine("using (null)");
- }
- Console.WriteLine("after using");
- }
-
- public static void ForWithMultipleVariables()
- {
- int x, y;
- Console.WriteLine("before for");
- for (x = y = 0; x < 10; x++) {
- y++;
- Console.WriteLine("x = " + x + ", y = " + y);
- }
- Console.WriteLine("after for");
- }
-
- public static void DoubleForEachWithSameVariable(IEnumerable enumerable)
- {
- Console.WriteLine("DoubleForEachWithSameVariable:");
- foreach (string current in enumerable) {
- Console.WriteLine(current.ToLower());
- }
- Console.WriteLine("after first loop");
- foreach (string current in enumerable) {
- Console.WriteLine(current.ToUpper());
- }
- Console.WriteLine("after second loop");
- }
-
- public static void ForeachExceptForNameCollision(IEnumerable inputs)
- {
- Console.WriteLine("ForeachWithNameCollision:");
- int current;
- using (IEnumerator enumerator = inputs.GetEnumerator()) {
- while (enumerator.MoveNext()) {
- current = enumerator.Current;
- Console.WriteLine(current);
- }
- }
- current = 1;
- Console.WriteLine(current);
- }
- }
-}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs
new file mode 100644
index 000000000..1878456bb
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Using.cs
@@ -0,0 +1,164 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
+{
+ class Using
+ {
+ class PrintOnDispose : IDisposable
+ {
+ private string v;
+
+ public PrintOnDispose(string v)
+ {
+ this.v = v;
+ }
+
+ public void Dispose()
+ {
+ Console.WriteLine(this.v);
+ }
+ }
+
+ static void Main()
+ {
+ SimpleUsingNullStatement();
+ NoUsingDueToAssignment();
+ NoUsingDueToAssignment2();
+ NoUsingDueToByRefCall();
+ NoUsingDueToContinuedDisposableUse();
+ ContinuedObjectUse();
+ VariableAlreadyUsedBefore();
+ UsingObject();
+ }
+
+ ///
+ /// Special case: Roslyn eliminates the try-finally altogether.
+ ///
+ public static void SimpleUsingNullStatement()
+ {
+ Console.WriteLine("before using");
+ using (null) {
+ Console.WriteLine("using (null)");
+ }
+ Console.WriteLine("after using");
+ }
+
+ public static void NoUsingDueToAssignment()
+ {
+ PrintOnDispose printOnDispose = new PrintOnDispose("Wrong");
+ try {
+ printOnDispose = new PrintOnDispose("Correct");
+ } finally {
+ printOnDispose.Dispose();
+ }
+ }
+
+ public static void NoUsingDueToAssignment2()
+ {
+ PrintOnDispose printOnDispose = new PrintOnDispose("NoUsing(): Wrong");
+ try {
+ printOnDispose = new PrintOnDispose("NoUsing(): Correct");
+ } finally {
+ IDisposable disposable = (object)printOnDispose as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+ }
+
+ static void Clear(ref T t)
+ {
+ t = default(T);
+ }
+
+ public static void NoUsingDueToByRefCall()
+ {
+ PrintOnDispose printOnDispose = new PrintOnDispose("NoUsingDueToByRefCall(): Wrong");
+ try {
+ Console.WriteLine("NoUsingDueToByRefCall");
+ Clear(ref printOnDispose);
+ } finally {
+ IDisposable disposable = (object)printOnDispose as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+ }
+
+ public static void NoUsingDueToContinuedDisposableUse()
+ {
+ var obj = new System.IO.StringWriter();
+ IDisposable disposable;
+ try {
+ obj.WriteLine("NoUsingDueToContinuedDisposableUse");
+ } finally {
+ disposable = (object)obj as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+ Console.WriteLine(disposable);
+ }
+
+ public static void ContinuedObjectUse()
+ {
+ var obj = new System.IO.StringWriter();
+ try {
+ obj.WriteLine("ContinuedObjectUse");
+ } finally {
+ IDisposable disposable = (object)obj as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+ Console.WriteLine(obj);
+ }
+
+ public static void VariableAlreadyUsedBefore()
+ {
+ System.IO.StringWriter obj = new System.IO.StringWriter();
+ obj.Write("VariableAlreadyUsedBefore - 1");
+ Console.WriteLine(obj);
+ obj = new System.IO.StringWriter();
+ try {
+ obj.WriteLine("VariableAlreadyUsedBefore - 2");
+ } finally {
+ IDisposable disposable = (object)obj as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+ }
+
+ public static void UsingObject()
+ {
+ object obj = new object();
+ try {
+ Console.WriteLine("UsingObject: {0}", obj);
+ } finally {
+ IDisposable disposable = obj as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs
index 12e4b1308..72dbb9101 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs
@@ -2,7 +2,7 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
- public struct MutValueType
+ public struct MutValueType : IDisposable
{
public int val;
@@ -11,6 +11,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine("Inc() called on {0}", val);
val = val + 1;
}
+
+ public void Dispose()
+ {
+ Console.WriteLine("MutValueType disposed on {0}", val);
+ }
}
public struct GenericValueType
@@ -50,6 +55,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
ValueParameter(m);
Field();
Box();
+ Using();
var gvt = new GenericValueType("Test");
gvt.Call(ref gvt);
new ValueTypeCall().InstanceFieldTests();
@@ -88,11 +94,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
static void Box()
{
+ Console.WriteLine("Box");
object o = new MutValueType { val = 300 };
((MutValueType)o).Increment();
((MutValueType)o).Increment();
+ MutValueType unboxed1 = (MutValueType)o;
+ unboxed1.Increment();
+ unboxed1.Increment();
+ ((MutValueType)o).Increment();
+ MutValueType unboxed2 = (MutValueType)o;
+ unboxed2.val = 100;
+ ((MutValueType)o).Dispose();
}
-
+
MutValueType instanceField;
ValueTypeWithReadOnlyMember mutableInstanceFieldWithReadOnlyMember;
@@ -103,5 +117,41 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
mutableInstanceFieldWithReadOnlyMember = new ValueTypeWithReadOnlyMember(45);
Console.WriteLine(this.mutableInstanceFieldWithReadOnlyMember.Member);
}
+
+ static void Using()
+ {
+ Using1();
+ Using2();
+ Using3();
+ }
+
+ static void Using1()
+ {
+ Console.WriteLine("Using:");
+ using (var x = new MutValueType()) {
+ x.Increment();
+ }
+ }
+
+ static void Using2()
+ {
+ Console.WriteLine("Not using:");
+ var y = new MutValueType();
+ try {
+ y.Increment();
+ } finally {
+ MutValueType x = y;
+ x.Dispose();
+ }
+ }
+
+ static void Using3()
+ {
+ Console.WriteLine("Using with variable declared outside:");
+ MutValueType z;
+ using (z = new MutValueType()) {
+ z.Increment();
+ }
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs
new file mode 100644
index 000000000..e4da79407
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs
@@ -0,0 +1,54 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
+//
+// 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.
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
+{
+ internal class Generics
+ {
+ private class GenericClass
+ {
+ public void M(out GenericClass self)
+ {
+ self = this;
+ }
+ }
+
+ public class BaseClass
+ {
+ }
+
+ public class DerivedClass : BaseClass
+ {
+ }
+
+ public T CastToTypeParameter(DerivedClass d) where T : BaseClass
+ {
+ return (T)(BaseClass)d;
+ }
+
+ public T New() where T : new()
+ {
+ return new T();
+ }
+
+ public bool IsNull(T t)
+ {
+ return t == null;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.il
new file mode 100644
index 000000000..35cd026a6
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.il
@@ -0,0 +1,172 @@
+
+// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+
+
+// 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 lb3mdocq
+{
+ .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.
+ .permissionset reqmin
+ = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+.module lb3mdocq.dll
+// MVID: {4412C112-CBEB-40EB-BC42-5C82526C8657}
+.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
+// Image base: 0x03060000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+ extends [mscorlib]System.Object
+{
+ .class auto ansi nested private beforefieldinit GenericClass`1
+ extends [mscorlib]System.Object
+ {
+ .method public hidebysig instance void
+ M([out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/GenericClass`1& self) cil managed
+ {
+ // Code size 5 (0x5)
+ .maxstack 8
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: ldarg.0
+ IL_0003: stind.ref
+ IL_0004: ret
+ } // end of method GenericClass`1::M
+
+ .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 GenericClass`1::.ctor
+
+ } // end of class GenericClass`1
+
+ .class auto ansi nested public beforefieldinit BaseClass
+ extends [mscorlib]System.Object
+ {
+ .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 BaseClass::.ctor
+
+ } // end of class BaseClass
+
+ .class auto ansi nested public beforefieldinit DerivedClass
+ extends ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass
+ {
+ .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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass::.ctor()
+ IL_0006: ret
+ } // end of method DerivedClass::.ctor
+
+ } // end of class DerivedClass
+
+ .method public hidebysig instance !!T CastToTypeParameter<(ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass) T>(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/DerivedClass d) cil managed
+ {
+ // Code size 12 (0xc)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: unbox.any !!T
+ IL_0007: stloc.0
+ IL_0008: br.s IL_000a
+
+ IL_000a: ldloc.0
+ IL_000b: ret
+ } // end of method Generics::CastToTypeParameter
+
+ .method public hidebysig instance !!T New<.ctor T>() cil managed
+ {
+ // Code size 39 (0x27)
+ .maxstack 1
+ .locals init (!!T V_0,
+ !!T V_1)
+ IL_0000: nop
+ IL_0001: ldloca.s V_1
+ IL_0003: initobj !!T
+ IL_0009: ldloc.1
+ IL_000a: box !!T
+ IL_000f: brfalse.s IL_001c
+
+ IL_0011: ldloca.s V_1
+ IL_0013: initobj !!T
+ IL_0019: ldloc.1
+ IL_001a: br.s IL_0021
+
+ IL_001c: call !!0 [mscorlib]System.Activator::CreateInstance()
+ IL_0021: nop
+ IL_0022: stloc.0
+ IL_0023: br.s IL_0025
+
+ IL_0025: ldloc.0
+ IL_0026: ret
+ } // end of method Generics::New
+
+ .method public hidebysig instance bool
+ IsNull(!!T t) cil managed
+ {
+ // Code size 15 (0xf)
+ .maxstack 2
+ .locals init (bool V_0)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: box !!T
+ IL_0007: ldnull
+ IL_0008: ceq
+ IL_000a: stloc.0
+ IL_000b: br.s IL_000d
+
+ IL_000d: ldloc.0
+ IL_000e: ret
+ } // end of method Generics::IsNull
+
+ .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 Generics::.ctor
+
+} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+
+
+// =============================================================
+
+// *********** DISASSEMBLY COMPLETE ***********************
+// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Generics.res
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.il
new file mode 100644
index 000000000..9f5537892
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.il
@@ -0,0 +1,153 @@
+
+// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+
+
+// 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 sxsfxc4c
+{
+ .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.
+ .permissionset reqmin
+ = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+.module sxsfxc4c.dll
+// MVID: {4BDFEFB1-623B-4D1C-B489-AE8EC4D00CF2}
+.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
+// Image base: 0x02FA0000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+ extends [mscorlib]System.Object
+{
+ .class auto ansi nested private beforefieldinit GenericClass`1
+ extends [mscorlib]System.Object
+ {
+ .method public hidebysig instance void
+ M([out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/GenericClass`1& self) cil managed
+ {
+ // Code size 4 (0x4)
+ .maxstack 8
+ IL_0000: ldarg.1
+ IL_0001: ldarg.0
+ IL_0002: stind.ref
+ IL_0003: ret
+ } // end of method GenericClass`1::M
+
+ .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 GenericClass`1::.ctor
+
+ } // end of class GenericClass`1
+
+ .class auto ansi nested public beforefieldinit BaseClass
+ extends [mscorlib]System.Object
+ {
+ .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 BaseClass::.ctor
+
+ } // end of class BaseClass
+
+ .class auto ansi nested public beforefieldinit DerivedClass
+ extends ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass
+ {
+ .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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass::.ctor()
+ IL_0006: ret
+ } // end of method DerivedClass::.ctor
+
+ } // end of class DerivedClass
+
+ .method public hidebysig instance !!T CastToTypeParameter<(ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass) T>(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/DerivedClass d) cil managed
+ {
+ // Code size 7 (0x7)
+ .maxstack 8
+ IL_0000: ldarg.1
+ IL_0001: unbox.any !!T
+ IL_0006: ret
+ } // end of method Generics::CastToTypeParameter
+
+ .method public hidebysig instance !!T New<.ctor T>() cil managed
+ {
+ // Code size 32 (0x20)
+ .maxstack 1
+ .locals init (!!T V_0,
+ !!T V_1)
+ IL_0000: ldloca.s V_0
+ IL_0002: initobj !!T
+ IL_0008: ldloc.0
+ IL_0009: box !!T
+ IL_000e: brfalse.s IL_001a
+
+ IL_0010: ldloca.s V_1
+ IL_0012: initobj !!T
+ IL_0018: ldloc.1
+ IL_0019: ret
+
+ IL_001a: call !!0 [mscorlib]System.Activator::CreateInstance()
+ IL_001f: ret
+ } // end of method Generics::New
+
+ .method public hidebysig instance bool
+ IsNull(!!T t) cil managed
+ {
+ // Code size 10 (0xa)
+ .maxstack 8
+ IL_0000: ldarg.1
+ IL_0001: box !!T
+ IL_0006: ldnull
+ IL_0007: ceq
+ IL_0009: ret
+ } // end of method Generics::IsNull
+
+ .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 Generics::.ctor
+
+} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+
+
+// =============================================================
+
+// *********** DISASSEMBLY COMPLETE ***********************
+// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Generics.opt.res
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.roslyn.il
new file mode 100644
index 000000000..e52e38966
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.roslyn.il
@@ -0,0 +1,143 @@
+
+// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+
+
+// 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 Generics
+{
+ .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 Generics.dll
+// MVID: {6515FC2D-ED0B-41CF-9FD2-8CD5192CF5A1}
+.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
+// Image base: 0x01880000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+ extends [mscorlib]System.Object
+{
+ .class auto ansi nested private beforefieldinit GenericClass`1
+ extends [mscorlib]System.Object
+ {
+ .method public hidebysig instance void
+ M([out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/GenericClass`1& self) cil managed
+ {
+ // Code size 4 (0x4)
+ .maxstack 8
+ IL_0000: ldarg.1
+ IL_0001: ldarg.0
+ IL_0002: stind.ref
+ IL_0003: ret
+ } // end of method GenericClass`1::M
+
+ .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 GenericClass`1::.ctor
+
+ } // end of class GenericClass`1
+
+ .class auto ansi nested public beforefieldinit BaseClass
+ extends [mscorlib]System.Object
+ {
+ .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 BaseClass::.ctor
+
+ } // end of class BaseClass
+
+ .class auto ansi nested public beforefieldinit DerivedClass
+ extends ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass
+ {
+ .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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass::.ctor()
+ IL_0006: ret
+ } // end of method DerivedClass::.ctor
+
+ } // end of class DerivedClass
+
+ .method public hidebysig instance !!T CastToTypeParameter<(ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass) T>(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/DerivedClass d) cil managed
+ {
+ // Code size 7 (0x7)
+ .maxstack 8
+ IL_0000: ldarg.1
+ IL_0001: unbox.any !!T
+ IL_0006: ret
+ } // end of method Generics::CastToTypeParameter
+
+ .method public hidebysig instance !!T New<.ctor T>() cil managed
+ {
+ // Code size 6 (0x6)
+ .maxstack 8
+ IL_0000: call !!0 [mscorlib]System.Activator::CreateInstance()
+ IL_0005: ret
+ } // end of method Generics::New
+
+ .method public hidebysig instance bool
+ IsNull(!!T t) cil managed
+ {
+ // Code size 10 (0xa)
+ .maxstack 8
+ IL_0000: ldarg.1
+ IL_0001: box !!T
+ IL_0006: ldnull
+ IL_0007: ceq
+ IL_0009: ret
+ } // end of method Generics::IsNull
+
+ .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 Generics::.ctor
+
+} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+
+
+// =============================================================
+
+// *********** DISASSEMBLY COMPLETE ***********************
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.roslyn.il
new file mode 100644
index 000000000..2fcbc212e
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.roslyn.il
@@ -0,0 +1,166 @@
+
+// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+
+
+// 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 Generics
+{
+ .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 Generics.dll
+// MVID: {9BC41AF0-27DC-4022-8C9C-4D7039122350}
+.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
+// Image base: 0x03030000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+ extends [mscorlib]System.Object
+{
+ .class auto ansi nested private beforefieldinit GenericClass`1
+ extends [mscorlib]System.Object
+ {
+ .method public hidebysig instance void
+ M([out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/GenericClass`1& self) cil managed
+ {
+ // Code size 5 (0x5)
+ .maxstack 8
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: ldarg.0
+ IL_0003: stind.ref
+ IL_0004: ret
+ } // end of method GenericClass`1::M
+
+ .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 GenericClass`1::.ctor
+
+ } // end of class GenericClass`1
+
+ .class auto ansi nested public beforefieldinit BaseClass
+ extends [mscorlib]System.Object
+ {
+ .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 BaseClass::.ctor
+
+ } // end of class BaseClass
+
+ .class auto ansi nested public beforefieldinit DerivedClass
+ extends ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass
+ {
+ .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 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method DerivedClass::.ctor
+
+ } // end of class DerivedClass
+
+ .method public hidebysig instance !!T CastToTypeParameter<(ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/BaseClass) T>(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics/DerivedClass d) cil managed
+ {
+ // Code size 12 (0xc)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: unbox.any !!T
+ IL_0007: stloc.0
+ IL_0008: br.s IL_000a
+
+ IL_000a: ldloc.0
+ IL_000b: ret
+ } // end of method Generics::CastToTypeParameter
+
+ .method public hidebysig instance !!T New<.ctor T>() cil managed
+ {
+ // Code size 11 (0xb)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: nop
+ IL_0001: call !!0 [mscorlib]System.Activator::CreateInstance()
+ IL_0006: stloc.0
+ IL_0007: br.s IL_0009
+
+ IL_0009: ldloc.0
+ IL_000a: ret
+ } // end of method Generics::New
+
+ .method public hidebysig instance bool
+ IsNull(!!T t) cil managed
+ {
+ // Code size 15 (0xf)
+ .maxstack 2
+ .locals init (bool V_0)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: box !!T
+ IL_0007: ldnull
+ IL_0008: ceq
+ IL_000a: stloc.0
+ IL_000b: br.s IL_000d
+
+ IL_000d: ldloc.0
+ IL_000e: ret
+ } // end of method Generics::IsNull
+
+ .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 Generics::.ctor
+
+} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Generics
+
+
+// =============================================================
+
+// *********** DISASSEMBLY COMPLETE ***********************
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs
index b4e7d2500..113288a80 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs
@@ -220,5 +220,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine("Last: " + num);
}
+
+ public static void NonGenericForeachWithReturnFallbackTest(IEnumerable e)
+ {
+ Console.WriteLine("NonGenericForeachWithReturnFallback:");
+ IEnumerator enumerator = e.GetEnumerator();
+ try {
+ Console.WriteLine("MoveNext");
+ if (enumerator.MoveNext()) {
+ object current = enumerator.Current;
+ Console.WriteLine("current: " + current);
+ }
+ } finally {
+ IDisposable disposable = enumerator as IDisposable;
+ if (disposable != null)
+ disposable.Dispose();
+ }
+ Console.WriteLine("After finally!");
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il
index 9809aa115..ae3d4d573 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il
@@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
-.assembly nknknvk4
+.assembly kpve0joz
{
.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
@@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
-.module nknknvk4.dll
-// MVID: {90F191A2-32E6-4921-8379-FD1D98A52EE5}
+.module kpve0joz.dll
+// MVID: {20825129-FBF4-4D0D-B2DB-BB2FE12F3807}
.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
-// Image base: 0x02820000
+// Image base: 0x02A50000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -658,6 +658,78 @@
IL_005c: ret
} // end of method Loops::ForeachExceptForContinuedUse
+ .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed
+ {
+ // Code size 113 (0x71)
+ .maxstack 2
+ .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
+ object V_1,
+ class [mscorlib]System.IDisposable V_2,
+ bool V_3)
+ IL_0000: nop
+ IL_0001: ldstr "NonGenericForeachWithReturnFallback:"
+ IL_0006: call void [mscorlib]System.Console::WriteLine(string)
+ IL_000b: nop
+ IL_000c: ldarg.0
+ IL_000d: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
+ IL_0012: stloc.0
+ .try
+ {
+ IL_0013: nop
+ IL_0014: ldstr "MoveNext"
+ IL_0019: call void [mscorlib]System.Console::WriteLine(string)
+ IL_001e: nop
+ IL_001f: ldloc.0
+ IL_0020: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
+ IL_0025: ldc.i4.0
+ IL_0026: ceq
+ IL_0028: stloc.3
+ IL_0029: ldloc.3
+ IL_002a: brtrue.s IL_0046
+
+ IL_002c: nop
+ IL_002d: ldloc.0
+ IL_002e: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
+ IL_0033: stloc.1
+ IL_0034: ldstr "current: "
+ IL_0039: ldloc.1
+ IL_003a: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_003f: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0044: nop
+ IL_0045: nop
+ IL_0046: nop
+ IL_0047: leave.s IL_0064
+
+ } // end .try
+ finally
+ {
+ IL_0049: nop
+ IL_004a: ldloc.0
+ IL_004b: isinst [mscorlib]System.IDisposable
+ IL_0050: stloc.2
+ IL_0051: ldloc.2
+ IL_0052: ldnull
+ IL_0053: ceq
+ IL_0055: stloc.3
+ IL_0056: ldloc.3
+ IL_0057: brtrue.s IL_0062
+
+ IL_0059: nop
+ IL_005a: ldloc.2
+ IL_005b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0060: nop
+ IL_0061: nop
+ IL_0062: nop
+ IL_0063: endfinally
+ } // end handler
+ IL_0064: nop
+ IL_0065: ldstr "After finally!"
+ IL_006a: call void [mscorlib]System.Console::WriteLine(string)
+ IL_006f: nop
+ IL_0070: ret
+ } // end of method Loops::NonGenericForeachWithReturnFallbackTest
+
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il
index 1b6af9a3f..01bc47850 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il
@@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
-.assembly mwkkmknt
+.assembly ukcebu5a
{
.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
@@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
-.module mwkkmknt.dll
-// MVID: {A6DBEA2E-03CE-4841-BC30-F2B0EDA8AC75}
+.module ukcebu5a.dll
+// MVID: {D6A0A420-9828-48A7-BAA6-F0BAFB50052B}
.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
-// Image base: 0x02F00000
+// Image base: 0x02F50000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -473,6 +473,54 @@
IL_004b: ret
} // end of method Loops::ForeachExceptForContinuedUse
+ .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed
+ {
+ // Code size 88 (0x58)
+ .maxstack 2
+ .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
+ object V_1,
+ class [mscorlib]System.IDisposable V_2)
+ IL_0000: ldstr "NonGenericForeachWithReturnFallback:"
+ IL_0005: call void [mscorlib]System.Console::WriteLine(string)
+ IL_000a: ldarg.0
+ IL_000b: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
+ IL_0010: stloc.0
+ .try
+ {
+ IL_0011: ldstr "MoveNext"
+ IL_0016: call void [mscorlib]System.Console::WriteLine(string)
+ IL_001b: ldloc.0
+ IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
+ IL_0021: brfalse.s IL_003a
+
+ IL_0023: ldloc.0
+ IL_0024: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
+ IL_0029: stloc.1
+ IL_002a: ldstr "current: "
+ IL_002f: ldloc.1
+ IL_0030: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_0035: call void [mscorlib]System.Console::WriteLine(string)
+ IL_003a: leave.s IL_004d
+
+ } // end .try
+ finally
+ {
+ IL_003c: ldloc.0
+ IL_003d: isinst [mscorlib]System.IDisposable
+ IL_0042: stloc.2
+ IL_0043: ldloc.2
+ IL_0044: brfalse.s IL_004c
+
+ IL_0046: ldloc.2
+ IL_0047: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_004c: endfinally
+ } // end handler
+ IL_004d: ldstr "After finally!"
+ IL_0052: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0057: ret
+ } // end of method Loops::NonGenericForeachWithReturnFallbackTest
+
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il
index 5d6993254..4f67a29b3 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il
@@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Loops.dll
-// MVID: {03277CBF-8A87-4FAE-8B0F-A5BF6427386C}
+// MVID: {7BFC079C-5BB9-4A1A-A6EB-BA407AB11CEF}
.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
-// Image base: 0x01720000
+// Image base: 0x011F0000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -465,6 +465,54 @@
IL_004b: ret
} // end of method Loops::ForeachExceptForContinuedUse
+ .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed
+ {
+ // Code size 88 (0x58)
+ .maxstack 2
+ .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
+ object V_1,
+ class [mscorlib]System.IDisposable V_2)
+ IL_0000: ldstr "NonGenericForeachWithReturnFallback:"
+ IL_0005: call void [mscorlib]System.Console::WriteLine(string)
+ IL_000a: ldarg.0
+ IL_000b: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
+ IL_0010: stloc.0
+ .try
+ {
+ IL_0011: ldstr "MoveNext"
+ IL_0016: call void [mscorlib]System.Console::WriteLine(string)
+ IL_001b: ldloc.0
+ IL_001c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
+ IL_0021: brfalse.s IL_003a
+
+ IL_0023: ldloc.0
+ IL_0024: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
+ IL_0029: stloc.1
+ IL_002a: ldstr "current: "
+ IL_002f: ldloc.1
+ IL_0030: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_0035: call void [mscorlib]System.Console::WriteLine(string)
+ IL_003a: leave.s IL_004d
+
+ } // end .try
+ finally
+ {
+ IL_003c: ldloc.0
+ IL_003d: isinst [mscorlib]System.IDisposable
+ IL_0042: stloc.2
+ IL_0043: ldloc.2
+ IL_0044: brfalse.s IL_004c
+
+ IL_0046: ldloc.2
+ IL_0047: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_004c: endfinally
+ } // end handler
+ IL_004d: ldstr "After finally!"
+ IL_0052: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0057: ret
+ } // end of method Loops::NonGenericForeachWithReturnFallbackTest
+
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il
index 1fca3d17c..b5b720de7 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il
@@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Loops.dll
-// MVID: {BAC1C379-EE42-472A-8FCB-BE9DD2565A1C}
+// MVID: {C3B63F66-C940-45B6-9786-076A79DB1EC5}
.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
-// Image base: 0x027A0000
+// Image base: 0x011E0000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -621,6 +621,76 @@
IL_0056: ret
} // end of method Loops::ForeachExceptForContinuedUse
+ .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed
+ {
+ // Code size 111 (0x6f)
+ .maxstack 2
+ .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
+ bool V_1,
+ object V_2,
+ class [mscorlib]System.IDisposable V_3,
+ bool V_4)
+ IL_0000: nop
+ IL_0001: ldstr "NonGenericForeachWithReturnFallback:"
+ IL_0006: call void [mscorlib]System.Console::WriteLine(string)
+ IL_000b: nop
+ IL_000c: ldarg.0
+ IL_000d: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
+ IL_0012: stloc.0
+ .try
+ {
+ IL_0013: nop
+ IL_0014: ldstr "MoveNext"
+ IL_0019: call void [mscorlib]System.Console::WriteLine(string)
+ IL_001e: nop
+ IL_001f: ldloc.0
+ IL_0020: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
+ IL_0025: stloc.1
+ IL_0026: ldloc.1
+ IL_0027: brfalse.s IL_0043
+
+ IL_0029: nop
+ IL_002a: ldloc.0
+ IL_002b: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
+ IL_0030: stloc.2
+ IL_0031: ldstr "current: "
+ IL_0036: ldloc.2
+ IL_0037: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_003c: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0041: nop
+ IL_0042: nop
+ IL_0043: nop
+ IL_0044: leave.s IL_0063
+
+ } // end .try
+ finally
+ {
+ IL_0046: nop
+ IL_0047: ldloc.0
+ IL_0048: isinst [mscorlib]System.IDisposable
+ IL_004d: stloc.3
+ IL_004e: ldloc.3
+ IL_004f: ldnull
+ IL_0050: cgt.un
+ IL_0052: stloc.s V_4
+ IL_0054: ldloc.s V_4
+ IL_0056: brfalse.s IL_0061
+
+ IL_0058: nop
+ IL_0059: ldloc.3
+ IL_005a: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_005f: nop
+ IL_0060: nop
+ IL_0061: nop
+ IL_0062: endfinally
+ } // end handler
+ IL_0063: ldstr "After finally!"
+ IL_0068: call void [mscorlib]System.Console::WriteLine(string)
+ IL_006d: nop
+ IL_006e: ret
+ } // end of method Loops::NonGenericForeachWithReturnFallbackTest
+
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs
index 0279ff6ad..3f615c56b 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs
@@ -39,21 +39,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
- private class PrintOnDispose : IDisposable
- {
- private string v;
-
- public PrintOnDispose(string v)
- {
- this.v = v;
- }
-
- public void Dispose()
- {
- Console.WriteLine(this.v);
- }
- }
-
#if LEGACY_CSC
public void SimpleUsingNullStatement()
{
@@ -101,13 +86,38 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
- public void NoUsing()
+ private void UsingStatementOnNullableStruct(UsingStruct? us)
+ {
+ using (us) {
+ Console.WriteLine("using-body: " + us);
+ }
+ }
+
+ public void GenericUsing(T t) where T : IDisposable
+ {
+ using (t) {
+ Console.WriteLine(t);
+ }
+ }
+
+ public void GenericStructUsing(T t) where T : struct, IDisposable
+ {
+ using (t) {
+ Console.WriteLine(t);
+ }
+ }
+
+ public void GenericClassUsing(T t) where T : class, IDisposable
+ {
+ using (t) {
+ Console.WriteLine(t);
+ }
+ }
+
+ public void GenericNullableUsing(T? t) where T : struct, IDisposable
{
- PrintOnDispose printOnDispose = new PrintOnDispose("Wrong");
- try {
- printOnDispose = new PrintOnDispose("Correct");
- } finally {
- printOnDispose.Dispose();
+ using (t) {
+ Console.WriteLine(t);
}
}
}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.il
index 0001b7454..9f08d8d24 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.il
@@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
-.assembly ivwruwuv
+.assembly wgewqkaz
{
.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
@@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
-.module ivwruwuv.dll
-// MVID: {CC03EEA0-A20B-4731-ADE9-CC2419DB878B}
+.module wgewqkaz.dll
+// MVID: {298A3373-9EF1-4D76-BCEC-2E82A945AF55}
.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
-// Image base: 0x00BA0000
+// Image base: 0x00550000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -67,42 +67,6 @@
} // end of class UsingStruct
- .class auto ansi nested private beforefieldinit PrintOnDispose
- extends [mscorlib]System.Object
- implements [mscorlib]System.IDisposable
- {
- .field private string v
- .method public hidebysig specialname rtspecialname
- instance void .ctor(string v) cil managed
- {
- // Code size 17 (0x11)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: nop
- IL_0007: nop
- IL_0008: ldarg.0
- IL_0009: ldarg.1
- IL_000a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_000f: nop
- IL_0010: ret
- } // end of method PrintOnDispose::.ctor
-
- .method public hidebysig newslot virtual final
- instance void Dispose() cil managed
- {
- // Code size 14 (0xe)
- .maxstack 8
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_0007: call void [mscorlib]System.Console::WriteLine(string)
- IL_000c: nop
- IL_000d: ret
- } // end of method PrintOnDispose::Dispose
-
- } // end of class PrintOnDispose
-
.method public hidebysig instance void
SimpleUsingNullStatement() cil managed
{
@@ -338,38 +302,206 @@
IL_0034: ret
} // end of method Using::UsingStatementOnStructWithVariable
+ .method private hidebysig instance void
+ UsingStatementOnNullableStruct(valuetype [mscorlib]System.Nullable`1 us) cil managed
+ {
+ // Code size 63 (0x3f)
+ .maxstack 2
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0,
+ bool V_1)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldstr "using-body: "
+ IL_0009: ldarg.1
+ IL_000a: box valuetype [mscorlib]System.Nullable`1
+ IL_000f: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_0014: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0019: nop
+ IL_001a: nop
+ IL_001b: leave.s IL_003d
+
+ } // end .try
+ finally
+ {
+ IL_001d: ldloca.s V_0
+ IL_001f: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_0024: ldc.i4.0
+ IL_0025: ceq
+ IL_0027: stloc.1
+ IL_0028: ldloc.1
+ IL_0029: brtrue.s IL_003c
+
+ IL_002b: ldloc.0
+ IL_002c: box valuetype [mscorlib]System.Nullable`1
+ IL_0031: unbox.any [mscorlib]System.IDisposable
+ IL_0036: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_003b: nop
+ IL_003c: endfinally
+ } // end handler
+ IL_003d: nop
+ IL_003e: ret
+ } // end of method Using::UsingStatementOnNullableStruct
+
+ .method public hidebysig instance void
+ GenericUsing<([mscorlib]System.IDisposable) T>(!!T t) cil managed
+ {
+ // Code size 49 (0x31)
+ .maxstack 2
+ .locals init (!!T V_0,
+ bool V_1)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box !!T
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_002f
+
+ } // end .try
+ finally
+ {
+ IL_0013: ldloc.0
+ IL_0014: box !!T
+ IL_0019: ldnull
+ IL_001a: ceq
+ IL_001c: stloc.1
+ IL_001d: ldloc.1
+ IL_001e: brtrue.s IL_002e
+
+ IL_0020: ldloca.s V_0
+ IL_0022: constrained. !!T
+ IL_0028: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_002d: nop
+ IL_002e: endfinally
+ } // end handler
+ IL_002f: nop
+ IL_0030: ret
+ } // end of method Using::GenericUsing
+
.method public hidebysig instance void
- NoUsing() cil managed
+ GenericStructUsing(!!T t) cil managed
{
- // Code size 39 (0x27)
+ // Code size 36 (0x24)
.maxstack 1
- .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose V_0)
+ .locals init (!!T V_0)
IL_0000: nop
- IL_0001: ldstr "Wrong"
- IL_0006: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_000b: stloc.0
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
.try
{
- IL_000c: nop
- IL_000d: ldstr "Correct"
- IL_0012: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_0017: stloc.0
- IL_0018: nop
- IL_0019: leave.s IL_0025
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box !!T
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_0022
+
+ } // end .try
+ finally
+ {
+ IL_0013: ldloca.s V_0
+ IL_0015: constrained. !!T
+ IL_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0020: nop
+ IL_0021: endfinally
+ } // end handler
+ IL_0022: nop
+ IL_0023: ret
+ } // end of method Using::GenericStructUsing
+
+ .method public hidebysig instance void
+ GenericClassUsing(!!T t) cil managed
+ {
+ // Code size 49 (0x31)
+ .maxstack 2
+ .locals init (!!T V_0,
+ bool V_1)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box !!T
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_002f
+
+ } // end .try
+ finally
+ {
+ IL_0013: ldloc.0
+ IL_0014: box !!T
+ IL_0019: ldnull
+ IL_001a: ceq
+ IL_001c: stloc.1
+ IL_001d: ldloc.1
+ IL_001e: brtrue.s IL_002e
+
+ IL_0020: ldloca.s V_0
+ IL_0022: constrained. !!T
+ IL_0028: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_002d: nop
+ IL_002e: endfinally
+ } // end handler
+ IL_002f: nop
+ IL_0030: ret
+ } // end of method Using::GenericClassUsing
+
+ .method public hidebysig instance void
+ GenericNullableUsing(valuetype [mscorlib]System.Nullable`1 t) cil managed
+ {
+ // Code size 53 (0x35)
+ .maxstack 2
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0,
+ bool V_1)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box valuetype [mscorlib]System.Nullable`1
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_0033
} // end .try
finally
{
- IL_001b: nop
- IL_001c: ldloc.0
- IL_001d: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::Dispose()
- IL_0022: nop
- IL_0023: nop
- IL_0024: endfinally
+ IL_0013: ldloca.s V_0
+ IL_0015: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_001a: ldc.i4.0
+ IL_001b: ceq
+ IL_001d: stloc.1
+ IL_001e: ldloc.1
+ IL_001f: brtrue.s IL_0032
+
+ IL_0021: ldloc.0
+ IL_0022: box valuetype [mscorlib]System.Nullable`1
+ IL_0027: unbox.any [mscorlib]System.IDisposable
+ IL_002c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0031: nop
+ IL_0032: endfinally
} // end handler
- IL_0025: nop
- IL_0026: ret
- } // end of method Using::NoUsing
+ IL_0033: nop
+ IL_0034: ret
+ } // end of method Using::GenericNullableUsing
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.il
index 28df258fe..79a3b695b 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.il
@@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
-.assembly '2darmbjq'
+.assembly ms0npdxf
{
.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
@@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
-.module '2darmbjq.dll'
-// MVID: {947C8C54-1B8B-4766-9D81-4FCCD54C005F}
+.module ms0npdxf.dll
+// MVID: {060FC94F-8DAE-4720-AF67-48BEEC70FFC8}
.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
-// Image base: 0x02B80000
+// Image base: 0x030B0000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -64,37 +64,6 @@
} // end of class UsingStruct
- .class auto ansi nested private beforefieldinit PrintOnDispose
- extends [mscorlib]System.Object
- implements [mscorlib]System.IDisposable
- {
- .field private string v
- .method public hidebysig specialname rtspecialname
- instance void .ctor(string v) cil managed
- {
- // Code size 14 (0xe)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: ldarg.0
- IL_0007: ldarg.1
- IL_0008: stfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_000d: ret
- } // end of method PrintOnDispose::.ctor
-
- .method public hidebysig newslot virtual final
- instance void Dispose() cil managed
- {
- // Code size 12 (0xc)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: ret
- } // end of method PrintOnDispose::Dispose
-
- } // end of class PrintOnDispose
-
.method public hidebysig instance void
SimpleUsingNullStatement() cil managed
{
@@ -272,31 +241,156 @@
IL_002d: ret
} // end of method Using::UsingStatementOnStructWithVariable
+ .method private hidebysig instance void
+ UsingStatementOnNullableStruct(valuetype [mscorlib]System.Nullable`1 us) cil managed
+ {
+ // Code size 52 (0x34)
+ .maxstack 2
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldstr "using-body: "
+ IL_0007: ldarg.1
+ IL_0008: box valuetype [mscorlib]System.Nullable`1
+ IL_000d: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_0012: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0017: leave.s IL_0033
+
+ } // end .try
+ finally
+ {
+ IL_0019: ldloca.s V_0
+ IL_001b: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_0020: brfalse.s IL_0032
+
+ IL_0022: ldloc.0
+ IL_0023: box valuetype [mscorlib]System.Nullable`1
+ IL_0028: unbox.any [mscorlib]System.IDisposable
+ IL_002d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0032: endfinally
+ } // end handler
+ IL_0033: ret
+ } // end of method Using::UsingStatementOnNullableStruct
+
+ .method public hidebysig instance void
+ GenericUsing<([mscorlib]System.IDisposable) T>(!!T t) cil managed
+ {
+ // Code size 38 (0x26)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldarg.1
+ IL_0003: box !!T
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_0025
+
+ } // end .try
+ finally
+ {
+ IL_000f: ldloc.0
+ IL_0010: box !!T
+ IL_0015: brfalse.s IL_0024
+
+ IL_0017: ldloca.s V_0
+ IL_0019: constrained. !!T
+ IL_001f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0024: endfinally
+ } // end handler
+ IL_0025: ret
+ } // end of method Using::GenericUsing
+
.method public hidebysig instance void
- NoUsing() cil managed
+ GenericStructUsing(!!T t) cil managed
{
- // Code size 32 (0x20)
+ // Code size 30 (0x1e)
.maxstack 1
- .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose V_0)
- IL_0000: ldstr "Wrong"
- IL_0005: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_000a: stloc.0
+ .locals init (!!T V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
.try
{
- IL_000b: ldstr "Correct"
- IL_0010: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_0015: stloc.0
- IL_0016: leave.s IL_001f
+ IL_0002: ldarg.1
+ IL_0003: box !!T
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_001d
} // end .try
finally
{
+ IL_000f: ldloca.s V_0
+ IL_0011: constrained. !!T
+ IL_0017: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_001c: endfinally
+ } // end handler
+ IL_001d: ret
+ } // end of method Using::GenericStructUsing
+
+ .method public hidebysig instance void
+ GenericClassUsing(!!T t) cil managed
+ {
+ // Code size 38 (0x26)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldarg.1
+ IL_0003: box !!T
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_0025
+
+ } // end .try
+ finally
+ {
+ IL_000f: ldloc.0
+ IL_0010: box !!T
+ IL_0015: brfalse.s IL_0024
+
+ IL_0017: ldloca.s V_0
+ IL_0019: constrained. !!T
+ IL_001f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0024: endfinally
+ } // end handler
+ IL_0025: ret
+ } // end of method Using::GenericClassUsing
+
+ .method public hidebysig instance void
+ GenericNullableUsing(valuetype [mscorlib]System.Nullable`1 t) cil managed
+ {
+ // Code size 42 (0x2a)
+ .maxstack 1
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldarg.1
+ IL_0003: box valuetype [mscorlib]System.Nullable`1
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_0029
+
+ } // end .try
+ finally
+ {
+ IL_000f: ldloca.s V_0
+ IL_0011: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_0016: brfalse.s IL_0028
+
IL_0018: ldloc.0
- IL_0019: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::Dispose()
- IL_001e: endfinally
+ IL_0019: box valuetype [mscorlib]System.Nullable`1
+ IL_001e: unbox.any [mscorlib]System.IDisposable
+ IL_0023: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0028: endfinally
} // end handler
- IL_001f: ret
- } // end of method Using::NoUsing
+ IL_0029: ret
+ } // end of method Using::GenericNullableUsing
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.roslyn.il
index e0655784c..f8778c673 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.roslyn.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.opt.roslyn.il
@@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Using.dll
-// MVID: {8BD4B2FD-6883-4252-8E37-486B759E3F3E}
+// MVID: {AD3F5551-3D76-468D-A005-EEF5916A56A8}
.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
-// Image base: 0x00CA0000
+// Image base: 0x015E0000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -68,37 +68,6 @@
} // end of class UsingStruct
- .class auto ansi nested private beforefieldinit PrintOnDispose
- extends [mscorlib]System.Object
- implements [mscorlib]System.IDisposable
- {
- .field private string v
- .method public hidebysig specialname rtspecialname
- instance void .ctor(string v) cil managed
- {
- // Code size 14 (0xe)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: ldarg.0
- IL_0007: ldarg.1
- IL_0008: stfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_000d: ret
- } // end of method PrintOnDispose::.ctor
-
- .method public hidebysig newslot virtual final
- instance void Dispose() cil managed
- {
- // Code size 12 (0xc)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: ret
- } // end of method PrintOnDispose::Dispose
-
- } // end of class PrintOnDispose
-
.method public hidebysig instance void
SimpleUsingExpressionStatement() cil managed
{
@@ -249,31 +218,162 @@
IL_002d: ret
} // end of method Using::UsingStatementOnStructWithVariable
+ .method private hidebysig instance void
+ UsingStatementOnNullableStruct(valuetype [mscorlib]System.Nullable`1 us) cil managed
+ {
+ // Code size 57 (0x39)
+ .maxstack 2
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0,
+ valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/UsingStruct V_1)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldstr "using-body: "
+ IL_0007: ldarg.1
+ IL_0008: box valuetype [mscorlib]System.Nullable`1
+ IL_000d: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_0012: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0017: leave.s IL_0038
+
+ } // end .try
+ finally
+ {
+ IL_0019: ldloca.s V_0
+ IL_001b: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_0020: brfalse.s IL_0037
+
+ IL_0022: ldloca.s V_0
+ IL_0024: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault()
+ IL_0029: stloc.1
+ IL_002a: ldloca.s V_1
+ IL_002c: constrained. ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/UsingStruct
+ IL_0032: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0037: endfinally
+ } // end handler
+ IL_0038: ret
+ } // end of method Using::UsingStatementOnNullableStruct
+
+ .method public hidebysig instance void
+ GenericUsing<([mscorlib]System.IDisposable) T>(!!T t) cil managed
+ {
+ // Code size 38 (0x26)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldarg.1
+ IL_0003: box !!T
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_0025
+
+ } // end .try
+ finally
+ {
+ IL_000f: ldloc.0
+ IL_0010: box !!T
+ IL_0015: brfalse.s IL_0024
+
+ IL_0017: ldloca.s V_0
+ IL_0019: constrained. !!T
+ IL_001f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0024: endfinally
+ } // end handler
+ IL_0025: ret
+ } // end of method Using::GenericUsing
+
+ .method public hidebysig instance void
+ GenericStructUsing(!!T t) cil managed
+ {
+ // Code size 30 (0x1e)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldarg.1
+ IL_0003: box !!T
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_001d
+
+ } // end .try
+ finally
+ {
+ IL_000f: ldloca.s V_0
+ IL_0011: constrained. !!T
+ IL_0017: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_001c: endfinally
+ } // end handler
+ IL_001d: ret
+ } // end of method Using::GenericStructUsing
+
+ .method public hidebysig instance void
+ GenericClassUsing(!!T t) cil managed
+ {
+ // Code size 36 (0x24)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
+ .try
+ {
+ IL_0002: ldarg.1
+ IL_0003: box !!T
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_0023
+
+ } // end .try
+ finally
+ {
+ IL_000f: ldloc.0
+ IL_0010: box !!T
+ IL_0015: brfalse.s IL_0022
+
+ IL_0017: ldloc.0
+ IL_0018: box !!T
+ IL_001d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0022: endfinally
+ } // end handler
+ IL_0023: ret
+ } // end of method Using::GenericClassUsing
+
.method public hidebysig instance void
- NoUsing() cil managed
+ GenericNullableUsing(valuetype [mscorlib]System.Nullable`1 t) cil managed
{
- // Code size 32 (0x20)
+ // Code size 47 (0x2f)
.maxstack 1
- .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose V_0)
- IL_0000: ldstr "Wrong"
- IL_0005: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_000a: stloc.0
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0,
+ !!T V_1)
+ IL_0000: ldarg.1
+ IL_0001: stloc.0
.try
{
- IL_000b: ldstr "Correct"
- IL_0010: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_0015: stloc.0
- IL_0016: leave.s IL_001f
+ IL_0002: ldarg.1
+ IL_0003: box valuetype [mscorlib]System.Nullable`1
+ IL_0008: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000d: leave.s IL_002e
} // end .try
finally
{
- IL_0018: ldloc.0
- IL_0019: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::Dispose()
- IL_001e: endfinally
+ IL_000f: ldloca.s V_0
+ IL_0011: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_0016: brfalse.s IL_002d
+
+ IL_0018: ldloca.s V_0
+ IL_001a: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault()
+ IL_001f: stloc.1
+ IL_0020: ldloca.s V_1
+ IL_0022: constrained. !!T
+ IL_0028: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_002d: endfinally
} // end handler
- IL_001f: ret
- } // end of method Using::NoUsing
+ IL_002e: ret
+ } // end of method Using::GenericNullableUsing
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.roslyn.il
index 45493b085..601aba087 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.roslyn.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.roslyn.il
@@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Using.dll
-// MVID: {3686D0B4-0A65-4B60-A432-7042528E2212}
+// MVID: {7437788B-6603-4074-AE80-83AE987F007D}
.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
-// Image base: 0x02E50000
+// Image base: 0x011B0000
// =============== CLASS MEMBERS DECLARATION ===================
@@ -71,41 +71,6 @@
} // end of class UsingStruct
- .class auto ansi nested private beforefieldinit PrintOnDispose
- extends [mscorlib]System.Object
- implements [mscorlib]System.IDisposable
- {
- .field private string v
- .method public hidebysig specialname rtspecialname
- instance void .ctor(string v) cil managed
- {
- // Code size 16 (0x10)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: nop
- IL_0007: nop
- IL_0008: ldarg.0
- IL_0009: ldarg.1
- IL_000a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_000f: ret
- } // end of method PrintOnDispose::.ctor
-
- .method public hidebysig newslot virtual final
- instance void Dispose() cil managed
- {
- // Code size 14 (0xe)
- .maxstack 8
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::v
- IL_0007: call void [mscorlib]System.Console::WriteLine(string)
- IL_000c: nop
- IL_000d: ret
- } // end of method PrintOnDispose::Dispose
-
- } // end of class PrintOnDispose
-
.method public hidebysig instance void
SimpleUsingExpressionStatement() cil managed
{
@@ -282,37 +247,187 @@
IL_0032: ret
} // end of method Using::UsingStatementOnStructWithVariable
+ .method private hidebysig instance void
+ UsingStatementOnNullableStruct(valuetype [mscorlib]System.Nullable`1 us) cil managed
+ {
+ // Code size 62 (0x3e)
+ .maxstack 2
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0,
+ valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/UsingStruct V_1)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldstr "using-body: "
+ IL_0009: ldarg.1
+ IL_000a: box valuetype [mscorlib]System.Nullable`1
+ IL_000f: call string [mscorlib]System.String::Concat(object,
+ object)
+ IL_0014: call void [mscorlib]System.Console::WriteLine(string)
+ IL_0019: nop
+ IL_001a: nop
+ IL_001b: leave.s IL_003d
+
+ } // end .try
+ finally
+ {
+ IL_001d: ldloca.s V_0
+ IL_001f: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_0024: brfalse.s IL_003c
+
+ IL_0026: ldloca.s V_0
+ IL_0028: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault()
+ IL_002d: stloc.1
+ IL_002e: ldloca.s V_1
+ IL_0030: constrained. ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/UsingStruct
+ IL_0036: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_003b: nop
+ IL_003c: endfinally
+ } // end handler
+ IL_003d: ret
+ } // end of method Using::UsingStatementOnNullableStruct
+
.method public hidebysig instance void
- NoUsing() cil managed
+ GenericUsing<([mscorlib]System.IDisposable) T>(!!T t) cil managed
{
- // Code size 38 (0x26)
+ // Code size 43 (0x2b)
.maxstack 1
- .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose V_0)
+ .locals init (!!T V_0)
IL_0000: nop
- IL_0001: ldstr "Wrong"
- IL_0006: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_000b: stloc.0
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
.try
{
- IL_000c: nop
- IL_000d: ldstr "Correct"
- IL_0012: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::.ctor(string)
- IL_0017: stloc.0
- IL_0018: nop
- IL_0019: leave.s IL_0025
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box !!T
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_002a
} // end .try
finally
{
- IL_001b: nop
- IL_001c: ldloc.0
- IL_001d: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Using/PrintOnDispose::Dispose()
- IL_0022: nop
- IL_0023: nop
- IL_0024: endfinally
+ IL_0013: ldloc.0
+ IL_0014: box !!T
+ IL_0019: brfalse.s IL_0029
+
+ IL_001b: ldloca.s V_0
+ IL_001d: constrained. !!T
+ IL_0023: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0028: nop
+ IL_0029: endfinally
} // end handler
- IL_0025: ret
- } // end of method Using::NoUsing
+ IL_002a: ret
+ } // end of method Using::GenericUsing
+
+ .method public hidebysig instance void
+ GenericStructUsing(!!T t) cil managed
+ {
+ // Code size 35 (0x23)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box !!T
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_0022
+
+ } // end .try
+ finally
+ {
+ IL_0013: ldloca.s V_0
+ IL_0015: constrained. !!T
+ IL_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0020: nop
+ IL_0021: endfinally
+ } // end handler
+ IL_0022: ret
+ } // end of method Using::GenericStructUsing
+
+ .method public hidebysig instance void
+ GenericClassUsing(!!T t) cil managed
+ {
+ // Code size 41 (0x29)
+ .maxstack 1
+ .locals init (!!T V_0)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box !!T
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_0028
+
+ } // end .try
+ finally
+ {
+ IL_0013: ldloc.0
+ IL_0014: box !!T
+ IL_0019: brfalse.s IL_0027
+
+ IL_001b: ldloc.0
+ IL_001c: box !!T
+ IL_0021: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0026: nop
+ IL_0027: endfinally
+ } // end handler
+ IL_0028: ret
+ } // end of method Using::GenericClassUsing
+
+ .method public hidebysig instance void
+ GenericNullableUsing(valuetype [mscorlib]System.Nullable`1 t) cil managed
+ {
+ // Code size 52 (0x34)
+ .maxstack 1
+ .locals init (valuetype [mscorlib]System.Nullable`1 V_0,
+ !!T V_1)
+ IL_0000: nop
+ IL_0001: ldarg.1
+ IL_0002: stloc.0
+ .try
+ {
+ IL_0003: nop
+ IL_0004: ldarg.1
+ IL_0005: box valuetype [mscorlib]System.Nullable`1
+ IL_000a: call void [mscorlib]System.Console::WriteLine(object)
+ IL_000f: nop
+ IL_0010: nop
+ IL_0011: leave.s IL_0033
+
+ } // end .try
+ finally
+ {
+ IL_0013: ldloca.s V_0
+ IL_0015: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue()
+ IL_001a: brfalse.s IL_0032
+
+ IL_001c: ldloca.s V_0
+ IL_001e: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault()
+ IL_0023: stloc.1
+ IL_0024: ldloca.s V_1
+ IL_0026: constrained. !!T
+ IL_002c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
+ IL_0031: nop
+ IL_0032: endfinally
+ } // end handler
+ IL_0033: ret
+ } // end of method Using::GenericNullableUsing
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index c2c7a223f..1a46017d6 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp
return expr.ConvertToBoolean(this, negate);
}
- ExpressionWithResolveResult ConvertVariable(ILVariable variable)
+ internal ExpressionWithResolveResult ConvertVariable(ILVariable variable)
{
Expression expr;
if (variable.Kind == VariableKind.Parameter && variable.Index < 0)
diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
index da962a304..7f0e96855 100644
--- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
@@ -257,7 +257,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
#region foreach construction
- static readonly AssignmentExpression getEnumeratorPattern = new AssignmentExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), new InvocationExpression(new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetEnumerator")));
+ static readonly InvocationExpression getEnumeratorPattern = new InvocationExpression(new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetEnumerator"));
static readonly InvocationExpression moveNextConditionPattern = new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNext"));
protected internal override Statement VisitUsingInstruction(UsingInstruction inst)
@@ -265,19 +265,49 @@ namespace ICSharpCode.Decompiler.CSharp
var transformed = TransformToForeach(inst, out var resource);
if (transformed != null)
return transformed;
- return new UsingStatement {
- ResourceAcquisition = resource,
- EmbeddedStatement = ConvertAsBlock(inst.Body)
- };
+ AstNode usingInit = resource;
+ var var = inst.Variable;
+ if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) {
+ var.Kind = VariableKind.Local;
+ var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable);
+ var disposeVariable = currentFunction.RegisterVariable(
+ VariableKind.Local, disposeType,
+ AssignVariableNames.GenerateVariableName(currentFunction, disposeType)
+ );
+ return new BlockStatement {
+ new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(var).Expression, resource.Detach())),
+ new TryCatchStatement {
+ TryBlock = ConvertAsBlock(inst.Body),
+ FinallyBlock = new BlockStatement() {
+ new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, new AsExpression(exprBuilder.ConvertVariable(var).Expression, exprBuilder.ConvertType(disposeType)))),
+ new IfElseStatement {
+ Condition = new BinaryOperatorExpression(exprBuilder.ConvertVariable(disposeVariable), BinaryOperatorType.InEquality, new NullReferenceExpression()),
+ TrueStatement = new ExpressionStatement(new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, "Dispose")))
+ }
+ }
+ },
+ };
+ } else {
+ if (var.LoadCount > 0 || var.AddressCount > 0) {
+ var type = settings.AnonymousTypes && var.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(var.Type);
+ var vds = new VariableDeclarationStatement(type, var.Name, resource);
+ vds.Variables.Single().AddAnnotation(new ILVariableResolveResult(var, var.Type));
+ usingInit = vds;
+ }
+ return new UsingStatement {
+ ResourceAcquisition = usingInit,
+ EmbeddedStatement = ConvertAsBlock(inst.Body)
+ };
+ }
}
- Statement TransformToForeach(UsingInstruction inst, out TranslatedExpression resource)
+ Statement TransformToForeach(UsingInstruction inst, out Expression resource)
{
resource = exprBuilder.Translate(inst.ResourceExpression);
- var m = getEnumeratorPattern.Match(resource.Expression);
+ var m = getEnumeratorPattern.Match(resource);
if (!(inst.Body is BlockContainer container) || !m.Success)
return null;
- var enumeratorVar = m.Get("enumerator").Single().GetILVariable();
+ var enumeratorVar = inst.Variable;
var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
var loop = DetectedLoop.DetectLoop(loopContainer);
if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
@@ -322,7 +352,7 @@ namespace ICSharpCode.Decompiler.CSharp
itemVariable.Kind = VariableKind.ForeachLocal;
itemVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), itemVariable);
}
- var whileLoop = (WhileStatement)ConvertAsBlock(inst.Body).First();
+ var whileLoop = (WhileStatement)ConvertAsBlock(container).First();
BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();
foreachBody.Statements.First().Detach();
var foreachStmt = new ForeachStatement {
@@ -331,6 +361,7 @@ namespace ICSharpCode.Decompiler.CSharp
InExpression = collectionExpr.Detach(),
EmbeddedStatement = foreachBody
};
+ foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));
if (optionalReturnAfterLoop != null) {
return new BlockStatement {
Statements = {
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
index 334acde34..91386de38 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
@@ -314,8 +314,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
while (!(v.InsertionPoint.nextNode.Parent is BlockStatement)) {
if (v.InsertionPoint.nextNode.Parent is ForStatement f && v.InsertionPoint.nextNode == f.Initializers.FirstOrDefault() && IsMatchingAssignment(v, out _))
break;
- if (v.InsertionPoint.nextNode.Parent is UsingStatement u && v.InsertionPoint.nextNode == u.ResourceAcquisition && IsMatchingAssignment(v, out _))
- break;
v.InsertionPoint = v.InsertionPoint.Up();
}
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
index 6b16a7273..96e9a9516 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
@@ -98,6 +98,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
break;
+ case "System.Activator.CreateInstance":
+ if (method.TypeArguments.Count == 1 && arguments.Length == 0 && method.TypeArguments[0].Kind == TypeKind.TypeParameter) {
+ invocationExpression.ReplaceWith(new ObjectCreateExpression(context.TypeSystemAstBuilder.ConvertType(method.TypeArguments.First())));
+ }
+ break;
}
BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name);
diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs
index 130bdb566..8f6ec5e08 100644
--- a/ICSharpCode.Decompiler/IL/ILVariable.cs
+++ b/ICSharpCode.Decompiler/IL/ILVariable.cs
@@ -303,6 +303,12 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.InitializerTarget:
output.Write("initializer ");
break;
+ case VariableKind.ForeachLocal:
+ output.Write("foreach ");
+ break;
+ case VariableKind.UsingLocal:
+ output.Write("using ");
+ break;
default:
throw new ArgumentOutOfRangeException();
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs
index 79cd405a4..015b312b7 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions.cs
@@ -1701,13 +1701,53 @@ namespace ICSharpCode.Decompiler.IL
namespace ICSharpCode.Decompiler.IL
{
/// Using statement
- public sealed partial class UsingInstruction : ILInstruction
+ public sealed partial class UsingInstruction : ILInstruction, IStoreInstruction
{
- public UsingInstruction(ILInstruction resourceExpression, ILInstruction body) : base(OpCode.UsingInstruction)
+ public UsingInstruction(ILVariable variable, ILInstruction resourceExpression, ILInstruction body) : base(OpCode.UsingInstruction)
{
+ Debug.Assert(variable != null);
+ this.variable = variable;
this.ResourceExpression = resourceExpression;
this.Body = body;
}
+ ILVariable variable;
+ public ILVariable Variable {
+ get { return variable; }
+ set {
+ Debug.Assert(value != null);
+ if (IsConnected)
+ variable.RemoveStoreInstruction(this);
+ variable = value;
+ if (IsConnected)
+ variable.AddStoreInstruction(this);
+ }
+ }
+
+ public int IndexInStoreInstructionList { get; set; } = -1;
+
+ int IInstructionWithVariableOperand.IndexInVariableInstructionMapping {
+ get { return ((IStoreInstruction)this).IndexInStoreInstructionList; }
+ set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; }
+ }
+
+ protected override void Connected()
+ {
+ base.Connected();
+ variable.AddStoreInstruction(this);
+ }
+
+ protected override void Disconnected()
+ {
+ variable.RemoveStoreInstruction(this);
+ base.Disconnected();
+ }
+
+ internal override void CheckInvariant(ILPhase phase)
+ {
+ base.CheckInvariant(phase);
+ Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function));
+ Debug.Assert(phase <= ILPhase.InILReader || variable.Function.Variables[variable.IndexInFunction] == variable);
+ }
public static readonly SlotInfo ResourceExpressionSlot = new SlotInfo("ResourceExpression", canInlineInto: true);
ILInstruction resourceExpression;
public ILInstruction ResourceExpression {
@@ -1775,11 +1815,11 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.Void; } }
protected override InstructionFlags ComputeFlags()
{
- return resourceExpression.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.SideEffect;
+ return InstructionFlags.MayWriteLocals | resourceExpression.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.SideEffect;
}
public override InstructionFlags DirectFlags {
get {
- return InstructionFlags.ControlFlow | InstructionFlags.SideEffect;
+ return InstructionFlags.MayWriteLocals | InstructionFlags.ControlFlow | InstructionFlags.SideEffect;
}
}
public override void AcceptVisitor(ILVisitor visitor)
@@ -1797,7 +1837,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as UsingInstruction;
- return o != null && this.resourceExpression.PerformMatch(o.resourceExpression, ref match) && this.body.PerformMatch(o.body, ref match);
+ return o != null && variable == o.variable && this.resourceExpression.PerformMatch(o.resourceExpression, ref match) && this.body.PerformMatch(o.body, ref match);
}
}
}
@@ -5337,14 +5377,16 @@ namespace ICSharpCode.Decompiler.IL
body = default(ILInstruction);
return false;
}
- public bool MatchUsingInstruction(out ILInstruction resourceExpression, out ILInstruction body)
+ public bool MatchUsingInstruction(out ILVariable variable, out ILInstruction resourceExpression, out ILInstruction body)
{
var inst = this as UsingInstruction;
if (inst != null) {
+ variable = inst.Variable;
resourceExpression = inst.ResourceExpression;
body = inst.Body;
return true;
}
+ variable = default(ILVariable);
resourceExpression = default(ILInstruction);
body = default(ILInstruction);
return false;
diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt
index 0e77359bf..2b28a9efe 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.tt
+++ b/ICSharpCode.Decompiler/IL/Instructions.tt
@@ -119,7 +119,7 @@
new ArgumentInfo("onExpression"),
new ChildInfo("body")
}), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")),
- new OpCode("using", "Using statement", CustomClassName("UsingInstruction"),
+ new OpCode("using", "Using statement", CustomClassName("UsingInstruction"), HasVariableOperand("Store"),
CustomChildren(new [] {
new ArgumentInfo("resourceExpression"),
new ChildInfo("body")
diff --git a/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs
index a955e52ae..f0c1b1ebc 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs
@@ -39,11 +39,28 @@ namespace ICSharpCode.Decompiler.IL
}
}
+ ///
+ /// IL using instruction.
+ /// Equivalent to:
+ ///
+ /// stloc v(resourceExpression)
+ /// try {
+ /// body
+ /// } finally {
+ /// v?.Dispose();
+ /// }
+ ///
+ ///
+ ///
+ /// The value of v is undefined after the end of the body block.
+ ///
partial class UsingInstruction
{
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
output.Write("using (");
+ Variable.WriteTo(output);
+ output.Write(" = ");
ResourceExpression.WriteTo(output, options);
output.WriteLine(") {");
output.Indent();
diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
index 1f1b06390..253070622 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
@@ -58,10 +58,7 @@ namespace ICSharpCode.Decompiler.IL
///
public bool MatchLdLocRef(ILVariable variable)
{
- if (variable.Type.IsReferenceType == true)
- return MatchLdLoc(variable);
- else
- return MatchLdLoca(variable);
+ return MatchLdLocRef(out var v) && v == variable;
}
///
@@ -75,7 +72,7 @@ namespace ICSharpCode.Decompiler.IL
return variable.Type.IsReferenceType == true;
case LdLoca ldloca:
variable = ldloca.Variable;
- return variable.Type.IsReferenceType != true;
+ return variable.Type.IsReferenceType != true || variable.Type.Kind == TypeKind.TypeParameter;
default:
variable = null;
return false;
diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
index 78680b35e..d038b8639 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
@@ -79,7 +79,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
AddExistingName(reservedVariableNames, v.Name);
break;
default:
- if (v.HasGeneratedName || !IsValidName(v.Name)) {
+ if (v.HasGeneratedName || !IsValidName(v.Name) || ConflictWithLocal(v)) {
// don't use the name from the debug symbols if it looks like a generated name
v.Name = null;
} else {
@@ -104,6 +104,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
+ bool ConflictWithLocal(ILVariable v)
+ {
+ if (v.Kind == VariableKind.UsingLocal || v.Kind == VariableKind.ForeachLocal) {
+ if (reservedVariableNames.ContainsKey(v.Name))
+ return true;
+ }
+ return false;
+ }
+
static bool IsValidName(string varName)
{
if (string.IsNullOrEmpty(varName))
@@ -263,7 +272,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
}
- string GetNameByType(IType type)
+ static string GetNameByType(IType type)
{
var git = type as ParameterizedType;
if (git != null && git.FullName == "System.Nullable`1" && git.TypeArguments.Count == 1) {
@@ -275,6 +284,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
name = "array";
} else if (type is PointerType) {
name = "ptr";
+ } else if (type.Kind == TypeKind.TypeParameter) {
+ name = "val";
} else if (type.IsAnonymousType()) {
name = "anon";
} else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {
@@ -376,6 +387,49 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
+ internal static string GenerateVariableName(ILFunction function, IType type, ILVariable existingVariable = null)
+ {
+ if (function == null)
+ throw new ArgumentNullException(nameof(function));
+ var reservedVariableNames = new Dictionary();
+ foreach (var v in function.Descendants.OfType().SelectMany(m => m.Variables)) {
+ if (v != existingVariable)
+ AddExistingName(reservedVariableNames, v.Name);
+ }
+ foreach (var f in function.Method.DeclaringType.Fields.Select(f => f.Name))
+ AddExistingName(reservedVariableNames, f);
+
+ string baseName = GetNameByType(type);
+ string proposedName = "obj";
+
+ if (!string.IsNullOrEmpty(baseName)) {
+ if (!IsPlural(baseName, ref proposedName)) {
+ if (baseName.Length > 4 && baseName.EndsWith("List", StringComparison.Ordinal)) {
+ proposedName = baseName.Substring(0, baseName.Length - 4);
+ } else if (baseName.Equals("list", StringComparison.OrdinalIgnoreCase)) {
+ proposedName = "item";
+ } else if (baseName.EndsWith("children", StringComparison.OrdinalIgnoreCase)) {
+ proposedName = baseName.Remove(baseName.Length - 3);
+ } else {
+ proposedName = baseName;
+ }
+ }
+ }
+
+ // remove any numbers from the proposed name
+ proposedName = SplitName(proposedName, out int number);
+
+ if (!reservedVariableNames.ContainsKey(proposedName)) {
+ reservedVariableNames.Add(proposedName, 0);
+ }
+ int count = ++reservedVariableNames[proposedName];
+ if (count > 1) {
+ return proposedName + count.ToString();
+ } else {
+ return proposedName;
+ }
+ }
+
private static bool IsPlural(string baseName, ref string proposedName)
{
if (baseName.EndsWith("ies", StringComparison.OrdinalIgnoreCase) && baseName.Length > 3) {
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
index 57ad20b20..31bd540dc 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
@@ -131,13 +131,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return;
}
}
+
+ if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out arg, out var type) && type.Kind == TypeKind.TypeParameter) {
+ if (inst.Kind == ComparisonKind.Equality) {
+ context.Step("comp(box T(..) == ldnull) -> comp(.. == ldnull)", inst);
+ inst.Left = arg;
+ }
+ if (inst.Kind == ComparisonKind.Inequality) {
+ context.Step("comp(box T(..) != ldnull) -> comp(.. != ldnull)", inst);
+ inst.Left = arg;
+ }
+ }
}
protected internal override void VisitConv(Conv inst)
{
inst.Argument.AcceptVisitor(this);
- ILInstruction array;
- if (inst.Argument.MatchLdLen(StackType.I, out array) && inst.TargetType.IsIntegerType() && !inst.CheckForOverflow) {
+ if (inst.Argument.MatchLdLen(StackType.I, out ILInstruction array) && inst.TargetType.IsIntegerType() && !inst.CheckForOverflow) {
context.Step("conv.i4(ldlen array) => ldlen.i4(array)", inst);
inst.AddILRange(inst.Argument.ILRange);
inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array) { ILRange = inst.ILRange });
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
index 40278bb46..2a5cf6236 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
@@ -164,7 +164,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInstruction loadInst;
if (FindLoadInNext(next, v, inlinedExpression, out loadInst) == true) {
if (loadInst.OpCode == OpCode.LdLoca) {
- if (!IsGeneratedValueTypeTemporary(next, loadInst.Parent, loadInst.ChildIndex, v, inlinedExpression))
+ if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression))
return false;
} else {
Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
@@ -187,36 +187,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
return false;
}
-
+
///
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
///
/// The next top-level expression
- /// The direct parent of the load within 'next'
- /// Index of the load within 'parent'
+ /// The load instruction (a descendant within 'next')
/// The variable being inlined.
- static bool IsGeneratedValueTypeTemporary(ILInstruction next, ILInstruction parent, int pos, ILVariable v, ILInstruction inlinedExpression)
+ static bool IsGeneratedValueTypeTemporary(ILInstruction next, LdLoca loadInst, ILVariable v, ILInstruction inlinedExpression)
+ {
+ Debug.Assert(loadInst.Variable == v);
+ // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
+ // that the method is operating on a copy.
+ // Thus, we have to ensure we're operating on an r-value.
+ // Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
+ // of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
+ return IsUsedAsThisPointerInCall(loadInst) && !IsLValue(inlinedExpression);
+ }
+
+ internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca)
{
- if (pos == 0 && v.Type != null && v.Type.IsReferenceType == false) {
- // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
- // that the method is operating on a copy.
- // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers
- if (IsLValue(inlinedExpression)) {
+ if (ldloca.ChildIndex != 0)
+ return false;
+ if (ldloca.Variable.Type.IsReferenceType != false)
+ return false;
+ switch (ldloca.Parent.OpCode) {
+ case OpCode.Call:
+ case OpCode.CallVirt:
+ return !((CallInstruction)ldloca.Parent).Method.IsStatic;
+ case OpCode.Await:
+ return true;
+ default:
return false;
- }
-
- // inline the compiler-generated variable that are used when accessing a member on a value type:
- switch (parent.OpCode) {
- case OpCode.Call:
- return !((Call)parent).Method.IsStatic;
- case OpCode.CallVirt:
- return !((CallVirt)parent).Method.IsStatic;
- case OpCode.LdFlda:
- case OpCode.Await:
- return true;
- }
}
- return false;
}
///
diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
index 1fbf2c94d..9e703791c 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
@@ -143,6 +143,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue != b.HasValue) : true
// => a != b
return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst);
+ } else if (IsGenericNewPattern(condition, trueInst, falseInst)) {
+ // (default(T) == null) ? Activator.CreateInstance() : default(T)
+ // => Activator.CreateInstance()
+ return trueInst;
}
} else {
// Not (in)equality, but one of < <= > >=.
@@ -233,6 +237,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
}
+ private bool IsGenericNewPattern(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst)
+ {
+ // (default(T) == null) ? Activator.CreateInstance() : default(T)
+ return falseInst.MatchDefaultValue(out var type) &&
+ (trueInst is Call c && c.Method.FullName == "System.Activator.CreateInstance" && c.Method.TypeArguments.Count == 1) &&
+ type.Kind == TypeKind.TypeParameter &&
+ condition.MatchCompEquals(out var left, out var right) &&
+ left.MatchDefaultValue(out var type2) &&
+ type.Equals(type2) &&
+ right.MatchLdNull();
+ }
+
private bool MatchThreeValuedLogicConditionPattern(ILInstruction condition, out ILVariable nullable1, out ILVariable nullable2)
{
// Try to match: nullable1.GetValueOrDefault() || (!nullable2.GetValueOrDefault() && !nullable1.HasValue)
@@ -574,7 +590,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// Matches 'call get_HasValue(ldloca v)'
///
- static bool MatchHasValueCall(ILInstruction inst, out ILVariable v)
+ internal static bool MatchHasValueCall(ILInstruction inst, out ILVariable v)
{
v = null;
if (!(inst is Call call))
@@ -591,7 +607,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// Matches 'call get_HasValue(ldloca v)'
///
- static bool MatchHasValueCall(ILInstruction inst, ILVariable v)
+ internal static bool MatchHasValueCall(ILInstruction inst, ILVariable v)
{
return MatchHasValueCall(inst, out var v2) && v == v2;
}
@@ -641,7 +657,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// Matches 'call Nullable{T}.GetValueOrDefault(ldloca v)'
///
- static bool MatchGetValueOrDefault(ILInstruction inst, out ILVariable v)
+ internal static bool MatchGetValueOrDefault(ILInstruction inst, out ILVariable v)
{
v = null;
return MatchGetValueOrDefault(inst, out ILInstruction arg)
@@ -651,7 +667,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// Matches 'call Nullable{T}.GetValueOrDefault(ldloca v)'
///
- static bool MatchGetValueOrDefault(ILInstruction inst, ILVariable v)
+ internal static bool MatchGetValueOrDefault(ILInstruction inst, ILVariable v)
{
return MatchGetValueOrDefault(inst, out ILVariable v2) && v == v2;
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
index 2f932184f..a39916d1b 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
@@ -72,30 +72,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool TransformUsing(Block block, int i)
{
if (i < 1) return false;
- if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 1] is StLoc storeInst))
+ if (!(block.Instructions[i] is TryFinally tryFinally) || !(block.Instructions[i - 1] is StLoc storeInst))
return false;
- if (!(body.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull()))
+ if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type)))
return false;
- ILInstruction resourceExpression;
- if (storeInst.Variable.Type.IsReferenceType != false) {
- if (storeInst.Variable.IsSingleDefinition && storeInst.Variable.LoadCount <= 2) {
- resourceExpression = storeInst.Value;
- } else {
- resourceExpression = storeInst;
- }
- } else {
- if (storeInst.Variable.StoreCount == 1 && storeInst.Variable.LoadCount == 0 && storeInst.Variable.AddressCount == 1) {
- resourceExpression = storeInst.Value;
- } else {
- resourceExpression = storeInst;
- }
- }
- context.Step("UsingTransform", body);
+ if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally)))
+ return false;
+ if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la))))
+ return false;
+ if (storeInst.Variable.StoreInstructions.OfType().Any(st => st != storeInst))
+ return false;
+ if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull()))
+ return false;
+ context.Step("UsingTransform", tryFinally);
+ storeInst.Variable.Kind = VariableKind.UsingLocal;
block.Instructions.RemoveAt(i);
- block.Instructions[i - 1] = new UsingInstruction(resourceExpression, body.TryBlock);
+ block.Instructions[i - 1] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock);
return true;
}
+ bool CheckResourceType(IType type)
+ {
+ // non-generic IEnumerator does not implement IDisposable.
+ // This is a workaround for non-generic foreach.
+ if (type.IsKnownType(KnownTypeCode.IEnumerator))
+ return true;
+ return NullableType.GetUnderlyingType(type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable));
+ }
+
bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull)
{
var entryPoint = container.EntryPoint;
@@ -118,32 +122,66 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!entryPoint.Instructions[leaveIndex].MatchLeave(container, out var returnValue) || !returnValue.MatchNop())
return false;
CallVirt callVirt;
- // reference types have a null check.
- if (isReference) {
+ if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) {
if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst))
return false;
- if (!condition.MatchCompNotEquals(out var left, out var right) || !left.MatchLdLoc(objVar) || !right.MatchLdNull())
+ if (!(NullableLiftingTransform.MatchHasValueCall(condition, out var v) && v == objVar))
return false;
if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1)
return false;
if (!(disposeBlock.Instructions[0] is CallVirt cv))
return false;
callVirt = cv;
- } else {
- if (!(entryPoint.Instructions[checkIndex] is CallVirt cv))
+ if (callVirt.Method.FullName != "System.IDisposable.Dispose")
return false;
- callVirt = cv;
- }
- if (callVirt.Method.Name != "Dispose" || callVirt.Method.DeclaringType.FullName != "System.IDisposable")
- return false;
- if (callVirt.Method.Parameters.Count > 0)
- return false;
- if (callVirt.Arguments.Count != 1)
- return false;
- if (objVar.Type.IsReferenceType != false) {
- return callVirt.Arguments[0].MatchLdLoc(objVar) || (usingNull && callVirt.Arguments[0].MatchLdNull());
+ if (callVirt.Method.Parameters.Count > 0)
+ return false;
+ if (callVirt.Arguments.Count != 1)
+ return false;
+ var firstArg = cv.Arguments.FirstOrDefault();
+ if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(KnownTypeCode.IDisposable))) {
+ if (!firstArg.MatchAddressOf(out var innerArg2))
+ return false;
+ return NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar);
+ } else {
+ if (!(innerArg1.MatchBox(out firstArg, out var boxType) && boxType.IsKnownType(KnownTypeCode.NullableOfT) &&
+ NullableType.GetUnderlyingType(boxType).Equals(NullableType.GetUnderlyingType(objVar.Type))))
+ return false;
+ return firstArg.MatchLdLoc(objVar);
+ }
} else {
- return callVirt.Arguments[0].MatchLdLoca(objVar);
+ ILInstruction target;
+ if (isReference) {
+ // reference types have a null check.
+ if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst))
+ return false;
+ if (!condition.MatchCompNotEquals(out var left, out var right) || !left.MatchLdLoc(objVar) || !right.MatchLdNull())
+ return false;
+ if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1)
+ return false;
+ if (!(disposeBlock.Instructions[0] is CallVirt cv))
+ return false;
+ target = cv.Arguments.FirstOrDefault();
+ if (target == null)
+ return false;
+ if (target.MatchBox(out var newTarget, out var type) && type.Equals(objVar.Type))
+ target = newTarget;
+ callVirt = cv;
+ } else {
+ if (!(entryPoint.Instructions[checkIndex] is CallVirt cv))
+ return false;
+ target = cv.Arguments.FirstOrDefault();
+ if (target == null)
+ return false;
+ callVirt = cv;
+ }
+ if (callVirt.Method.FullName != "System.IDisposable.Dispose")
+ return false;
+ if (callVirt.Method.Parameters.Count > 0)
+ return false;
+ if (callVirt.Arguments.Count != 1)
+ return false;
+ return target.MatchLdLocRef(objVar) || (usingNull && callVirt.Arguments[0].MatchLdNull());
}
}
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs
index c28488018..505d08f9a 100644
--- a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs
@@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
if (type == null)
throw new ArgumentNullException("type");
ParameterizedType pt = type as ParameterizedType;
- if (pt != null && pt.TypeParameterCount == 1 && pt.FullName == "System.Nullable")
+ if (pt != null && pt.TypeParameterCount == 1 && pt.GetDefinition().KnownTypeCode == KnownTypeCode.NullableOfT)
return pt.GetTypeArgument(0);
else
return type;
diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs
index 7f6324118..d3da19b36 100644
--- a/ILSpy/Languages/ILAstLanguage.cs
+++ b/ILSpy/Languages/ILAstLanguage.cs
@@ -157,7 +157,8 @@ namespace ICSharpCode.ILSpy
if (!method.HasBody)
return;
var typeSystem = new DecompilerTypeSystem(method.Module);
- ILReader reader = new ILReader(typeSystem);
+ var specializingTypeSystem = typeSystem.GetSpecializingTypeSystem(new SimpleTypeResolveContext(typeSystem.Resolve(method)));
+ var reader = new ILReader(specializingTypeSystem);
reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols;
ILFunction il = reader.ReadIL(method.Body, options.CancellationToken);
ILTransformContext context = new ILTransformContext {