Browse Source

[async] Support state machine classes.

pull/844/head
Daniel Grunwald 8 years ago
parent
commit
2396a7449b
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 31
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -656,6 +656,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -656,6 +656,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (function.IsAsync) {
entityDecl.Modifiers |= Modifiers.Async;
RemoveAttribute(entityDecl, new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncStateMachineAttribute"));
RemoveAttribute(entityDecl, new TopLevelTypeName("System.Diagnostics", "DebuggerStepThroughAttribute"));
}
}

31
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
IType taskType; // return type of the async method
IType underlyingReturnType; // return type of the method (only the "T" for Task{T})
AsyncMethodType methodType;
ITypeDefinition stateMachineStruct;
ITypeDefinition stateMachineType;
ITypeDefinition builderType;
IField builderField;
IField stateField;
@ -84,8 +84,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -84,8 +84,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (body.Count < 5)
return false;
/* Example:
V_0 is an instance of the compiler-generated struct,
V_1 is an instance of the builder struct
V_0 is an instance of the compiler-generated struct/class,
V_1 is an instance of the builder struct/class
Block IL_0000 (incoming: 1) {
stobj System.Runtime.CompilerServices.AsyncVoidMethodBuilder(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+<AwaitYield>d__3.<>t__builder](ldloca V_0), call Create())
stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+<AwaitYield>d__3.<>1__state](ldloca V_0), ldc.i4 -1)
@ -127,8 +127,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -127,8 +127,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
if (!startCall.Arguments[1].MatchLdLoca(out ILVariable stateMachineVar))
return false;
stateMachineStruct = stateMachineVar.Type.GetDefinition();
if (stateMachineStruct?.Kind != TypeKind.Struct)
stateMachineType = stateMachineVar.Type.GetDefinition();
if (stateMachineType == null)
return false;
// Check third-to-last instruction (copy of builder)
@ -162,7 +162,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -162,7 +162,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
if (builderField2.MemberDefinition != builderField)
return false;
if (!target.MatchLdLoca(stateMachineVar))
if (!target.MatchLdLocRef(stateMachineVar))
return false;
}
@ -186,9 +186,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -186,9 +186,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (createCall.Method.Name != "Create" || createCall.Arguments.Count != 0)
return false;
for (int i = 0; i < body.Count - 5; i++) {
int pos = 0;
if (stateMachineType.Kind == TypeKind.Class) {
// If state machine is a class, the first instruction creates an instance:
// stloc stateMachine(newobj StateMachine.ctor())
if (!body[pos].MatchStLoc(stateMachineVar, out var init))
return false;
if (!(init is NewObj newobj && newobj.Arguments.Count == 0 && newobj.Method.DeclaringTypeDefinition == stateMachineType))
return false;
pos++;
}
for (; pos < body.Count - 5; pos++) {
// stfld StateMachine.field(ldloca stateMachine, ldvar(param))
if (!MatchStFld(body[i], stateMachineVar, out var field, out var fieldInit))
if (!MatchStFld(body[pos], stateMachineVar, out var field, out var fieldInit))
return false;
if (!fieldInit.MatchLdLoc(out var v))
return false;
@ -223,7 +233,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -223,7 +233,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (!stfld.MatchStFld(out var target, out field, out value))
return false;
field = field.MemberDefinition as IField;
return field != null && target.MatchLdLoca(stateMachineVar);
return field != null && target.MatchLdLocRef(stateMachineVar);
}
#endregion
@ -233,7 +243,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -233,7 +243,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary>
void AnalyzeMoveNext()
{
var moveNextMethod = context.TypeSystem.GetCecil(stateMachineStruct)?.Methods.FirstOrDefault(f => f.Name == "MoveNext");
var moveNextMethod = context.TypeSystem.GetCecil(stateMachineType)?.Methods.FirstOrDefault(f => f.Name == "MoveNext");
if (moveNextMethod == null)
throw new SymbolicAnalysisFailedException();
moveNextFunction = YieldReturnDecompiler.CreateILAst(moveNextMethod, context);
@ -275,6 +285,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -275,6 +285,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (!args[1].MatchLdLoc(out resultVar))
throw new SymbolicAnalysisFailedException();
} else {
resultVar = null;
if (args.Count != 1)
throw new SymbolicAnalysisFailedException();
}

Loading…
Cancel
Save