Browse Source

yield return decompiler: fix yield return in structs; fix local variables in finally block

pull/734/merge
Daniel Grunwald 8 years ago
parent
commit
b9b510d225
  1. 36
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  2. 35
      ICSharpCode.Decompiler/Tests/TestCases/Correctness/YieldReturn.cs

36
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// For each finally method, stores the target state when entering the finally block, /// For each finally method, stores the target state when entering the finally block,
/// and the decompiled code of the finally method body. /// and the decompiled code of the finally method body.
/// </summary> /// </summary>
readonly Dictionary<IMethod, (int? outerState, BlockContainer body)> decompiledFinallyMethods = new Dictionary<IMethod, (int? outerState, BlockContainer body)>(); readonly Dictionary<IMethod, (int? outerState, ILFunction function)> decompiledFinallyMethods = new Dictionary<IMethod, (int? outerState, ILFunction body)>();
/// <summary> /// <summary>
/// Temporary stores for 'yield break'. /// Temporary stores for 'yield break'.
@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
newBody.SortBlocks(deleteUnreachableBlocks: true); newBody.SortBlocks(deleteUnreachableBlocks: true);
DecompileFinallyBlocks(); DecompileFinallyBlocks();
ReconstructTryFinallyBlocks(newBody); ReconstructTryFinallyBlocks(function);
context.Step("Translate fields to local accesses", function); context.Step("Translate fields to local accesses", function);
TranslateFieldsToLocalAccess(function, function, fieldToParameterMap); TranslateFieldsToLocalAccess(function, function, fieldToParameterMap);
@ -188,12 +188,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
int i; int i;
for (i = 1; i < body.Instructions.Count; i++) { for (i = 1; i < body.Instructions.Count; i++) {
// stfld(..., ldloc(var_1), ldloc(parameter)) // stfld(..., ldloc(var_1), ldloc(parameter))
if (!body.Instructions[i].MatchStFld(out var ldloc, out var storedField, out var loadParameter)) // or (in structs): stfld(..., ldloc(var_1), ldobj(ldloc(this)))
if (!body.Instructions[i].MatchStFld(out var ldloc, out var storedField, out var value))
break; break;
if (ldloc.MatchLdLoc(var1) if (!ldloc.MatchLdLoc(var1)) {
&& loadParameter.MatchLdLoc(out var parameter) return false;
&& parameter.Kind == VariableKind.Parameter) { }
if (value.MatchLdLoc(out var parameter) && parameter.Kind == VariableKind.Parameter) {
fieldToParameterMap[(IField)storedField.MemberDefinition] = parameter; fieldToParameterMap[(IField)storedField.MemberDefinition] = parameter;
} else if (value is LdObj ldobj && ldobj.Target.MatchLdThis()) {
// copy of 'this' in struct
fieldToParameterMap[(IField)storedField.MemberDefinition] = ((LdLoc)ldobj.Target).Variable;
} else { } else {
return false; return false;
} }
@ -618,7 +623,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
v.StateMachineField = ldflda.Field; v.StateMachineField = ldflda.Field;
fieldToVariableMap.Add(fieldDef, v); fieldToVariableMap.Add(fieldDef, v);
} }
inst.ReplaceWith(new LdLoca(v) { ILRange = inst.ILRange }); if (v.StackType == StackType.Ref) {
Debug.Assert(v.Kind == VariableKind.Parameter && v.Index < 0); // this pointer
inst.ReplaceWith(new LdLoc(v) { ILRange = inst.ILRange });
} else {
inst.ReplaceWith(new LdLoca(v) { ILRange = inst.ILRange });
}
} else if (inst.MatchLdThis()) { } else if (inst.MatchLdThis()) {
inst.ReplaceWith(new InvalidExpression("stateMachine") { ExpectedResultType = inst.ResultType, ILRange = inst.ILRange }); inst.ReplaceWith(new InvalidExpression("stateMachine") { ExpectedResultType = inst.ResultType, ILRange = inst.ILRange });
} else { } else {
@ -651,7 +661,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
body.EntryPoint.Instructions.RemoveAt(0); body.EntryPoint.Instructions.RemoveAt(0);
} }
function.ReleaseRef(); // make body reusable outside of function function.ReleaseRef(); // make body reusable outside of function
decompiledFinallyMethods.Add(method, (newState, body)); decompiledFinallyMethods.Add(method, (newState, function));
} }
} }
#endregion #endregion
@ -670,8 +680,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// ///
/// Precondition: the blocks in newBody are topologically sorted. /// Precondition: the blocks in newBody are topologically sorted.
/// </summary> /// </summary>
void ReconstructTryFinallyBlocks(BlockContainer newBody) void ReconstructTryFinallyBlocks(ILFunction iteratorFunction)
{ {
BlockContainer newBody = (BlockContainer)iteratorFunction.Body;
context.Stepper.Step("Reconstuct try-finally blocks"); context.Stepper.Step("Reconstuct try-finally blocks");
var blockState = new int[newBody.Blocks.Count]; var blockState = new int[newBody.Blocks.Count];
blockState[0] = -1; blockState[0] = -1;
@ -736,7 +747,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
{ {
var finallyMethod = FindFinallyMethod(state); var finallyMethod = FindFinallyMethod(state);
Debug.Assert(finallyMethod != null); Debug.Assert(finallyMethod != null);
// remove the method so that it doesn't get cause ambiguity when processing nested try-finally blocks // remove the method so that it doesn't cause ambiguity when processing nested try-finally blocks
finallyMethodToStateRange.Remove(finallyMethod); finallyMethodToStateRange.Remove(finallyMethod);
var tryBlock = new Block(); var tryBlock = new Block();
@ -748,7 +759,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ILInstruction finallyBlock; ILInstruction finallyBlock;
if (decompiledFinallyMethods.TryGetValue(finallyMethod, out var decompiledMethod)) { if (decompiledFinallyMethods.TryGetValue(finallyMethod, out var decompiledMethod)) {
finallyBlock = decompiledMethod.body; finallyBlock = decompiledMethod.function.Body;
var vars = decompiledMethod.function.Variables.ToArray();
decompiledMethod.function.Variables.Clear();
iteratorFunction.Variables.AddRange(vars);
} else { } else {
finallyBlock = new InvalidBranch("Missing decompiledFinallyMethod"); finallyBlock = new InvalidBranch("Missing decompiledFinallyMethod");
} }

35
ICSharpCode.Decompiler/Tests/TestCases/Correctness/YieldReturn.cs

@ -63,9 +63,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Print("TryFinallyWithTwoExitPointsInNestedCatch(true)", TryFinallyWithTwoExitPointsInNestedCatch(true).GetEnumerator()); Print("TryFinallyWithTwoExitPointsInNestedCatch(true)", TryFinallyWithTwoExitPointsInNestedCatch(true).GetEnumerator());
#endif #endif
Print("GenericYield<int>()", GenericYield<int>().GetEnumerator()); Print("GenericYield<int>()", GenericYield<int>().GetEnumerator());
StructWithYieldReturn.Run();
} }
static void Print<T>(string name, IEnumerator<T> enumerator) internal static void Print<T>(string name, IEnumerator<T> enumerator)
{ {
Console.WriteLine(name + ": Test start"); Console.WriteLine(name + ": Test start");
while (enumerator.MoveNext()) { while (enumerator.MoveNext()) {
@ -427,6 +428,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
} }
#endif #endif
public static IEnumerable<int> LocalInFinally<T>(T a) where T : IDisposable
{
yield return 1;
try {
yield return 2;
} finally {
T b = a;
b.Dispose();
b.Dispose();
}
yield return 3;
}
public static IEnumerable<T> GenericYield<T>() where T : new() public static IEnumerable<T> GenericYield<T>() where T : new()
{ {
T val = new T(); T val = new T();
@ -435,4 +449,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
} }
} }
} }
struct StructWithYieldReturn
{
public static void Run()
{
var s = new StructWithYieldReturn { val = 2 };
var count = s.Count();
YieldReturnTest.Print("StructWithYieldReturn", count.GetEnumerator());
YieldReturnTest.Print("StructWithYieldReturn (again)", count.GetEnumerator());
}
int val;
public IEnumerable<int> Count()
{
yield return val++;
yield return val++;
}
}
} }
Loading…
Cancel
Save