Browse Source

Fix #646: VB-compilers 'For Each In' is not recognized as foreach during decompilation

pull/900/head
Siegfried Pammer 8 years ago
parent
commit
23bca3713f
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  3. 20
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue646.cs
  4. 93
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue646.il
  5. 51
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -62,6 +62,7 @@
<Compile Include="TestCases\Correctness\TrickyTypes.cs" /> <Compile Include="TestCases\Correctness\TrickyTypes.cs" />
<Compile Include="TestCases\Correctness\Using.cs" /> <Compile Include="TestCases\Correctness\Using.cs" />
<Compile Include="TestCases\ILPretty\Issue379.cs" /> <Compile Include="TestCases\ILPretty\Issue379.cs" />
<None Include="TestCases\ILPretty\Issue646.cs" />
<Compile Include="TestCases\Pretty\Async.cs" /> <Compile Include="TestCases\Pretty\Async.cs" />
<Compile Include="TestCases\Pretty\CheckedUnchecked.cs" /> <Compile Include="TestCases\Pretty\CheckedUnchecked.cs" />
<Compile Include="TestCases\Pretty\Generics.cs" /> <Compile Include="TestCases\Pretty\Generics.cs" />
@ -115,6 +116,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="TestCases\ILPretty\Issue646.il" />
<None Include="TestCases\Pretty\HelloWorld.il"> <None Include="TestCases\Pretty\HelloWorld.il">
<DependentUpon>HelloWorld.cs</DependentUpon> <DependentUpon>HelloWorld.cs</DependentUpon>
</None> </None>

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -36,6 +36,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(); Run();
} }
[Test]
public void Issue646()
{
Run();
}
void Run([CallerMemberName] string testName = null) void Run([CallerMemberName] string testName = null)
{ {
var ilFile = Path.Combine(TestCasePath, testName + ".il"); var ilFile = Path.Combine(TestCasePath, testName + ".il");

20
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue646.cs

@ -0,0 +1,20 @@
using Microsoft.VisualBasic.CompilerServices;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
[StandardModule]
internal sealed class Issue646
{
[STAThread]
public static void Main()
{
List<string> list = new List<string>();
foreach (string item in list) {
Debug.WriteLine(item);
}
}
}
}

93
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue646.il

@ -0,0 +1,93 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly extern System
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly extern Microsoft.VisualBasic
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 10:0:0:0
}
.assembly ConsoleApp11
{
.ver 1:0:0:0
}
.module ConsoleApp11.exe
// MVID: {B973FCD6-A9C4-48A9-8291-26DDC248E208}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00020003 // ILONLY 32BITPREFERRED
// Image base: 0x000001C4B6C90000
.class private auto ansi sealed ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue646
extends [mscorlib]System.Object
{
.custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method public static
void Main () cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x21b4
// Code size 61 (0x3d)
.maxstack 1
.entrypoint
.locals init (
[0] class [mscorlib]System.Collections.Generic.List`1<string>,
[1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>,
[2] string,
[3] bool
)
IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
IL_000d: stloc.1
IL_000e: br.s IL_0020
// loop start (head: IL_0020)
IL_0010: ldloca.s 1
IL_0012: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
IL_0017: stloc.2
IL_0018: ldloc.2
IL_0019: call void [System]System.Diagnostics.Debug::WriteLine(string)
IL_001e: nop
IL_001f: nop
IL_0020: ldloca.s 1
IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
IL_0027: stloc.3
IL_0028: ldloc.3
IL_0029: brtrue.s IL_0010
// end loop
IL_002b: leave.s IL_003c
} // end .try
finally
{
IL_002d: ldloca.s 1
IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003a: nop
IL_003b: endfinally
} // end handler
IL_003c: ret
} // end of method Module1::Main
} // end of class ConsoleApp11.Module1

51
ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!context.Settings.UsingStatement) return; if (!context.Settings.UsingStatement) return;
this.context = context; this.context = context;
for (int i = block.Instructions.Count - 1; i >= 0; i--) { for (int i = block.Instructions.Count - 1; i >= 0; i--) {
if (!TransformUsing(block, i)) if (!TransformUsing(block, i) && !TransformUsingVB(block, i))
continue; continue;
// This happens in some cases: // This happens in some cases:
// Use correct index after transformation. // Use correct index after transformation.
@ -91,6 +91,55 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
/// <summary>
/// .try BlockContainer {
/// Block IL_0003(incoming: 1) {
/// stloc obj(resourceExpression)
/// call WriteLine(ldstr "using (null)")
/// leave IL_0003(nop)
/// }
/// } finally BlockContainer {
/// Block IL_0012(incoming: 1) {
/// if (comp(ldloc obj != ldnull)) Block IL_001a {
/// callvirt Dispose(ldnull)
/// }
/// leave IL_0012(nop)
/// }
/// }
/// leave IL_0000(nop)
/// =>
/// using (resourceExpression) {
/// BlockContainer {
/// Block IL_0003(incoming: 1) {
/// call WriteLine(ldstr "using (null)")
/// leave IL_0003(nop)
/// }
/// }
/// }
/// </summary>
bool TransformUsingVB(Block block, int i)
{
if (!(block.Instructions[i] is TryFinally tryFinally))
return false;
if (!(tryFinally.TryBlock is BlockContainer tryContainer && tryContainer.EntryPoint.Instructions.FirstOrDefault() is StLoc storeInst))
return false;
if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type)))
return false;
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally)))
return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la))))
return false;
if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst))
return false;
if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull()))
return false;
context.Step("UsingTransformVB", tryFinally);
storeInst.Variable.Kind = VariableKind.UsingLocal;
tryContainer.EntryPoint.Instructions.RemoveAt(0);
block.Instructions[i] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock);
return true;
}
bool CheckResourceType(IType type) bool CheckResourceType(IType type)
{ {
// non-generic IEnumerator does not implement IDisposable. // non-generic IEnumerator does not implement IDisposable.

Loading…
Cancel
Save