diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index d6951a600..b8d65759e 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -81,6 +81,8 @@ namespace ICSharpCode.Decompiler.Ast return true; if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(type)) return true; + if (settings.AsyncAwait && AsyncDecompiler.IsCompilerGeneratedStateMachine(type)) + return true; } else if (type.IsCompilerGenerated()) { if (type.Name.StartsWith("", StringComparison.Ordinal)) return true; @@ -1357,6 +1359,12 @@ namespace ICSharpCode.Decompiler.Ast // don't show the ParamArrayAttribute (it's converted to the 'params' modifier) continue; } + if (customAttribute.AttributeType.Name == "DebuggerStepThroughAttribute" && customAttribute.AttributeType.Namespace == "System.Diagnostics") { + // don't show the attribute if the method is async + EntityDeclaration entityDecl = attributedNode as EntityDeclaration; + if (entityDecl != null && entityDecl.HasModifier(Modifiers.Async)) + continue; + } var attribute = new ICSharpCode.NRefactory.CSharp.Attribute(); attribute.AddAnnotation(customAttribute); diff --git a/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs b/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs index e24dd48bc..5876febcd 100644 --- a/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs +++ b/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Mono.Cecil; +using Mono.Cecil.Cil; namespace ICSharpCode.Decompiler.ILAst { @@ -29,6 +30,17 @@ namespace ICSharpCode.Decompiler.ILAst /// class AsyncDecompiler { + public static bool IsCompilerGeneratedStateMachine(TypeDefinition type) + { + if (!(type.DeclaringType != null && type.IsCompilerGenerated())) + return false; + foreach (TypeReference i in type.Interfaces) { + if (i.Namespace == "System.Runtime.CompilerServices" && i.Name == "IAsyncStateMachine") + return true; + } + return false; + } + enum AsyncMethodType { Void, @@ -90,6 +102,7 @@ namespace ICSharpCode.Decompiler.ILAst ValidateCatchBlock(mainTryCatch.CatchBlocks[0]); AnalyzeStateMachine(mainTryCatch.TryBlock); // AnalyzeStateMachine invokes ConvertBody + MarkGeneratedVariables(); YieldReturnDecompiler.TranslateFieldsToLocalAccess(newTopLevelBody, fieldToParameterMap); } #endregion @@ -384,6 +397,7 @@ namespace ICSharpCode.Decompiler.ILAst FieldDefinition awaiterField; int targetStateID; HandleAwait(newBody, out awaiterVar, out awaiterField, out targetStateID); + MarkAsGeneratedVariable(awaiterVar); newBody.Add(new ILExpression(ILCode.Await, null, new ILExpression(ILCode.Ldloca, awaiterVar))); newBody.Add(MakeGoTo(mapping, targetStateID)); } else if (tryCatchBlock != null) { @@ -431,6 +445,10 @@ namespace ICSharpCode.Decompiler.ILAst if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies) && loadZero.Match(ILCode.Ldc_I4, out num) && num == 0) { newBody.RemoveAt(0); } + } else if (ceqExpr.Match(ILCode.LogicNot, out loadDoFinallyBodies)) { + if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies)) { + newBody.RemoveAt(0); + } } } return newBody; @@ -481,6 +499,26 @@ namespace ICSharpCode.Decompiler.ILAst } #endregion + #region MarkGeneratedVariables + int smallestGeneratedVariableIndex = int.MaxValue; + + void MarkAsGeneratedVariable(ILVariable v) + { + if (v.OriginalVariable != null && v.OriginalVariable.Index >= 0) { + smallestGeneratedVariableIndex = Math.Min(smallestGeneratedVariableIndex, v.OriginalVariable.Index); + } + } + + void MarkGeneratedVariables() + { + var expressions = new ILBlock(newTopLevelBody).GetSelfAndChildrenRecursive(); + foreach (var v in expressions.Select(e => e.Operand).OfType()) { + if (v.OriginalVariable != null && v.OriginalVariable.Index >= smallestGeneratedVariableIndex) + v.IsGenerated = true; + } + } + #endregion + #region RunStep2() method public static void RunStep2(DecompilerContext context, ILBlock method) { @@ -524,6 +562,11 @@ namespace ICSharpCode.Decompiler.ILAst if (!loadAwaiter.Match(ILCode.Ldloca, out awaiterVar)) return false; + ILVariable stackVar; + ILExpression stackExpr; + while (pos >= 1 && body[pos - 1].Match(ILCode.Stloc, out stackVar, out stackExpr)) + pos--; + // stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1::GetAwaiter, awaiterExpr) ILExpression getAwaiterCall; if (!(pos >= 2 && body[pos - 2].MatchStloc(awaiterVar, out getAwaiterCall))) @@ -552,6 +595,7 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Stloc: case ILCode.Initobj: case ILCode.Stfld: + case ILCode.Await: // e.g. // stloc(CS$0$0001, ldfld(StateMachine::<>u__$awaitere, ldloc(this))) // initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1, ldloca(CS$0$0002_66)) diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 3d7dd0787..2e46e285b 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -812,6 +812,14 @@ namespace ICSharpCode.Decompiler.ILAst } } return null; + case ILCode.Await: + { + TypeReference taskType = InferTypeForExpression(expr.Arguments[0], null); + if (taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") { + return ((GenericInstanceType)taskType).GenericArguments[0]; + } + return null; + } #endregion case ILCode.Pop: return null; diff --git a/ICSharpCode.Decompiler/Tests/Async.cs b/ICSharpCode.Decompiler/Tests/Async.cs index b7eae6748..e4707059c 100644 --- a/ICSharpCode.Decompiler/Tests/Async.cs +++ b/ICSharpCode.Decompiler/Tests/Async.cs @@ -59,7 +59,8 @@ public class Async public async void TwoAwaitsWithDifferentAwaiterTypes() { Console.WriteLine("Before"); - if (await SimpleBoolTaskMethod()) { + if (await this.SimpleBoolTaskMethod()) + { await Task.Delay(TimeSpan.FromSeconds(1.0)); } Console.WriteLine("After"); @@ -77,27 +78,33 @@ public class Async public async void AwaitInLoopCondition() { - while (await SimpleBoolTaskMethod()) { + while (await this.SimpleBoolTaskMethod()) + { Console.WriteLine("Body"); } } public async Task AwaitInForEach(IEnumerable> elements) { - int sum = 0; - foreach (Task element in elements) { - sum += await element; + int num = 0; + foreach (Task current in elements) + { + num += await current; } - return sum; + return num; } public async Task TaskMethodWithoutAwaitButWithExceptionHandling() { - try { - using (new StringWriter()) { + try + { + using (new StringWriter()) + { Console.WriteLine("No Await"); } - } catch (Exception) { + } + catch (Exception) + { Console.WriteLine("Crash"); } } @@ -106,4 +113,22 @@ public class Async { return await(await task); } + + public async Task AwaitWithStack(Task task) + { + Console.WriteLine("A", 1, await task); + } + + public async Task AwaitWithStack2(Task task) + { + if (await this.SimpleBoolTaskMethod()) + { + Console.WriteLine("A", 1, await task); + } + else + { + int num = 1; + Console.WriteLine("A", 1, num); + } + } } diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 8160a7376..52a03a462 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -34,6 +34,12 @@ namespace ICSharpCode.Decompiler.Tests [TestFixture] public class TestRunner { + [Test] + public void Async() + { + TestFile(@"..\..\Tests\Async.cs"); + } + [Test, Ignore("disambiguating overloads is not yet implemented")] public void CallOverloadedMethod() { diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index 46a242e21..96e88154b 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -71,6 +71,9 @@ namespace ICSharpCode.ILSpy output.Write("pinned "); v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } + if (v.IsGenerated) { + output.Write(" [generated]"); + } output.WriteLine(); } output.WriteLine();