From 856cedc95e647fa744ccfa08734118d634b65e1c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 23 Jul 2019 23:38:03 +0200 Subject: [PATCH] #1456: add test case; add additional checks to ensure we only transform normal locals into using/foreach-locals --- .../TestCases/Pretty/Async.cs | 11 +++++++++++ ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 2 +- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 3 ++- .../CSharp/Transforms/PatternStatementTransform.cs | 1 + ICSharpCode.Decompiler/IL/ILVariable.cs | 10 +++++++--- .../IL/Transforms/UsingTransform.cs | 2 ++ 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 14e6a8858..49a9a47f3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -18,6 +18,7 @@ #pragma warning disable 1998 using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -126,6 +127,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } #endif + + public static async Task GetIntegerSumAsync(IEnumerable items) + { + await Task.Delay(100); + int num = 0; + foreach (int item in items) { + num += item; + } + return num; + } } public struct HopToThreadPoolAwaitable : INotifyCompletion diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index e7e560d16..c0bbd2dc6 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -65,7 +65,7 @@ namespace ICSharpCode.Decompiler.CSharp /// * Otherwise, the C# type of the resulting expression shall match the IL stack type, /// and the evaluated values shall be the same. /// - class ExpressionBuilder : ILVisitor + sealed class ExpressionBuilder : ILVisitor { readonly IDecompilerTypeSystem typeSystem; internal readonly ITypeResolveContext decompilationContext; diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 3a36ebd9a..c0a6a31bb 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -32,7 +32,7 @@ using ICSharpCode.Decompiler.CSharp.Resolver; namespace ICSharpCode.Decompiler.CSharp { - class StatementBuilder : ILVisitor + sealed class StatementBuilder : ILVisitor { internal readonly ExpressionBuilder exprBuilder; readonly ILFunction currentFunction; @@ -412,6 +412,7 @@ namespace ICSharpCode.Decompiler.CSharp AstNode usingInit = resource; var var = inst.Variable; if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) { + Debug.Assert(var.Kind == VariableKind.UsingLocal); var.Kind = VariableKind.Local; var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable); var disposeVariable = currentFunction.RegisterVariable( diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 7d23edfa8..8aa5cfedb 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -467,6 +467,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms statementsToDelete.Add(stmt.GetNextStatement()); var itemVariable = foreachVariable.GetILVariable(); if (itemVariable == null || !itemVariable.IsSingleDefinition + || (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot) || !upperBounds.All(ub => ub.IsSingleDefinition && ub.LoadCount == 1) || !lowerBounds.All(lb => lb.StoreCount == 2 && lb.LoadCount == 3 && lb.AddressCount == 0)) return null; diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index 1983089df..9b77c5ecc 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -107,8 +107,12 @@ namespace ICSharpCode.Decompiler.IL internal set { if (kind == VariableKind.Parameter) throw new InvalidOperationException("Kind=Parameter cannot be changed!"); - if (Index != null && value.IsLocal()) - Debug.Assert(kind.IsLocal()); + if (Index != null && value.IsLocal() && !kind.IsLocal()) { + // For variables, Index has different meaning than for stack slots, + // so we need to reset it to null. + // StackSlot -> ForeachLocal can happen sometimes (e.g. PST.TransformForeachOnArray) + Index = null; + } kind = value; } } @@ -138,7 +142,7 @@ namespace ICSharpCode.Decompiler.IL /// For ExceptionStackSlot, the index is the IL offset of the exception handler. /// For other kinds, the index has no meaning, and is usually null. /// - public readonly int? Index; + public int? Index { get; private set; } [Conditional("DEBUG")] internal void CheckInvariant() diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index 452ccec52..471319713 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -127,6 +127,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type))) return false; + if (storeInst.Variable.Kind != VariableKind.Local) + 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))))