Browse Source

[async]: control flow reconstruction: fixes for legacy csc

pull/844/head
Daniel Grunwald 8 years ago
parent
commit
3ff3b34be9
  1. 25
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  2. 6
      ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs

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

@ -56,6 +56,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
int finalState; // final state after the setResultAndExitBlock int finalState; // final state after the setResultAndExitBlock
ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock
ILVariable doFinallyBodies;
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {
if (!context.Settings.AsyncAwait) if (!context.Settings.AsyncAwait)
@ -267,7 +269,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
++pos; ++pos;
} }
mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch; mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch;
// TryCatch will be validated in ValidateCatchBlock() // CatchHandler will be validated in ValidateCatchBlock()
if (((BlockContainer)mainTryCatch.TryBlock).EntryPoint.Instructions[0] is StLoc initDoFinallyBodies
&& initDoFinallyBodies.Variable.Kind == VariableKind.Local
&& initDoFinallyBodies.Variable.Type.IsKnownType(KnownTypeCode.Boolean)
&& initDoFinallyBodies.Value.MatchLdcI4(1))
{
doFinallyBodies = initDoFinallyBodies.Variable;
}
setResultAndExitBlock = blockContainer.Blocks[1]; setResultAndExitBlock = blockContainer.Blocks[1];
// stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+<SimpleBoolTaskMethod>d__7.<>1__state](ldloc this), ldc.i4 -2) // stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+<SimpleBoolTaskMethod>d__7.<>1__state](ldloc this), ldc.i4 -2)
@ -413,6 +423,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Use a separate state range analysis per container. // Use a separate state range analysis per container.
var sra = new StateRangeAnalysis(StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); var sra = new StateRangeAnalysis(StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar);
sra.CancellationToken = context.CancellationToken; sra.CancellationToken = context.CancellationToken;
sra.doFinallyBodies = doFinallyBodies;
sra.AssignStateRanges(container, LongSet.Universe); sra.AssignStateRanges(container, LongSet.Universe);
foreach (var block in container.Blocks) { foreach (var block in container.Blocks) {
@ -429,6 +440,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
} }
} }
// Skip the state dispatcher and directly jump to the initial state
var entryPoint = sra.FindBlock(container, initialState); var entryPoint = sra.FindBlock(container, initialState);
if (entryPoint != null) { if (entryPoint != null) {
container.Blocks.Insert(0, new Block { container.Blocks.Insert(0, new Block {
@ -448,6 +460,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
state = 0; state = 0;
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
int pos = block.Instructions.Count - 2; int pos = block.Instructions.Count - 2;
if (doFinallyBodies != null && block.Instructions[pos] is StLoc storeDoFinallyBodies) {
if (!(storeDoFinallyBodies.Variable.Kind == VariableKind.Local
&& storeDoFinallyBodies.Variable.Type.IsKnownType(KnownTypeCode.Boolean)
&& storeDoFinallyBodies.Variable.Index == doFinallyBodies.Index)) {
return false;
}
if (!storeDoFinallyBodies.Value.MatchLdcI4(0))
return false;
pos--;
}
// call AwaitUnsafeOnCompleted(ldflda <>t__builder(ldloc this), ldloca awaiter, ldloc this) // call AwaitUnsafeOnCompleted(ldflda <>t__builder(ldloc this), ldloca awaiter, ldloc this)
if (!MatchCall(block.Instructions[pos], "AwaitUnsafeOnCompleted", out var callArgs)) if (!MatchCall(block.Instructions[pos], "AwaitUnsafeOnCompleted", out var callArgs))
return false; return false;

6
ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs

@ -58,6 +58,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
readonly Dictionary<Block, LongSet> ranges = new Dictionary<Block, LongSet>(); readonly Dictionary<Block, LongSet> ranges = new Dictionary<Block, LongSet>();
readonly internal Dictionary<IMethod, LongSet> finallyMethodToStateRange; // used only for IteratorDispose readonly internal Dictionary<IMethod, LongSet> finallyMethodToStateRange; // used only for IteratorDispose
internal ILVariable doFinallyBodies;
public StateRangeAnalysis(StateRangeAnalysisMode mode, IField stateField, ILVariable cachedStateVar = null) public StateRangeAnalysis(StateRangeAnalysisMode mode, IField stateField, ILVariable cachedStateVar = null)
{ {
this.mode = mode; this.mode = mode;
@ -149,6 +151,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return LongSet.Empty; return LongSet.Empty;
case Nop nop: case Nop nop:
return stateRange; return stateRange;
case StLoc stloc when stloc.Variable == doFinallyBodies:
// pre-roslyn async/await uses a generated 'bool doFinallyBodies';
// do not treat this as user code.
return stateRange;
case StLoc stloc when stloc.Variable.IsSingleDefinition: case StLoc stloc when stloc.Variable.IsSingleDefinition:
val = evalContext.Eval(stloc.Value); val = evalContext.Eval(stloc.Value);
if (val.Type == SymbolicValueType.State && val.Constant == 0) { if (val.Type == SymbolicValueType.State && val.Constant == 0) {

Loading…
Cancel
Save