Browse Source

Fix #1782: AsyncAwaitDecompiler for methods in struct

pull/1792/head
Daniel Grunwald 7 years ago
parent
commit
eaecedd8ea
  1. 13
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  2. 30
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncStreams.cs
  3. 35
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

13
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs

@ -199,6 +199,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif #endif
} }
public struct AsyncInStruct
{
private int i;
public async Task<int> Test(AsyncInStruct xx)
{
xx.i++;
i++;
await Task.Yield();
return i + xx.i;
}
}
public struct HopToThreadPoolAwaitable : INotifyCompletion public struct HopToThreadPoolAwaitable : INotifyCompletion
{ {
public bool IsCompleted { public bool IsCompleted {

30
ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncStreams.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
@ -33,5 +34,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
yield break; yield break;
} }
public async IAsyncEnumerable<int> AwaitInFinally()
{
try {
Console.WriteLine("try");
yield return 1;
Console.WriteLine("end try");
} finally {
Console.WriteLine("finally");
await Task.Yield();
Console.WriteLine("end finally");
}
}
}
public struct TestStruct
{
private int i;
public async IAsyncEnumerable<int> AwaitInStruct(TestStruct xx)
{
xx.i++;
i++;
await Task.Yield();
yield return i;
yield return xx.i;
}
} }
} }

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

@ -326,11 +326,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// stfld StateMachine.field(ldloca stateMachine, ldvar(param)) // stfld StateMachine.field(ldloca stateMachine, ldvar(param))
if (!MatchStFld(body[pos], stateMachineVar, out var field, out var fieldInit)) if (!MatchStFld(body[pos], stateMachineVar, out var field, out var fieldInit))
return false; return false;
if (!fieldInit.MatchLdLoc(out var v)) if (fieldInit.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter) {
return false; // OK, copies parameter into state machine
if (v.Kind != VariableKind.Parameter)
return false;
fieldToParameterMap[field] = v; fieldToParameterMap[field] = v;
} else if (fieldInit is LdObj ldobj && ldobj.Target.MatchLdThis()) {
// stfld <>4__this(ldloc stateMachine, ldobj AsyncInStruct(ldloc this))
fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable;
} else {
return false;
}
} }
return true; return true;
@ -413,9 +417,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
pos++; pos++;
while (MatchStFld(body.Instructions[pos], v, out var field, out var value)) { while (MatchStFld(body.Instructions[pos], v, out var field, out var value)) {
if (!value.MatchLdLoc(out var p)) if (value.MatchLdLoc(out var p) && p.Kind == VariableKind.Parameter) {
return false;
fieldToParameterMap[field] = p; fieldToParameterMap[field] = p;
} else if (value is LdObj ldobj && ldobj.Target.MatchLdThis()) {
fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable;
} else {
return false;
}
pos++; pos++;
} }
if (!body.Instructions[pos].MatchReturn(out var returnValue)) if (!body.Instructions[pos].MatchReturn(out var returnValue))
@ -967,18 +975,27 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
private bool SimplifyIfDisposeMode(Block block) private bool SimplifyIfDisposeMode(Block block)
{ {
// Occasionally Roslyn optimizes out an "if (disposeMode)", but keeps the
// disposeMode field access. Get rid of those field accesses:
block.Instructions.RemoveAll(MatchLdDisposeMode);
// if (logic.not(ldfld disposeMode(ldloc this))) br falseInst // if (logic.not(ldfld disposeMode(ldloc this))) br falseInst
// br trueInst // br trueInst
if (!block.MatchIfAtEndOfBlock(out var condition, out _, out var falseInst)) if (!block.MatchIfAtEndOfBlock(out var condition, out _, out var falseInst))
return false; return false;
if (!condition.MatchLdFld(out var target, out var field)) if (!MatchLdDisposeMode(condition))
return false;
if (!(target.MatchLdThis() && field.MemberDefinition == disposeModeField))
return false; return false;
context.Step($"SimplifyIfDisposeMode({block.StartILOffset:x4})", block); context.Step($"SimplifyIfDisposeMode({block.StartILOffset:x4})", block);
block.Instructions[block.Instructions.Count - 2] = falseInst; block.Instructions[block.Instructions.Count - 2] = falseInst;
block.Instructions.RemoveAt(block.Instructions.Count - 1); block.Instructions.RemoveAt(block.Instructions.Count - 1);
return true; return true;
bool MatchLdDisposeMode(ILInstruction inst)
{
if (!inst.MatchLdFld(out var target, out var field))
return false;
return target.MatchLdThis() && field.MemberDefinition == disposeModeField;
}
} }
bool AnalyzeAwaitBlock(Block block, out ILVariable awaiter, out IField awaiterField, out int state, out int yieldOffset) bool AnalyzeAwaitBlock(Block block, out ILVariable awaiter, out IField awaiterField, out int state, out int yieldOffset)

Loading…
Cancel
Save