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 {