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. 35
      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. 45
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  17. 136
      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:
- DotNetCore - DotNetCore
packages: packages:
- id: ICSharpCode.Decompiler - 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 Setup: load the references required to work with the decompiler

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

@ -9,7 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.0.1" /> <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> </ItemGroup>
</Project> </Project>

14
ICSharpCode.Decompiler.PowerShell/Demo.ps1

@ -1,5 +1,13 @@
Import-Module .\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.PSCore.dll $basePath = $PSScriptRoot
$decompiler = Get-Decompiler .\bin\Debug\netstandard2.0\ICSharpCode.Decompiler.PSCore.dll 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 = Get-DecompiledTypes $decompiler -Types class
$classes.Count $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 Get-DecompiledProject $decompiler -OutputPath .\decomptest

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

@ -8,7 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="PowerShellStandard.Library" Version="3.0.0-preview-01" /> <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> </ItemGroup>
</Project> </Project>

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

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

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

@ -21,7 +21,7 @@ extends [mscorlib]System.Object
.entrypoint .entrypoint
call void Program::InlineAssignByte() call void Program::InlineAssignByte()
//call void Program::Int32OrNativeTests() call void Program::Int32OrNativeTests()
ret ret
} // end of method Main } // end of method Main
@ -48,7 +48,6 @@ pointless:
ret ret
} }
/*
.method public static void Int32OrNativeTests() .method public static void Int32OrNativeTests()
{ {
ldstr "Int32OrNative(0x7fffffff, false) = {0}" ldstr "Int32OrNative(0x7fffffff, false) = {0}"
@ -79,6 +78,7 @@ pointless:
box native int box native int
call void [mscorlib]System.Console::WriteLine(string, object) call void [mscorlib]System.Console::WriteLine(string, object)
/*
ldstr "Int32OrNativeLoopStyle(0x7fffffff):" ldstr "Int32OrNativeLoopStyle(0x7fffffff):"
call void [mscorlib]System.Console::WriteLine(string) call void [mscorlib]System.Console::WriteLine(string)
ldc.i4 0x7fffffff ldc.i4 0x7fffffff
@ -100,6 +100,7 @@ pointless:
call native int Program::Int32OrNativeDeadCode(int32) call native int Program::Int32OrNativeDeadCode(int32)
box native int box native int
call void [mscorlib]System.Console::WriteLine(string, object) call void [mscorlib]System.Console::WriteLine(string, object)
*/
ldc.i4 0x7fffffff ldc.i4 0x7fffffff
call void Program::RunInt32OrNativeMultiUse(int32) call void Program::RunInt32OrNativeMultiUse(int32)
@ -115,6 +116,26 @@ pointless:
use_i4: use_i4:
ldarg.0 ldarg.0
br after_if 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: after_if:
ldc.i4.1 ldc.i4.1
add add
@ -124,9 +145,11 @@ pointless:
conv.u conv.u
br after_if br after_if
} }
.method public static void Int32OrNativeLoopStyle(int32 val) .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 ( .locals init (
int32 i int32 i
) )
@ -149,6 +172,8 @@ pointless:
.method public static native int Int32OrNativeDeadCode(int32 val) .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: use_i4:
ldarg.0 ldarg.0
br after_if br after_if
@ -161,6 +186,7 @@ pointless:
conv.u conv.u
br after_if br after_if
} }
*/
.method public static void RunInt32OrNativeMultiUse(int32 val) .method public static void RunInt32OrNativeMultiUse(int32 val)
{ {
@ -229,8 +255,7 @@ pointless:
add add
ret ret
} }
*/
.method public static void Print(native int val) .method public static void Print(native int val)
{ {
ldarg.0 ldarg.0

5
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* ptr = stackalloc SimpleStruct[checked(count * 2)];
SimpleStruct* ptr2 = stackalloc SimpleStruct[10]; 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); return this.UsePointer(&ptr->Y);
} }

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

@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0 .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.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 .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 .hash algorithm 0x00008004
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module veti52ie.dll .module '5mgf1rnb.dll'
// MVID: {F873CF77-738D-4A61-B0C6-96B9F5621119} // MVID: {C39B0AE8-69C3-4207-B912-FCEE701C71E8}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x028F0000 // Image base: 0x00C60000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -1238,11 +1238,13 @@
.method public hidebysig instance string .method public hidebysig instance string
StackAllocStruct(int32 count) cil managed StackAllocStruct(int32 count) cil managed
{ {
// Code size 46 (0x2e) // Code size 110 (0x6e)
.maxstack 2 .maxstack 3
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0, .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_1, 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_0000: nop
IL_0001: ldarg.1 IL_0001: ldarg.1
IL_0002: ldc.i4.2 IL_0002: ldc.i4.2
@ -1258,16 +1260,50 @@
IL_0018: mul.ovf.un IL_0018: mul.ovf.un
IL_0019: localloc IL_0019: localloc
IL_001b: stloc.1 IL_001b: stloc.1
IL_001c: ldarg.0 IL_001c: ldloc.0
IL_001d: ldloc.0 IL_001d: ldarg.1
IL_001e: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y IL_001e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_0023: conv.u IL_0023: ldloc.0
IL_0024: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) IL_0024: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct
IL_0029: stloc.2 IL_002a: add
IL_002a: br.s IL_002c IL_002b: ldloc.0
IL_002c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_002c: ldloc.2 IL_0031: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_002d: ret 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 } // end of method UnsafeCode::StackAllocStruct
.method family hidebysig virtual instance void .method family hidebysig virtual instance void

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

@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0 .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.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 .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 .hash algorithm 0x00008004
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module '24yl5hoc.dll' .module av3nix0s.dll
// MVID: {73FF4D49-1CDC-4675-A00D-0D382845C323} // MVID: {3E5B8427-2816-45EA-9E7A-139AA9452535}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x013A0000 // Image base: 0x025C0000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -986,9 +986,10 @@
.method public hidebysig instance string .method public hidebysig instance string
StackAllocStruct(int32 count) cil managed StackAllocStruct(int32 count) cil managed
{ {
// Code size 41 (0x29) // Code size 97 (0x61)
.maxstack 2 .maxstack 3
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0) .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0,
int32 V_1)
IL_0000: ldarg.1 IL_0000: ldarg.1
IL_0001: ldc.i4.2 IL_0001: ldc.i4.2
IL_0002: mul.ovf IL_0002: mul.ovf
@ -1003,12 +1004,41 @@
IL_0017: mul.ovf.un IL_0017: mul.ovf.un
IL_0018: localloc IL_0018: localloc
IL_001a: pop IL_001a: pop
IL_001b: ldarg.0 IL_001b: ldloc.0
IL_001c: ldloc.0 IL_001c: ldarg.1
IL_001d: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y IL_001d: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_0022: conv.u IL_0022: ldloc.0
IL_0023: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) IL_0023: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct
IL_0028: ret 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 } // end of method UnsafeCode::StackAllocStruct
.method family hidebysig virtual instance void .method family hidebysig virtual instance void

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

@ -25,14 +25,14 @@
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module UnsafeCode.dll .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 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x01320000 // Image base: 0x030D0000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -991,9 +991,10 @@
.method public hidebysig instance string .method public hidebysig instance string
StackAllocStruct(int32 count) cil managed StackAllocStruct(int32 count) cil managed
{ {
// Code size 41 (0x29) // Code size 97 (0x61)
.maxstack 2 .maxstack 3
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0) .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0,
int32 V_1)
IL_0000: ldarg.1 IL_0000: ldarg.1
IL_0001: ldc.i4.2 IL_0001: ldc.i4.2
IL_0002: mul.ovf IL_0002: mul.ovf
@ -1008,12 +1009,41 @@
IL_0017: mul.ovf.un IL_0017: mul.ovf.un
IL_0018: localloc IL_0018: localloc
IL_001a: pop IL_001a: pop
IL_001b: ldarg.0 IL_001b: ldloc.0
IL_001c: ldloc.0 IL_001c: ldarg.1
IL_001d: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y IL_001d: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_0022: conv.u IL_0022: ldloc.0
IL_0023: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) IL_0023: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct
IL_0028: ret 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 } // end of method UnsafeCode::StackAllocStruct
.method family hidebysig virtual instance void .method family hidebysig virtual instance void

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

@ -25,14 +25,14 @@
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module UnsafeCode.dll .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 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x00AC0000 // Image base: 0x006B0000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -1242,11 +1242,13 @@
.method public hidebysig instance string .method public hidebysig instance string
StackAllocStruct(int32 count) cil managed StackAllocStruct(int32 count) cil managed
{ {
// Code size 46 (0x2e) // Code size 110 (0x6e)
.maxstack 2 .maxstack 3
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0, .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct* V_1, 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_0000: nop
IL_0001: ldarg.1 IL_0001: ldarg.1
IL_0002: ldc.i4.2 IL_0002: ldc.i4.2
@ -1262,16 +1264,50 @@
IL_0018: mul.ovf.un IL_0018: mul.ovf.un
IL_0019: localloc IL_0019: localloc
IL_001b: stloc.1 IL_001b: stloc.1
IL_001c: ldarg.0 IL_001c: ldloc.0
IL_001d: ldloc.0 IL_001d: ldarg.1
IL_001e: ldflda float64 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::Y IL_001e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_0023: conv.u IL_0023: ldloc.0
IL_0024: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::UsePointer(float64*) IL_0024: sizeof ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct
IL_0029: stloc.2 IL_002a: add
IL_002a: br.s IL_002c IL_002b: ldloc.0
IL_002c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_002c: ldloc.2 IL_0031: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/SimpleStruct::X
IL_002d: ret 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 } // end of method UnsafeCode::StackAllocStruct
.method family hidebysig virtual instance void .method family hidebysig virtual instance void

15
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -810,6 +810,10 @@ namespace ICSharpCode.Decompiler.CSharp
} }
return Translate(countOffsetInst); 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)) { } else if (byteOffsetInst.MatchLdcI(out long val)) {
// If the offset is a constant, it's possible that the compiler // If the offset is a constant, it's possible that the compiler
// constant-folded the multiplication. // constant-folded the multiplication.
@ -1540,9 +1544,14 @@ namespace ICSharpCode.Decompiler.CSharp
if (!TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) { if (!TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) {
target = target.ConvertTo(new PointerType(inst.Type), this); target = target.ConvertTo(new PointerType(inst.Type), this);
} }
result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) {
.WithoutILInstruction() // *&ptr -> ptr
.WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType)); 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); var value = Translate(inst.Value, typeHint: result.Type);
return Assignment(result, value).WithILInstruction(inst); return Assignment(result, value).WithILInstruction(inst);

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

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

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

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

7
ICSharpCode.Decompiler/IL/BlockBuilder.cs

@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.IL
foreach (var inst in instructions) { foreach (var inst in instructions) {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
int start = inst.ILRange.Start; int start = inst.ILRange.Start;
if (currentBlock == null || incomingBranches[start]) { if (currentBlock == null || (incomingBranches[start] && !IsStackAdjustment(inst))) {
// Finish up the previous block // Finish up the previous block
FinalizeCurrentBlock(start, fallthrough: true); FinalizeCurrentBlock(start, fallthrough: true);
// Leave nested containers if necessary // Leave nested containers if necessary
@ -158,6 +158,11 @@ namespace ICSharpCode.Decompiler.IL
ConnectBranches(mainContainer, cancellationToken); ConnectBranches(mainContainer, cancellationToken);
} }
static bool IsStackAdjustment(ILInstruction inst)
{
return inst is StLoc stloc && stloc.IsStackAdjustment;
}
private void FinalizeCurrentBlock(int currentILOffset, bool fallthrough) private void FinalizeCurrentBlock(int currentILOffset, bool fallthrough)
{ {
if (currentBlock == null) if (currentBlock == null)

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

@ -93,6 +93,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var headBlock = (Block)h.UserData; var headBlock = (Block)h.UserData;
context.Step($"Construct loop with head {headBlock.Label}", headBlock); context.Step($"Construct loop with head {headBlock.Label}", headBlock);
// loop now is the union of all natural loops with loop head h. // 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: // Try to extend the loop to reduce the number of exit points:
ExtendLoop(h, loop, out var exitPoint); ExtendLoop(h, loop, out var exitPoint);
@ -109,24 +112,46 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
/// <summary> /// <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> /// </summary>
/// <remarks> /// <remarks>
/// More details for why this is necessary are here:
/// https://github.com/icsharpcode/ILSpy/issues/915
/// ///
/// Preconditions: /// Pre+Post-Condition: node.Visited iff loop.Contains(node)
/// * 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
/// </remarks> /// </remarks>
void FindLoops(ControlFlowNode h) void IncludeNestedContainers(List<ControlFlowNode> loop)
{ {
// Recurse into the dominator tree to find other possible loop heads for (int i = 0; i < loop.Count; i++) {
foreach (var child in h.DominatorTreeChildren) { IncludeBlock((Block)loop[i].UserData);
FindLoops(child);
} }
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 #region ExtendLoop
/// <summary> /// <summary>
/// Given a natural loop, add additional CFG nodes to the loop in order /// Given a natural loop, add additional CFG nodes to the loop in order

136
ICSharpCode.Decompiler/IL/ILReader.cs

@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.IL
Dictionary<int, ImmutableStack<ILVariable>> stackByOffset; Dictionary<int, ImmutableStack<ILVariable>> stackByOffset;
Dictionary<Cil.ExceptionHandler, ILVariable> variableByExceptionHandler; Dictionary<Cil.ExceptionHandler, ILVariable> variableByExceptionHandler;
UnionFind<ILVariable> unionFind; UnionFind<ILVariable> unionFind;
List<(ILVariable, ILVariable)> stackMismatchPairs;
IEnumerable<ILVariable> stackVariables; IEnumerable<ILVariable> stackVariables;
void Init(Cil.MethodBody body) void Init(Cil.MethodBody body)
@ -74,8 +75,9 @@ namespace ICSharpCode.Decompiler.IL
this.debugInfo = body.Method.DebugInformation; this.debugInfo = body.Method.DebugInformation;
this.currentInstruction = null; this.currentInstruction = null;
this.nextInstructionIndex = 0; this.nextInstructionIndex = 0;
this.currentStack = System.Collections.Immutable.ImmutableStack<ILVariable>.Empty; this.currentStack = ImmutableStack<ILVariable>.Empty;
this.unionFind = new UnionFind<ILVariable>(); this.unionFind = new UnionFind<ILVariable>();
this.stackMismatchPairs = new List<(ILVariable, ILVariable)>();
this.methodReturnStackType = typeSystem.Resolve(body.Method.ReturnType).GetStackType(); this.methodReturnStackType = typeSystem.Resolve(body.Method.ReturnType).GetStackType();
InitParameterVariables(); InitParameterVariables();
this.localVariables = body.Variables.SelectArray(CreateILVariable); this.localVariables = body.Variables.SelectArray(CreateILVariable);
@ -84,7 +86,7 @@ namespace ICSharpCode.Decompiler.IL
v.HasInitialValue = true; v.HasInitialValue = true;
} }
} }
mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType);
this.instructionBuilder = new List<ILInstruction>(); this.instructionBuilder = new List<ILInstruction>();
this.isBranchTarget = new BitArray(body.CodeSize); this.isBranchTarget = new BitArray(body.CodeSize);
this.stackByOffset = new Dictionary<int, ImmutableStack<ILVariable>>(); this.stackByOffset = new Dictionary<int, ImmutableStack<ILVariable>>();
@ -186,35 +188,87 @@ namespace ICSharpCode.Decompiler.IL
Warnings.Add(string.Format("IL_{0:x4}: {1}", currentInstruction.Offset, message)); 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(); if (CheckStackCompatibleWithoutAdjustments(a, b)) {
var enum2 = b.GetEnumerator(); // We only need to union the input variables, but can
bool ok1 = enum1.MoveNext(); // otherwise re-use the existing stack.
bool ok2 = enum2.MoveNext(); ImmutableStack<ILVariable> output = a;
while (ok1 && ok2) { while (!a.IsEmpty && !b.IsEmpty) {
if (enum1.Current.StackType != enum2.Current.StackType) { Debug.Assert(a.Peek().StackType == b.Peek().StackType);
Warn("Incompatible stack types: " + enum1.Current.StackType + " vs " + enum2.Current.StackType); unionFind.Merge(a.Peek(), b.Peek());
a = a.Pop();
b = b.Pop();
} }
unionFind.Merge(enum1.Current, enum2.Current); return output;
ok1 = enum1.MoveNext(); } else if (a.Count() != b.Count()) {
ok2 = enum2.MoveNext(); // Let's not try to merge mismatched stacks.
}
if (ok1 || ok2) {
Warn("Incompatible stack heights: " + a.Count() + " vs " + b.Count()); 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) void StoreStackForOffset(int offset, ImmutableStack<ILVariable> stack)
{ {
ImmutableStack<ILVariable> existing; if (stackByOffset.TryGetValue(offset, out var existing)) {
if (stackByOffset.TryGetValue(offset, out existing)) { var newStack = MergeStacks(existing, stack);
MergeStacks(existing, stack); if (newStack != existing)
stackByOffset[offset] = newStack;
} else { } else {
stackByOffset.Add(offset, stack); stackByOffset.Add(offset, stack);
} }
} }
void ReadInstructions(CancellationToken cancellationToken) void ReadInstructions(CancellationToken cancellationToken)
{ {
// Fill isBranchTarget and branchStackDict based on exception handlers // Fill isBranchTarget and branchStackDict based on exception handlers
@ -272,6 +326,44 @@ namespace ICSharpCode.Decompiler.IL
instructionBuilder[i] = instructionBuilder[i].AcceptVisitor(visitor); instructionBuilder[i] = instructionBuilder[i].AcceptVisitor(visitor);
} }
stackVariables = visitor.variables; 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> /// <summary>
@ -282,6 +374,12 @@ namespace ICSharpCode.Decompiler.IL
Init(body); Init(body);
ReadInstructions(cancellationToken); ReadInstructions(cancellationToken);
foreach (var inst in instructionBuilder) { foreach (var inst in instructionBuilder) {
if (inst is StLoc stloc && stloc.IsStackAdjustment) {
output.Write(" ");
inst.WriteTo(output, new ILAstWritingOptions());
output.WriteLine();
continue;
}
output.Write(" ["); output.Write(" [");
bool isFirstElement = true; bool isFirstElement = true;
foreach (var element in stackByOffset[inst.ILRange.Start]) { foreach (var element in stackByOffset[inst.ILRange.Start]) {

2
ICSharpCode.Decompiler/IL/Instructions.cs

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

10
ICSharpCode.Decompiler/IL/Instructions.tt

@ -153,6 +153,9 @@
new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine 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())", + "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"), 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")), ResultType("variable.StackType")),
new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.",
CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")), CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")),
@ -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. // ResultType trait: the instruction has the specified result type.
static Action<OpCode> ResultType(string type) static Action<OpCode> ResultType(string type)
{ {

22
ICSharpCode.Decompiler/IL/StackType.cs

@ -24,6 +24,15 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public enum StackType : byte 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, Unknown,
/// <summary>32-bit integer</summary> /// <summary>32-bit integer</summary>
/// <remarks> /// <remarks>
@ -33,23 +42,24 @@ namespace ICSharpCode.Decompiler.IL
/// and any enums with one of the above as underlying type. /// and any enums with one of the above as underlying type.
/// </remarks> /// </remarks>
I4, 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> /// <summary>64-bit integer</summary>
/// <remarks> /// <remarks>
/// Used for C# <c>long</c>, <c>ulong</c>, /// Used for C# <c>long</c>, <c>ulong</c>,
/// and any enums with one of the above as underlying type. /// and any enums with one of the above as underlying type.
/// </remarks> /// </remarks>
I8, 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> /// <summary>Floating point number</summary>
/// <remarks> /// <remarks>
/// Used for C# <c>float</c> and <c>double</c>. /// Used for C# <c>float</c> and <c>double</c>.
/// </remarks> /// </remarks>
F, F,
/// <summary>Another stack type. Includes objects, value types, function pointers, ...</summary> /// <summary>Another stack type. Includes objects, value types, ...</summary>
O, O,
/// <summary>A managed pointer</summary> /// <summary>A managed pointer</summary>
Ref, Ref,

2
ILSpy/Properties/AssemblyInfo.template.cs

@ -42,7 +42,7 @@ internal static class RevisionClass
public const string Minor = "0"; public const string Minor = "0";
public const string Build = "0"; public const string Build = "0";
public const string Revision = "$INSERTREVISION$"; 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$"; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$";
} }

4
appveyor.yml

@ -18,6 +18,10 @@ test:
- 'ILSpy.BamlDecompiler.Tests\bin\%configuration%\net461\ILSpy.BamlDecompiler.Tests.dll' - 'ILSpy.BamlDecompiler.Tests\bin\%configuration%\net461\ILSpy.BamlDecompiler.Tests.dll'
after_test: after_test:
- python BuildTools\tidy.py - python BuildTools\tidy.py
for:
- branches:
only:
- master
artifacts: artifacts:
- path: ILSpy_binaries.zip - path: ILSpy_binaries.zip
name: ILSpy %APPVEYOR_REPO_BRANCH% %ILSPY_VERSION_NUMBER% binaries name: ILSpy %APPVEYOR_REPO_BRANCH% %ILSPY_VERSION_NUMBER% binaries

Loading…
Cancel
Save