Browse Source

Fix #1782: AsyncAwaitDecompiler for methods in struct

pull/1792/head
Daniel Grunwald 6 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 @@ -199,6 +199,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#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 bool IsCompleted {

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

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
@ -33,5 +34,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -33,5 +34,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
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 @@ -326,11 +326,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// stfld StateMachine.field(ldloca stateMachine, ldvar(param))
if (!MatchStFld(body[pos], stateMachineVar, out var field, out var fieldInit))
return false;
if (!fieldInit.MatchLdLoc(out var v))
return false;
if (v.Kind != VariableKind.Parameter)
if (fieldInit.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter) {
// OK, copies parameter into state machine
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;
fieldToParameterMap[field] = v;
}
}
return true;
@ -413,9 +417,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -413,9 +417,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
pos++;
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) {
fieldToParameterMap[field] = p;
} else if (value is LdObj ldobj && ldobj.Target.MatchLdThis()) {
fieldToParameterMap[field] = ((LdLoc)ldobj.Target).Variable;
} else {
return false;
fieldToParameterMap[field] = p;
}
pos++;
}
if (!body.Instructions[pos].MatchReturn(out var returnValue))
@ -967,18 +975,27 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -967,18 +975,27 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
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
// br trueInst
if (!block.MatchIfAtEndOfBlock(out var condition, out _, out var falseInst))
return false;
if (!condition.MatchLdFld(out var target, out var field))
return false;
if (!(target.MatchLdThis() && field.MemberDefinition == disposeModeField))
if (!MatchLdDisposeMode(condition))
return false;
context.Step($"SimplifyIfDisposeMode({block.StartILOffset:x4})", block);
block.Instructions[block.Instructions.Count - 2] = falseInst;
block.Instructions.RemoveAt(block.Instructions.Count - 1);
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)

Loading…
Cancel
Save