diff --git a/DecompilerNuGetDemos.workbook b/DecompilerNuGetDemos.workbook index 49df888ea..a381ee75b 100644 --- a/DecompilerNuGetDemos.workbook +++ b/DecompilerNuGetDemos.workbook @@ -6,7 +6,7 @@ platforms: - DotNetCore packages: - id: ICSharpCode.Decompiler - version: 3.0.0.3246-beta1 + version: 3.0.0.3287-beta2 --- Setup: load the references required to work with the decompiler diff --git a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj index eb6e687e9..29b6d2dbc 100644 --- a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj +++ b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj @@ -9,7 +9,7 @@ - + diff --git a/ICSharpCode.Decompiler.PowerShell/Demo.ps1 b/ICSharpCode.Decompiler.PowerShell/Demo.ps1 index cd7482c52..36674d23c 100644 --- a/ICSharpCode.Decompiler.PowerShell/Demo.ps1 +++ b/ICSharpCode.Decompiler.PowerShell/Demo.ps1 @@ -1,5 +1,13 @@ -Import-Module .\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.PSCore.dll -$decompiler = Get-Decompiler .\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.PSCore.dll +$basePath = $PSScriptRoot +if ([string]::IsNullOrEmpty($basePath)) +{ + $basePath = Split-Path -parent $psISE.CurrentFile.Fullpath +} + +$modulePath = $basePath + '\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.Powershell.dll' + +Import-Module $modulePath +$decompiler = Get-Decompiler $modulePath $classes = Get-DecompiledTypes $decompiler -Types class $classes.Count @@ -10,6 +18,6 @@ foreach ($c in $classes) } -Get-DecompiledSource $decompiler -TypeName ICSharpCode.Decompiler.PSCore.GetDecompilerCmdlet +Get-DecompiledSource $decompiler -TypeName ICSharpCode.Decompiler.PowerShell.GetDecompilerCmdlet Get-DecompiledProject $decompiler -OutputPath .\decomptest \ No newline at end of file diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj index 673d1ecc4..4e3dce1bf 100644 --- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj +++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj @@ -8,7 +8,7 @@ - + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs index adc0c6660..96a3ab4e2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs @@ -82,6 +82,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Console.WriteLine(FirstOrDefault(new List { 1, 2, 3, 4, 5 })); Console.WriteLine(NoForeachDueToMultipleCurrentAccess(new List { 1, 2, 3, 4, 5 })); Console.WriteLine(NoForeachCallWithSideEffect(new CustomClassEnumeratorWithIDisposable())); + LoopWithGotoRepeat(); } public static void ForWithMultipleVariables() @@ -222,5 +223,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness return CallWithSideEffect(); } } + + static bool GetBool(string text) + { + return false; + } + + // https://github.com/icsharpcode/ILSpy/issues/915 + static void LoopWithGotoRepeat() + { + Console.WriteLine("LoopWithGotoRepeat:"); + try { + REPEAT: + Console.WriteLine("after repeat label"); + while (GetBool("Loop condition")) { + if (GetBool("if1")) { + if (GetBool("if3")) { + goto REPEAT; + } + break; + } + } + Console.WriteLine("after loop"); + } finally { + Console.WriteLine("finally"); + } + Console.WriteLine("after finally"); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il index 338a9cbce..2c8e3383c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il @@ -21,7 +21,7 @@ extends [mscorlib]System.Object .entrypoint call void Program::InlineAssignByte() - //call void Program::Int32OrNativeTests() + call void Program::Int32OrNativeTests() ret } // end of method Main @@ -48,7 +48,6 @@ pointless: ret } - /* .method public static void Int32OrNativeTests() { ldstr "Int32OrNative(0x7fffffff, false) = {0}" @@ -79,6 +78,7 @@ pointless: box native int call void [mscorlib]System.Console::WriteLine(string, object) + /* ldstr "Int32OrNativeLoopStyle(0x7fffffff):" call void [mscorlib]System.Console::WriteLine(string) ldc.i4 0x7fffffff @@ -100,6 +100,7 @@ pointless: call native int Program::Int32OrNativeDeadCode(int32) box native int call void [mscorlib]System.Console::WriteLine(string, object) + */ ldc.i4 0x7fffffff call void Program::RunInt32OrNativeMultiUse(int32) @@ -115,6 +116,26 @@ pointless: use_i4: ldarg.0 br after_if + use_native_int: + ldarg.0 + conv.u + br after_if + after_if: + ldc.i4.1 + add + ret + } + + /* + .method public static native int Int32OrNativeReordered(int32 val, bool use_native) + { + // The spec is ambiguous whether the addition will be in 32-bits or native size. + // The microsoft runtime picks native size. + ldarg.1 + brtrue use_native_int + use_i4: + ldarg.0 + br after_if after_if: ldc.i4.1 add @@ -124,9 +145,11 @@ pointless: conv.u br after_if } - + .method public static void Int32OrNativeLoopStyle(int32 val) { + // The spec is ambiguous whether the addition will be in 32-bits or native size. + // The microsoft runtime picks native size. .locals init ( int32 i ) @@ -149,6 +172,8 @@ pointless: .method public static native int Int32OrNativeDeadCode(int32 val) { + // The spec is ambiguous whether the addition will be in 32-bits or native size. + // The microsoft runtime picks 32-bits. use_i4: ldarg.0 br after_if @@ -161,6 +186,7 @@ pointless: conv.u br after_if } + */ .method public static void RunInt32OrNativeMultiUse(int32 val) { @@ -229,8 +255,7 @@ pointless: add ret } - */ - + .method public static void Print(native int val) { ldarg.0 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs index 480228762..9e4eef012 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -327,6 +327,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { SimpleStruct* ptr = stackalloc SimpleStruct[checked(count * 2)]; SimpleStruct* ptr2 = stackalloc SimpleStruct[10]; + ptr->X = count; + ptr[1].X = ptr->X; + for (int i = 2; i < 10; i++) { + ptr[i].X = count; + } return this.UsePointer(&ptr->Y); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il index e4d7a0692..65de1dfd2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly veti52ie +.assembly '5mgf1rnb' { .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 veti52ie.dll -// MVID: {F873CF77-738D-4A61-B0C6-96B9F5621119} +.module '5mgf1rnb.dll' +// MVID: {C39B0AE8-69C3-4207-B912-FCEE701C71E8} .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: 0x028F0000 +// Image base: 0x00C60000 // =============== CLASS MEMBERS DECLARATION =================== @@ -1238,11 +1238,13 @@ .method public hidebysig instance string StackAllocStruct(int32 count) cil managed { - // Code size 46 (0x2e) - .maxstack 2 + // Code size 110 (0x6e) + .maxstack 3 .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0, valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_1, - string V_2) + int32 V_2, + string V_3, + bool V_4) IL_0000: nop IL_0001: ldarg.1 IL_0002: ldc.i4.2 @@ -1258,16 +1260,50 @@ IL_0018: mul.ovf.un IL_0019: localloc IL_001b: stloc.1 - IL_001c: ldarg.0 - IL_001d: ldloc.0 - IL_001e: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y - IL_0023: conv.u - IL_0024: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) - IL_0029: stloc.2 - IL_002a: br.s IL_002c - - IL_002c: ldloc.2 - IL_002d: ret + IL_001c: ldloc.0 + IL_001d: ldarg.1 + IL_001e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0023: ldloc.0 + IL_0024: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_002a: add + IL_002b: ldloc.0 + IL_002c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0031: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0036: ldc.i4.2 + IL_0037: stloc.2 + IL_0038: br.s IL_0051 + + IL_003a: nop + IL_003b: ldloc.0 + IL_003c: ldloc.2 + IL_003d: conv.i + IL_003e: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_0044: mul + IL_0045: add + IL_0046: ldarg.1 + IL_0047: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_004c: nop + IL_004d: ldloc.2 + IL_004e: ldc.i4.1 + IL_004f: add + IL_0050: stloc.2 + IL_0051: ldloc.2 + IL_0052: ldc.i4.s 10 + IL_0054: clt + IL_0056: stloc.s V_4 + IL_0058: ldloc.s V_4 + IL_005a: brtrue.s IL_003a + + IL_005c: ldarg.0 + IL_005d: ldloc.0 + IL_005e: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y + IL_0063: conv.u + IL_0064: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) + IL_0069: stloc.3 + IL_006a: br.s IL_006c + + IL_006c: ldloc.3 + IL_006d: ret } // end of method UnsafeCode::StackAllocStruct .method family hidebysig virtual instance void diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il index 96247420b..1a0b0ea04 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly '24yl5hoc' +.assembly av3nix0s { .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 '24yl5hoc.dll' -// MVID: {73FF4D49-1CDC-4675-A00D-0D382845C323} +.module av3nix0s.dll +// MVID: {3E5B8427-2816-45EA-9E7A-139AA9452535} .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: 0x013A0000 +// Image base: 0x025C0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -986,9 +986,10 @@ .method public hidebysig instance string StackAllocStruct(int32 count) cil managed { - // Code size 41 (0x29) - .maxstack 2 - .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0) + // Code size 97 (0x61) + .maxstack 3 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0, + int32 V_1) IL_0000: ldarg.1 IL_0001: ldc.i4.2 IL_0002: mul.ovf @@ -1003,12 +1004,41 @@ IL_0017: mul.ovf.un IL_0018: localloc IL_001a: pop - IL_001b: ldarg.0 - IL_001c: ldloc.0 - IL_001d: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y - IL_0022: conv.u - IL_0023: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) - IL_0028: ret + IL_001b: ldloc.0 + IL_001c: ldarg.1 + IL_001d: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0022: ldloc.0 + IL_0023: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_0029: add + IL_002a: ldloc.0 + IL_002b: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0030: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0035: ldc.i4.2 + IL_0036: stloc.1 + IL_0037: br.s IL_004e + + IL_0039: ldloc.0 + IL_003a: ldloc.1 + IL_003b: conv.i + IL_003c: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_0042: mul + IL_0043: add + IL_0044: ldarg.1 + IL_0045: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_004a: ldloc.1 + IL_004b: ldc.i4.1 + IL_004c: add + IL_004d: stloc.1 + IL_004e: ldloc.1 + IL_004f: ldc.i4.s 10 + IL_0051: blt.s IL_0039 + + IL_0053: ldarg.0 + IL_0054: ldloc.0 + IL_0055: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y + IL_005a: conv.u + IL_005b: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) + IL_0060: ret } // end of method UnsafeCode::StackAllocStruct .method family hidebysig virtual instance void diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il index a4831768c..19c2a8b81 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module UnsafeCode.dll -// MVID: {DD1630B5-221C-4D3A-8171-7BFFD65CA0F2} +// MVID: {6E613246-08C3-4B77-B500-576D8782513A} .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: 0x01320000 +// Image base: 0x030D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -991,9 +991,10 @@ .method public hidebysig instance string StackAllocStruct(int32 count) cil managed { - // Code size 41 (0x29) - .maxstack 2 - .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0) + // Code size 97 (0x61) + .maxstack 3 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0, + int32 V_1) IL_0000: ldarg.1 IL_0001: ldc.i4.2 IL_0002: mul.ovf @@ -1008,12 +1009,41 @@ IL_0017: mul.ovf.un IL_0018: localloc IL_001a: pop - IL_001b: ldarg.0 - IL_001c: ldloc.0 - IL_001d: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y - IL_0022: conv.u - IL_0023: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) - IL_0028: ret + IL_001b: ldloc.0 + IL_001c: ldarg.1 + IL_001d: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0022: ldloc.0 + IL_0023: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_0029: add + IL_002a: ldloc.0 + IL_002b: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0030: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0035: ldc.i4.2 + IL_0036: stloc.1 + IL_0037: br.s IL_004e + + IL_0039: ldloc.0 + IL_003a: ldloc.1 + IL_003b: conv.i + IL_003c: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_0042: mul + IL_0043: add + IL_0044: ldarg.1 + IL_0045: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_004a: ldloc.1 + IL_004b: ldc.i4.1 + IL_004c: add + IL_004d: stloc.1 + IL_004e: ldloc.1 + IL_004f: ldc.i4.s 10 + IL_0051: blt.s IL_0039 + + IL_0053: ldarg.0 + IL_0054: ldloc.0 + IL_0055: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y + IL_005a: conv.u + IL_005b: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) + IL_0060: ret } // end of method UnsafeCode::StackAllocStruct .method family hidebysig virtual instance void diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il index 8cee9896b..d1f786100 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module UnsafeCode.dll -// MVID: {34A4C476-B60D-4D44-9CF4-288026720E1B} +// MVID: {E02FB293-0E44-4F82-B549-8BAB6E8A9750} .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: 0x00AC0000 +// Image base: 0x006B0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -1242,11 +1242,13 @@ .method public hidebysig instance string StackAllocStruct(int32 count) cil managed { - // Code size 46 (0x2e) - .maxstack 2 + // Code size 110 (0x6e) + .maxstack 3 .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0, valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_1, - string V_2) + int32 V_2, + bool V_3, + string V_4) IL_0000: nop IL_0001: ldarg.1 IL_0002: ldc.i4.2 @@ -1262,16 +1264,50 @@ IL_0018: mul.ovf.un IL_0019: localloc IL_001b: stloc.1 - IL_001c: ldarg.0 - IL_001d: ldloc.0 - IL_001e: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y - IL_0023: conv.u - IL_0024: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) - IL_0029: stloc.2 - IL_002a: br.s IL_002c - - IL_002c: ldloc.2 - IL_002d: ret + IL_001c: ldloc.0 + IL_001d: ldarg.1 + IL_001e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0023: ldloc.0 + IL_0024: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_002a: add + IL_002b: ldloc.0 + IL_002c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0031: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_0036: ldc.i4.2 + IL_0037: stloc.2 + IL_0038: br.s IL_0051 + + IL_003a: nop + IL_003b: ldloc.0 + IL_003c: ldloc.2 + IL_003d: conv.i + IL_003e: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct + IL_0044: mul + IL_0045: add + IL_0046: ldarg.1 + IL_0047: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X + IL_004c: nop + IL_004d: ldloc.2 + IL_004e: ldc.i4.1 + IL_004f: add + IL_0050: stloc.2 + IL_0051: ldloc.2 + IL_0052: ldc.i4.s 10 + IL_0054: clt + IL_0056: stloc.3 + IL_0057: ldloc.3 + IL_0058: brtrue.s IL_003a + + IL_005a: ldarg.0 + IL_005b: ldloc.0 + IL_005c: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y + IL_0061: conv.u + IL_0062: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) + IL_0067: stloc.s V_4 + IL_0069: br.s IL_006b + + IL_006b: ldloc.s V_4 + IL_006d: ret } // end of method UnsafeCode::StackAllocStruct .method family hidebysig virtual instance void diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 73f564d5c..da07fc688 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -810,6 +810,10 @@ namespace ICSharpCode.Decompiler.CSharp } return Translate(countOffsetInst); } + } else if (byteOffsetInst.UnwrapConv(ConversionKind.SignExtend) is SizeOf sizeOf && sizeOf.Type.Equals(pointerType.ElementType)) { + return new PrimitiveExpression(1) + .WithILInstruction(byteOffsetInst) + .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1)); } else if (byteOffsetInst.MatchLdcI(out long val)) { // If the offset is a constant, it's possible that the compiler // constant-folded the multiplication. @@ -1540,9 +1544,14 @@ namespace ICSharpCode.Decompiler.CSharp if (!TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) { target = target.ConvertTo(new PointerType(inst.Type), this); } - result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) - .WithoutILInstruction() - .WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType)); + if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { + // *&ptr -> ptr + result = target.UnwrapChild(uoe.Expression); + } else { + result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) + .WithoutILInstruction() + .WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType)); + } } var value = Translate(inst.Value, typeHint: result.Type); return Assignment(result, value).WithILInstruction(inst); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs index 10d16c823..f5fa6f30e 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs @@ -104,6 +104,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms pre.MemberName = memberReferenceExpression.MemberName; memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments); pre.CopyAnnotationsFrom(uoe); + pre.RemoveAnnotations(); // only copy the ResolveResult from the MRE pre.CopyAnnotationsFrom(memberReferenceExpression); memberReferenceExpression.ReplaceWith(pre); } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template index c4c9f57bd..7a473cdd3 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template @@ -17,8 +17,8 @@ - - + + diff --git a/ICSharpCode.Decompiler/IL/BlockBuilder.cs b/ICSharpCode.Decompiler/IL/BlockBuilder.cs index 9e23a194a..ff2b80818 100644 --- a/ICSharpCode.Decompiler/IL/BlockBuilder.cs +++ b/ICSharpCode.Decompiler/IL/BlockBuilder.cs @@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.IL foreach (var inst in instructions) { cancellationToken.ThrowIfCancellationRequested(); int start = inst.ILRange.Start; - if (currentBlock == null || incomingBranches[start]) { + if (currentBlock == null || (incomingBranches[start] && !IsStackAdjustment(inst))) { // Finish up the previous block FinalizeCurrentBlock(start, fallthrough: true); // Leave nested containers if necessary @@ -158,6 +158,11 @@ namespace ICSharpCode.Decompiler.IL ConnectBranches(mainContainer, cancellationToken); } + static bool IsStackAdjustment(ILInstruction inst) + { + return inst is StLoc stloc && stloc.IsStackAdjustment; + } + private void FinalizeCurrentBlock(int currentILOffset, bool fallthrough) { if (currentBlock == null) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index a6b9db9ba..87719cb20 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -93,6 +93,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow var headBlock = (Block)h.UserData; context.Step($"Construct loop with head {headBlock.Label}", headBlock); // loop now is the union of all natural loops with loop head h. + + // Ensure any block included into nested loops is also considered part of this loop: + IncludeNestedContainers(loop); // Try to extend the loop to reduce the number of exit points: ExtendLoop(h, loop, out var exitPoint); @@ -109,24 +112,46 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } /// - /// Recurse into the dominator tree and find back edges/natural loops. + /// For each block in the input loop that is the head of a nested loop or switch, + /// include all blocks from the nested container into the loop. + /// + /// This ensures that all blocks that were included into inner loops are also + /// included into the outer loop, thus keeping our loops well-nested. /// /// + /// More details for why this is necessary are here: + /// https://github.com/icsharpcode/ILSpy/issues/915 /// - /// Preconditions: - /// * dominance was computed for h - /// * all blocks in the dominator subtree starting at h are in the same BlockContainer - /// * the visited flag is set to false + /// Pre+Post-Condition: node.Visited iff loop.Contains(node) /// - void FindLoops(ControlFlowNode h) + void IncludeNestedContainers(List loop) { - // Recurse into the dominator tree to find other possible loop heads - foreach (var child in h.DominatorTreeChildren) { - FindLoops(child); + for (int i = 0; i < loop.Count; i++) { + IncludeBlock((Block)loop[i].UserData); } + void IncludeBlock(Block block) + { + if (block.Instructions[0] is BlockContainer nestedContainer) { + // Just in case the block has multiple nested containers (e.g. due to loop and switch), + // also check the entry point: + IncludeBlock(nestedContainer.EntryPoint); + // Use normal processing for all non-entry-point blocks + // (the entry-point itself doesn't have a CFG node, because it's newly created by this transform) + for (int i = 1; i < nestedContainer.Blocks.Count; i++) { + var node = context.ControlFlowGraph.GetNode(nestedContainer.Blocks[i]); + Debug.Assert(loop[0].Dominates(node)); + if (!node.Visited) { + node.Visited = true; + loop.Add(node); + // note: this block will be re-visited when the "i < loop.Count" + // gets around to the new entry + } + } + } + } } - + #region ExtendLoop /// /// Given a natural loop, add additional CFG nodes to the loop in order diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 7d13b4e29..1fb82890d 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.IL Dictionary> stackByOffset; Dictionary variableByExceptionHandler; UnionFind unionFind; + List<(ILVariable, ILVariable)> stackMismatchPairs; IEnumerable stackVariables; void Init(Cil.MethodBody body) @@ -74,8 +75,9 @@ namespace ICSharpCode.Decompiler.IL this.debugInfo = body.Method.DebugInformation; this.currentInstruction = null; this.nextInstructionIndex = 0; - this.currentStack = System.Collections.Immutable.ImmutableStack.Empty; + this.currentStack = ImmutableStack.Empty; this.unionFind = new UnionFind(); + this.stackMismatchPairs = new List<(ILVariable, ILVariable)>(); this.methodReturnStackType = typeSystem.Resolve(body.Method.ReturnType).GetStackType(); InitParameterVariables(); this.localVariables = body.Variables.SelectArray(CreateILVariable); @@ -84,7 +86,7 @@ namespace ICSharpCode.Decompiler.IL v.HasInitialValue = true; } } - mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); + this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); this.instructionBuilder = new List(); this.isBranchTarget = new BitArray(body.CodeSize); this.stackByOffset = new Dictionary>(); @@ -186,35 +188,87 @@ namespace ICSharpCode.Decompiler.IL Warnings.Add(string.Format("IL_{0:x4}: {1}", currentInstruction.Offset, message)); } - void MergeStacks(ImmutableStack a, ImmutableStack b) + ImmutableStack MergeStacks(ImmutableStack a, ImmutableStack b) { - var enum1 = a.GetEnumerator(); - var enum2 = b.GetEnumerator(); - bool ok1 = enum1.MoveNext(); - bool ok2 = enum2.MoveNext(); - while (ok1 && ok2) { - if (enum1.Current.StackType != enum2.Current.StackType) { - Warn("Incompatible stack types: " + enum1.Current.StackType + " vs " + enum2.Current.StackType); + if (CheckStackCompatibleWithoutAdjustments(a, b)) { + // We only need to union the input variables, but can + // otherwise re-use the existing stack. + ImmutableStack output = a; + while (!a.IsEmpty && !b.IsEmpty) { + Debug.Assert(a.Peek().StackType == b.Peek().StackType); + unionFind.Merge(a.Peek(), b.Peek()); + a = a.Pop(); + b = b.Pop(); } - unionFind.Merge(enum1.Current, enum2.Current); - ok1 = enum1.MoveNext(); - ok2 = enum2.MoveNext(); - } - if (ok1 || ok2) { + return output; + } else if (a.Count() != b.Count()) { + // Let's not try to merge mismatched stacks. Warn("Incompatible stack heights: " + a.Count() + " vs " + b.Count()); + return a; + } else { + // The more complex case where the stacks don't match exactly. + var output = new List(); + while (!a.IsEmpty && !b.IsEmpty) { + var varA = a.Peek(); + var varB = b.Peek(); + if (varA.StackType == varB.StackType) { + unionFind.Merge(varA, varB); + output.Add(varA); + } else { + if (!IsValidTypeStackTypeMerge(varA.StackType, varB.StackType)) { + Warn("Incompatible stack types: " + varA.StackType + " vs " + varB.StackType); + } + if (varA.StackType > varB.StackType) { + output.Add(varA); + // every store to varB should also store to varA + stackMismatchPairs.Add((varB, varA)); + } else { + output.Add(varB); + // every store to varA should also store to varB + stackMismatchPairs.Add((varA, varB)); + } + } + a = a.Pop(); + b = b.Pop(); + } + // because we built up output by popping from the input stacks, we need to reverse it to get back the original order + output.Reverse(); + return ImmutableStack.CreateRange(output); } } + static bool CheckStackCompatibleWithoutAdjustments(ImmutableStack a, ImmutableStack b) + { + while (!a.IsEmpty && !b.IsEmpty) { + if (a.Peek().StackType != b.Peek().StackType) + return false; + a = a.Pop(); + b = b.Pop(); + } + return a.IsEmpty && b.IsEmpty; + } + + private bool IsValidTypeStackTypeMerge(StackType stackType1, StackType stackType2) + { + if (stackType1 == StackType.I && stackType2 == StackType.I4) + return true; + if (stackType1 == StackType.I4 && stackType2 == StackType.I) + return true; + // allow merging unknown type with any other type + return stackType1 == StackType.Unknown || stackType2 == StackType.Unknown; + } + void StoreStackForOffset(int offset, ImmutableStack stack) { - ImmutableStack existing; - if (stackByOffset.TryGetValue(offset, out existing)) { - MergeStacks(existing, stack); + if (stackByOffset.TryGetValue(offset, out var existing)) { + var newStack = MergeStacks(existing, stack); + if (newStack != existing) + stackByOffset[offset] = newStack; } else { stackByOffset.Add(offset, stack); } } - + void ReadInstructions(CancellationToken cancellationToken) { // Fill isBranchTarget and branchStackDict based on exception handlers @@ -272,6 +326,44 @@ namespace ICSharpCode.Decompiler.IL instructionBuilder[i] = instructionBuilder[i].AcceptVisitor(visitor); } stackVariables = visitor.variables; + InsertStackAdjustments(); + } + + void InsertStackAdjustments() + { + if (stackMismatchPairs.Count == 0) + return; + var dict = new MultiDictionary(); + foreach (var (origA, origB) in stackMismatchPairs) { + var a = unionFind.Find(origA); + var b = unionFind.Find(origB); + Debug.Assert(a.StackType < b.StackType); + // For every store to a, insert a converting store to b. + if (!dict[a].Contains(b)) + dict.Add(a, b); + } + var newInstructions = new List(); + foreach (var inst in instructionBuilder) { + newInstructions.Add(inst); + if (inst is StLoc store) { + foreach (var additionalVar in dict[store.Variable]) { + ILInstruction value = new LdLoc(store.Variable); + switch (additionalVar.StackType) { + case StackType.I: + value = new Conv(value, PrimitiveType.I, false, Sign.None); + break; + case StackType.I8: + value = new Conv(value, PrimitiveType.I8, false, Sign.None); + break; + } + newInstructions.Add(new StLoc(additionalVar, value) { + IsStackAdjustment = true, + ILRange = inst.ILRange + }); + } + } + } + instructionBuilder = newInstructions; } /// @@ -282,6 +374,12 @@ namespace ICSharpCode.Decompiler.IL Init(body); ReadInstructions(cancellationToken); foreach (var inst in instructionBuilder) { + if (inst is StLoc stloc && stloc.IsStackAdjustment) { + output.Write(" "); + inst.WriteTo(output, new ILAstWritingOptions()); + output.WriteLine(); + continue; + } output.Write(" ["); bool isFirstElement = true; foreach (var element in stackByOffset[inst.ILRange.Start]) { diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index d96dc841a..9b60134b7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -2317,6 +2317,8 @@ namespace ICSharpCode.Decompiler.IL clone.Value = this.value.Clone(); return clone; } + /// If true, this stloc represents a stack type adjustment. This field is only used in ILReader and BlockBuilder, and should be ignored by ILAst transforms. + internal bool IsStackAdjustment; public override StackType ResultType { get { return variable.StackType; } } protected override InstructionFlags ComputeFlags() { diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index d29ea9d68..1a28b8151 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -153,6 +153,9 @@ new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine + "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign())", CustomClassName("StLoc"), HasVariableOperand("Store"), CustomArguments("value"), + AdditionalMember( + "/// If true, this stloc represents a stack type adjustment. This field is only used in ILReader and BlockBuilder, and should be ignored by ILAst transforms." + Environment.NewLine + + "internal bool IsStackAdjustment;"), ResultType("variable.StackType")), new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")), @@ -571,6 +574,13 @@ namespace ICSharpCode.Decompiler.IL }; } + static Action AdditionalMember(string declaration) + { + return opCode => { + opCode.Members.Add(declaration); + }; + } + // ResultType trait: the instruction has the specified result type. static Action ResultType(string type) { diff --git a/ICSharpCode.Decompiler/IL/StackType.cs b/ICSharpCode.Decompiler/IL/StackType.cs index b2a4381a1..b5a5b4d60 100644 --- a/ICSharpCode.Decompiler/IL/StackType.cs +++ b/ICSharpCode.Decompiler/IL/StackType.cs @@ -24,6 +24,15 @@ namespace ICSharpCode.Decompiler.IL /// public enum StackType : byte { + // Note: the numeric of these enum members is relevant for ILReader.MergeStacks: + // when two branches meet where a stack slot has different types, the type after + // the branch is the one with the higher numeric value. + + /// + /// The stack type is unknown; for example a call returning an unknown type + /// because an assembly reference isn't loaded. + /// Can also occur with invalid IL. + /// Unknown, /// 32-bit integer /// @@ -33,23 +42,24 @@ namespace ICSharpCode.Decompiler.IL /// and any enums with one of the above as underlying type. /// I4, + /// native-size integer, or unmanaged pointer + /// + /// Used for C# IntPtr, UIntPtr and any native pointer types (void* etc.) + /// Also used for IL function pointer types. + /// + I, /// 64-bit integer /// /// Used for C# long, ulong, /// and any enums with one of the above as underlying type. /// I8, - /// native-size integer, or unmanaged pointer - /// - /// Used for C# IntPtr, UIntPtr and any native pointer types (void* etc.) - /// - I, /// Floating point number /// /// Used for C# float and double. /// F, - /// Another stack type. Includes objects, value types, function pointers, ... + /// Another stack type. Includes objects, value types, ... O, /// A managed pointer Ref, diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index 140434136..ea8c2ffc4 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -42,7 +42,7 @@ internal static class RevisionClass public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; - public const string VersionName = "beta1"; + public const string VersionName = "beta2"; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; } diff --git a/appveyor.yml b/appveyor.yml index ad8006791..3dab27be5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,6 +18,10 @@ test: - 'ILSpy.BamlDecompiler.Tests\bin\%configuration%\net461\ILSpy.BamlDecompiler.Tests.dll' after_test: - python BuildTools\tidy.py +for: +- branches: + only: + - master artifacts: - path: ILSpy_binaries.zip name: ILSpy %APPVEYOR_REPO_BRANCH% %ILSPY_VERSION_NUMBER% binaries