diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 7ee64ffc4..f87cc0876 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -61,6 +61,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index d7c4aea81..5d3d575e8 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -58,6 +58,12 @@ namespace ICSharpCode.Decompiler.Tests Run(); } + [Test] + public void Issue684() + { + Run(); + } + [Test] public void Issue959() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il index 2e5183e67..4a9a59309 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il @@ -33,4 +33,4 @@ ret } -} // end of class DirectCallToExplicitInterfaceImpl +} // end of class TestClass diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs new file mode 100644 index 000000000..5ec516778 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs @@ -0,0 +1,39 @@ +using System; + +public static class Issue684 +{ + static int Main(string[] A_0) + { + int[] array = new int[1000]; + int num = int.Parse(Console.ReadLine()); + // Point of this test was to ensure the stack slot here uses an appropriate type, + // (bool instead of int). Unfortunately our type fixup runs too late to affect variable names. + bool num2 = num >= 1000; + if (!num2) { + num2 = (num < 2); + } + if (num2) { + Console.WriteLine(-1); + } else { + int i = 2; + for (int num3 = 2; num3 <= num; num3 = i) { + Console.WriteLine(num3); + for (; i <= num; i += num3) { + int num4 = array[i] = 1; + } + i = num3; + while (true) { + bool num5 = i <= num; + if (num5) { + num5 = (array[i] != 0); + } + if (!num5) { + break; + } + i++; + } + } + } + return 0; + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il new file mode 100644 index 000000000..55417b20b --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il @@ -0,0 +1,133 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly Issue684 +{ + .ver 1:0:0:0 +} +.module Issue684.exe + +.class public auto ansi abstract sealed Issue684 + extends [mscorlib]System.Object +{ + // Methods + + .method static privatescope + int32 Main$PST06000001 ( + string[] '' + ) cil managed + { + // Method begins at RVA 0x2050 + // Code size 196 (0xc4) + .maxstack 11 + .entrypoint + .locals init ( + [0] int32, + [1] int32, + [2] int32, + [3] int32[], + [4] int32 + ) + + IL_0000: ldc.i4 1000 + IL_0005: newarr [mscorlib]System.Int32 + IL_000a: stloc.3 + IL_000b: call string [mscorlib]System.Console::ReadLine() + IL_0010: call int32 [mscorlib]System.Int32::Parse(string) + IL_0015: stloc.2 + IL_0016: ldloc.2 + IL_0017: ldc.i4 1000 + IL_001c: clt + IL_001e: ldc.i4.0 + IL_001f: ceq + IL_0021: dup + IL_0022: brtrue IL_0030 + + IL_0027: pop + IL_0028: ldloc.2 + IL_0029: ldc.i4 2 + IL_002e: clt + + IL_0030: brfalse IL_0045 + + IL_0035: ldc.i4 1 + IL_003a: neg + IL_003b: call void [mscorlib]System.Console::WriteLine(int32) + IL_0040: br IL_00c2 + + IL_0045: ldc.i4 2 + IL_004a: stloc.0 + IL_004b: ldc.i4 2 + IL_0050: stloc.1 + // loop start (head: IL_0051) + IL_0051: ldloc.1 + IL_0052: ldloc.2 + IL_0053: cgt + IL_0055: ldc.i4.0 + IL_0056: ceq + IL_0058: brfalse IL_00c2 + + IL_005d: ldloc.1 + IL_005e: call void [mscorlib]System.Console::WriteLine(int32) + // loop start (head: IL_0063) + IL_0063: ldloc.0 + IL_0064: ldloc.2 + IL_0065: cgt + IL_0067: ldc.i4.0 + IL_0068: ceq + IL_006a: brfalse IL_0088 + + IL_006f: ldc.i4 1 + IL_0074: stloc.s 4 + IL_0076: ldloc.3 + IL_0077: ldloc.0 + IL_0078: ldloc.s 4 + IL_007a: stelem.any [mscorlib]System.Int32 + IL_007f: ldloc.0 + IL_0080: ldloc.1 + IL_0081: add + IL_0082: stloc.0 + IL_0083: br IL_0063 + // end loop + + IL_0088: ldloc.1 + IL_0089: stloc.0 + // loop start (head: IL_008a) + IL_008a: ldloc.0 + IL_008b: ldloc.2 + IL_008c: cgt + IL_008e: ldc.i4.0 + IL_008f: ceq + IL_0091: dup + IL_0092: brfalse IL_00a9 + + IL_0097: pop + IL_0098: ldloc.3 + IL_0099: ldloc.0 + IL_009a: ldelem.any [mscorlib]System.Int32 + IL_009f: ldc.i4 0 + IL_00a4: ceq + IL_00a6: ldc.i4.0 + IL_00a7: ceq + + IL_00a9: brfalse IL_00bb + + IL_00ae: ldloc.0 + IL_00af: ldc.i4 1 + IL_00b4: add + IL_00b5: stloc.0 + IL_00b6: br IL_008a + // end loop + + IL_00bb: ldloc.0 + IL_00bc: stloc.1 + IL_00bd: br IL_0051 + // end loop + + IL_00c2: ldc.i4.0 + IL_00c3: ret + } // end of method Program::Main + +} // end of class Issue684 diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c0bbd2dc6..ede9fa95c 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -613,7 +613,7 @@ namespace ICSharpCode.Decompiler.CSharp if (inst.Variable.Kind == VariableKind.StackSlot && !loadedVariablesSet.Contains(inst.Variable)) { // Stack slots in the ILAst have inaccurate types (e.g. System.Object for StackType.O) // so we should replace them with more accurate types where possible: - if ((inst.Variable.IsSingleDefinition || IsOtherValueType(translatedValue.Type) || inst.Variable.StackType == StackType.Ref) + if (CanUseTypeForStackSlot(inst.Variable, translatedValue.Type) && inst.Variable.StackType == translatedValue.Type.GetStackType() && translatedValue.Type.Kind != TypeKind.Null) { inst.Variable.Type = translatedValue.Type; @@ -633,10 +633,31 @@ namespace ICSharpCode.Decompiler.CSharp return Assignment(lhs, translatedValue).WithILInstruction(inst); } + bool CanUseTypeForStackSlot(ILVariable v, IType type) + { + return v.IsSingleDefinition + || IsOtherValueType(type) + || v.StackType == StackType.Ref + || AllStoresUseConsistentType(v.StoreInstructions, type); + } + bool IsOtherValueType(IType type) { return type.IsReferenceType == false && type.GetStackType() == StackType.O; } + + bool AllStoresUseConsistentType(IReadOnlyList storeInstructions, IType expectedType) + { + expectedType = expectedType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); + foreach (var store in storeInstructions) { + if (!(store is StLoc stloc)) + return false; + IType type = stloc.Value.InferType(compilation).AcceptVisitor(NormalizeTypeVisitor.TypeErasure); + if (!type.Equals(expectedType)) + return false; + } + return true; + } } protected internal override TranslatedExpression VisitComp(Comp inst, TranslationContext context)