Browse Source

Improved async/await decompiler.

pull/276/merge
Daniel Grunwald 13 years ago
parent
commit
8acf17d50a
  1. 8
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 44
      ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs
  3. 8
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  4. 43
      ICSharpCode.Decompiler/Tests/Async.cs
  5. 6
      ICSharpCode.Decompiler/Tests/TestRunner.cs
  6. 3
      ILSpy/Languages/ILAstLanguage.cs

8
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -81,6 +81,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -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("<PrivateImplementationDetails>", StringComparison.Ordinal))
return true;
@ -1357,6 +1359,12 @@ namespace ICSharpCode.Decompiler.Ast @@ -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);

44
ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -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 @@ -29,6 +30,17 @@ namespace ICSharpCode.Decompiler.ILAst
/// </summary>
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 @@ -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 @@ -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 @@ -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 @@ -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<ILExpression>();
foreach (var v in expressions.Select(e => e.Operand).OfType<ILVariable>()) {
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 @@ -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<bool>::GetAwaiter, awaiterExpr)
ILExpression getAwaiterCall;
if (!(pos >= 2 && body[pos - 2].MatchStloc(awaiterVar, out getAwaiterCall)))
@ -552,6 +595,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -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<bool>, ldloca(CS$0$0002_66))

8
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -812,6 +812,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -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;

43
ICSharpCode.Decompiler/Tests/Async.cs

@ -59,7 +59,8 @@ public class Async @@ -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 @@ -77,27 +78,33 @@ public class Async
public async void AwaitInLoopCondition()
{
while (await SimpleBoolTaskMethod()) {
while (await this.SimpleBoolTaskMethod())
{
Console.WriteLine("Body");
}
}
public async Task<int> AwaitInForEach(IEnumerable<Task<int>> elements)
{
int sum = 0;
foreach (Task<int> element in elements) {
sum += await element;
int num = 0;
foreach (Task<int> 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 @@ -106,4 +113,22 @@ public class Async
{
return await(await task);
}
public async Task AwaitWithStack(Task<int> task)
{
Console.WriteLine("A", 1, await task);
}
public async Task AwaitWithStack2(Task<int> task)
{
if (await this.SimpleBoolTaskMethod())
{
Console.WriteLine("A", 1, await task);
}
else
{
int num = 1;
Console.WriteLine("A", 1, num);
}
}
}

6
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -34,6 +34,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -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()
{

3
ILSpy/Languages/ILAstLanguage.cs

@ -71,6 +71,9 @@ namespace ICSharpCode.ILSpy @@ -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();

Loading…
Cancel
Save