Browse Source

[async] Handle pre-roslyn stack saving during await.

pull/850/head
Daniel Grunwald 8 years ago
parent
commit
098d1152ff
  1. 65
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

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

@ -648,9 +648,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return; return;
if (awaitBlockData.awaiterVar != awaiterVar) if (awaitBlockData.awaiterVar != awaiterVar)
return; return;
if (!CheckAwaitBlock(awaitBlock, out var resumeBlock)) if (!CheckAwaitBlock(awaitBlock, out var resumeBlock, out var stackField))
return; return;
if (!CheckResumeBlock(resumeBlock, awaiterVar, awaitBlockData.awaiterField, completedBlock)) if (!CheckResumeBlock(resumeBlock, awaiterVar, awaitBlockData.awaiterField, completedBlock, stackField))
return; return;
// Check completedBlock: // Check completedBlock:
ILInstruction getResultCall; ILInstruction getResultCall;
@ -685,25 +685,38 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
} }
} }
bool CheckAwaitBlock(Block block, out Block resumeBlock) bool CheckAwaitBlock(Block block, out Block resumeBlock, out IField stackField)
{ {
// awaitBlock: // awaitBlock:
// (pre-roslyn: save stack)
// await(ldloca V_2) // await(ldloca V_2)
// br resumeBlock // br resumeBlock
resumeBlock = null; resumeBlock = null;
if (block.Instructions.Count != 2) stackField = null;
return false; if (block.Instructions.Count < 2)
if (block.Instructions[0].OpCode != OpCode.Await)
return false; return false;
if (!block.Instructions[1].MatchBranch(out resumeBlock)) int pos = 0;
if (block.Instructions[pos] is StLoc stloc && stloc.Variable.IsSingleDefinition) {
if (!block.Instructions[pos + 1].MatchStFld(out var target, out stackField, out var value))
return false;
if (!target.MatchLdThis())
return false;
pos += 2;
}
// await(ldloca awaiterVar)
if (block.Instructions[pos].OpCode != OpCode.Await)
return false; return false;
return true; // br resumeBlock
return block.Instructions[pos + 1].MatchBranch(out resumeBlock);
} }
bool CheckResumeBlock(Block block, ILVariable awaiterVar, IField awaiterField, Block completedBlock) bool CheckResumeBlock(Block block, ILVariable awaiterVar, IField awaiterField, Block completedBlock, IField stackField)
{ {
int pos = 0; int pos = 0;
if (!RestoreStack(block, ref pos, stackField))
return false;
// stloc awaiterVar(ldfld awaiterField(ldloc this)) // stloc awaiterVar(ldfld awaiterField(ldloc this))
if (!block.Instructions[pos].MatchStLoc(awaiterVar, out var value)) if (!block.Instructions[pos].MatchStLoc(awaiterVar, out var value))
return false; return false;
@ -752,6 +765,38 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return block.Instructions[pos].MatchBranch(completedBlock); return block.Instructions[pos].MatchBranch(completedBlock);
} }
private bool RestoreStack(Block block, ref int pos, IField stackField)
{
if (stackField == null) {
return true; // nothing to restore
}
// stloc temp(unbox.any T(ldfld <>t__stack(ldloc this)))
if (!(block.Instructions[pos] is StLoc stloc))
return false;
if (!stloc.Variable.IsSingleDefinition)
return false;
if (!(stloc.Value is UnboxAny unbox))
return false;
if (!unbox.Argument.MatchLdFld(out var target, out var field))
return false;
if (!target.MatchLdThis())
return false;
if (!field.Equals(stackField))
return false;
pos++;
// restoring stack slots
while (block.Instructions[pos].MatchStLoc(out var v) && v.Kind == VariableKind.StackSlot) {
pos++;
}
// stfld <>t__stack(ldloc this, ldnull)
if (block.Instructions[pos].MatchStFld(out target, out field, out var value)) {
if (target.MatchLdThis() && field.Equals(stackField) && value.MatchLdNull()) {
pos++;
}
}
return true;
}
#endregion #endregion
void MarkGeneratedVariables(ILFunction function) void MarkGeneratedVariables(ILFunction function)

Loading…
Cancel
Save