Browse Source

Merge branch 'master' of github.com:icsharpcode/ILSpy into loops

# Conflicts:
#	ICSharpCode.Decompiler/IL/ILReader.cs
pull/976/head
Siegfried Pammer 8 years ago
parent
commit
2432c43880
  1. 2
      DecompilerNuGetDemos.workbook
  2. 2
      ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
  3. 14
      ICSharpCode.Decompiler.PowerShell/Demo.ps1
  4. 2
      ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
  5. 28
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs
  6. 31
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il
  7. 5
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
  8. 70
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il
  9. 56
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il
  10. 52
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il
  11. 66
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il
  12. 15
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  13. 1
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  14. 4
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template
  15. 7
      ICSharpCode.Decompiler/IL/BlockBuilder.cs
  16. 43
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  17. 134
      ICSharpCode.Decompiler/IL/ILReader.cs
  18. 2
      ICSharpCode.Decompiler/IL/Instructions.cs
  19. 10
      ICSharpCode.Decompiler/IL/Instructions.tt
  20. 22
      ICSharpCode.Decompiler/IL/StackType.cs
  21. 2
      ILSpy/Properties/AssemblyInfo.template.cs
  22. 4
      appveyor.yml

2
DecompilerNuGetDemos.workbook

@ -6,7 +6,7 @@ platforms: @@ -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

2
ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.0.1" />
<PackageReference Include="ICSharpCode.Decompiler" Version="3.0.0.3246-beta1" />
<PackageReference Include="ICSharpCode.Decompiler" Version="3.0.0.3287-beta2" />
</ItemGroup>
</Project>

14
ICSharpCode.Decompiler.PowerShell/Demo.ps1

@ -1,5 +1,13 @@ @@ -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) @@ -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

2
ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="PowerShellStandard.Library" Version="3.0.0-preview-01" />
<PackageReference Include="ICSharpCode.Decompiler" Version="3.0.0.3246-beta1" />
<PackageReference Include="ICSharpCode.Decompiler" Version="3.0.0.3287-beta2" />
</ItemGroup>
</Project>

28
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Loops.cs

@ -82,6 +82,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -82,6 +82,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine(FirstOrDefault(new List<int> { 1, 2, 3, 4, 5 }));
Console.WriteLine(NoForeachDueToMultipleCurrentAccess(new List<int> { 1, 2, 3, 4, 5 }));
Console.WriteLine(NoForeachCallWithSideEffect(new CustomClassEnumeratorWithIDisposable<int>()));
LoopWithGotoRepeat();
}
public static void ForWithMultipleVariables()
@ -222,5 +223,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -222,5 +223,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
return CallWithSideEffect<T>();
}
}
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");
}
}
}

31
ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il

@ -21,7 +21,7 @@ extends [mscorlib]System.Object @@ -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: @@ -48,7 +48,6 @@ pointless:
ret
}
/*
.method public static void Int32OrNativeTests()
{
ldstr "Int32OrNative(0x7fffffff, false) = {0}"
@ -79,6 +78,7 @@ pointless: @@ -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: @@ -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: @@ -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
@ -127,6 +148,8 @@ pointless: @@ -127,6 +148,8 @@ pointless:
.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: @@ -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: @@ -161,6 +186,7 @@ pointless:
conv.u
br after_if
}
*/
.method public static void RunInt32OrNativeMultiUse(int32 val)
{
@ -229,7 +255,6 @@ pointless: @@ -229,7 +255,6 @@ pointless:
add
ret
}
*/
.method public static void Print(native int val)
{

5
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs

@ -327,6 +327,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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);
}

70
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il

@ -10,7 +10,7 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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

56
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il

@ -10,7 +10,7 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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

52
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il

@ -25,14 +25,14 @@ @@ -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 @@ @@ -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 @@ @@ -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

66
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il

@ -25,14 +25,14 @@ @@ -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 @@ @@ -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 @@ @@ -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

15
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -810,6 +810,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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 @@ -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);

1
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs

@ -104,6 +104,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -104,6 +104,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
pre.MemberName = memberReferenceExpression.MemberName;
memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments);
pre.CopyAnnotationsFrom(uoe);
pre.RemoveAnnotations<ResolveResult>(); // only copy the ResolveResult from the MRE
pre.CopyAnnotationsFrom(memberReferenceExpression);
memberReferenceExpression.ReplaceWith(pre);
}

4
ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template

@ -17,8 +17,8 @@ @@ -17,8 +17,8 @@
<dependencies>
<dependency id="Humanizer.Core" version="2.2.0" />
<dependency id="Newtonsoft.Json" version="10.0.3" />
<dependency id="System.Collections.Immutable" version="1.4.0" />
<dependency id="System.ValueTuple" version="4.4.0" />
<dependency id="System.Collections.Immutable" version="1.3.1" />
<dependency id="System.ValueTuple" version="4.3.0" />
</dependencies>
</metadata>
<files>

7
ICSharpCode.Decompiler/IL/BlockBuilder.cs

@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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)

43
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -93,6 +93,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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,22 +112,44 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -109,22 +112,44 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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)
/// </remarks>
void FindLoops(ControlFlowNode h)
void IncludeNestedContainers(List<ControlFlowNode> 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

134
ICSharpCode.Decompiler/IL/ILReader.cs

@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.IL @@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.IL
Dictionary<int, ImmutableStack<ILVariable>> stackByOffset;
Dictionary<Cil.ExceptionHandler, ILVariable> variableByExceptionHandler;
UnionFind<ILVariable> unionFind;
List<(ILVariable, ILVariable)> stackMismatchPairs;
IEnumerable<ILVariable> stackVariables;
void Init(Cil.MethodBody body)
@ -74,8 +75,9 @@ namespace ICSharpCode.Decompiler.IL @@ -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<ILVariable>.Empty;
this.currentStack = ImmutableStack<ILVariable>.Empty;
this.unionFind = new UnionFind<ILVariable>();
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 @@ -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<ILInstruction>();
this.isBranchTarget = new BitArray(body.CodeSize);
this.stackByOffset = new Dictionary<int, ImmutableStack<ILVariable>>();
@ -186,30 +188,82 @@ namespace ICSharpCode.Decompiler.IL @@ -186,30 +188,82 @@ namespace ICSharpCode.Decompiler.IL
Warnings.Add(string.Format("IL_{0:x4}: {1}", currentInstruction.Offset, message));
}
void MergeStacks(ImmutableStack<ILVariable> a, ImmutableStack<ILVariable> b)
ImmutableStack<ILVariable> MergeStacks(ImmutableStack<ILVariable> a, ImmutableStack<ILVariable> 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<ILVariable> 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<ILVariable>();
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<ILVariable> a, ImmutableStack<ILVariable> 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<ILVariable> stack)
{
ImmutableStack<ILVariable> 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);
}
@ -272,6 +326,44 @@ namespace ICSharpCode.Decompiler.IL @@ -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<ILVariable, ILVariable>();
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<ILInstruction>();
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;
}
/// <summary>
@ -282,6 +374,12 @@ namespace ICSharpCode.Decompiler.IL @@ -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]) {

2
ICSharpCode.Decompiler/IL/Instructions.cs

@ -2317,6 +2317,8 @@ namespace ICSharpCode.Decompiler.IL @@ -2317,6 +2317,8 @@ namespace ICSharpCode.Decompiler.IL
clone.Value = this.value.Clone();
return clone;
}
/// <summary>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.</summary>
internal bool IsStackAdjustment;
public override StackType ResultType { get { return variable.StackType; } }
protected override InstructionFlags ComputeFlags()
{

10
ICSharpCode.Decompiler/IL/Instructions.tt

@ -153,6 +153,9 @@ @@ -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(
"/// <summary>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.</summary>" + 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 @@ -571,6 +574,13 @@ namespace ICSharpCode.Decompiler.IL
};
}
static Action<OpCode> AdditionalMember(string declaration)
{
return opCode => {
opCode.Members.Add(declaration);
};
}
// ResultType trait: the instruction has the specified result type.
static Action<OpCode> ResultType(string type)
{

22
ICSharpCode.Decompiler/IL/StackType.cs

@ -24,6 +24,15 @@ namespace ICSharpCode.Decompiler.IL @@ -24,6 +24,15 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
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.
/// <summary>
/// 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.
/// </summary>
Unknown,
/// <summary>32-bit integer</summary>
/// <remarks>
@ -33,23 +42,24 @@ namespace ICSharpCode.Decompiler.IL @@ -33,23 +42,24 @@ namespace ICSharpCode.Decompiler.IL
/// and any enums with one of the above as underlying type.
/// </remarks>
I4,
/// <summary>native-size integer, or unmanaged pointer</summary>
/// <remarks>
/// Used for C# <c>IntPtr</c>, <c>UIntPtr</c> and any native pointer types (<c>void*</c> etc.)
/// Also used for IL function pointer types.
/// </remarks>
I,
/// <summary>64-bit integer</summary>
/// <remarks>
/// Used for C# <c>long</c>, <c>ulong</c>,
/// and any enums with one of the above as underlying type.
/// </remarks>
I8,
/// <summary>native-size integer, or unmanaged pointer</summary>
/// <remarks>
/// Used for C# <c>IntPtr</c>, <c>UIntPtr</c> and any native pointer types (<c>void*</c> etc.)
/// </remarks>
I,
/// <summary>Floating point number</summary>
/// <remarks>
/// Used for C# <c>float</c> and <c>double</c>.
/// </remarks>
F,
/// <summary>Another stack type. Includes objects, value types, function pointers, ...</summary>
/// <summary>Another stack type. Includes objects, value types, ...</summary>
O,
/// <summary>A managed pointer</summary>
Ref,

2
ILSpy/Properties/AssemblyInfo.template.cs

@ -42,7 +42,7 @@ internal static class RevisionClass @@ -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$";
}

4
appveyor.yml

@ -18,6 +18,10 @@ test: @@ -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

Loading…
Cancel
Save