From 9228e0102ca940e2d06baf5c932fdfed2eb08370 Mon Sep 17 00:00:00 2001 From: Wenxuan Zhao Date: Fri, 19 Oct 2018 14:48:22 -0700 Subject: [PATCH] Fix yield return for assembly compiled with Mono --- .../IL/ControlFlow/YieldReturnDecompiler.cs | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index a2b386105..7b550b3d3 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -70,6 +70,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// Set in AnalyzeCurrentProperty() IField currentField; + /// The disposing field of the compiler-generated enumerator class./summary> + /// Set in ConstructExceptionTable() for assembly compiled with Mono + IField disposingField; + /// Maps the fields of the compiler-generated class to the original parameters. /// Set in MatchEnumeratorCreationPattern() and ResolveIEnumerableIEnumeratorFieldMapping() readonly Dictionary fieldToParameterMap = new Dictionary(); @@ -459,6 +463,20 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow void ConstructExceptionTable() { if (isCompiledWithMono) { + disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "Dispose"); + BlockContainer body = disposeMethod == null ? null : (BlockContainer)CreateILAst(disposeMethod, context).Body; + + for (var i = 0; (i < body.EntryPoint.Instructions.Count) && !(body.EntryPoint.Instructions[i] is Branch); i++) { + if (body.EntryPoint.Instructions[i] is StObj stobj + && stobj.MatchStFld(out var target, out var field, out var value) + && target.MatchLdThis() + && field.Type.IsKnownType(KnownTypeCode.Boolean) + && value.MatchLdcI4(1)) { + disposingField = (IField)field.MemberDefinition; + break; + } + } + // On mono, we don't need to analyse Dispose() to reconstruct the try-finally structure. disposeMethod = default; finallyMethodToStateRange = default; @@ -633,7 +651,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow newBody.Blocks.Add(new Block { ILRange = oldBody.Blocks[blockIndex].ILRange }); } // convert contents of blocks - + for (int i = 0; i < oldBody.Blocks.Count; i++) { var oldBlock = oldBody.Blocks[i]; var newBlock = newBody.Blocks[i]; @@ -693,8 +711,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow && target.MatchLdThis() && field.MemberDefinition == stateField && value.MatchLdcI4(out int newState))) { - newBlock.Instructions.Add(new InvalidBranch("Unable to find new state assignment for yield return")); - return; + if (isCompiledWithMono + && disposingField != null + && oldBlock.Instructions[i + 1].MatchIfInstruction(out var condition, out var trueInst) + && condition.MatchLdFld(out target, out field) + && target.MatchLdThis() && field.MemberDefinition.Equals(disposingField) + && oldBlock.Instructions[i + 2].MatchBranch(out var targetBlock) + && targetBlock.Instructions[0].MatchStFld(out target, out field, out value) + && target.MatchLdThis() + && field.MemberDefinition == stateField + && value.MatchLdcI4(out newState)) { + // skip disposing branch, chain the next block + oldBlock = targetBlock; + i = -1; + if (oldBlock.Instructions[1].MatchBranch(out targetBlock) + && targetBlock.Instructions.Count == 2 + && targetBlock.Instructions[0].MatchStLoc(out var variable, out value) + && value.MatchLdcI4(out var flag) && flag == 1 + && targetBlock.Instructions[1].MatchBranch(out targetBlock)) { + oldBlock = targetBlock; + i = -2; + } + } else { + newBlock.Instructions.Add(new InvalidBranch("Unable to find new state assignment for yield return")); + return; + } } int pos = i + 2; if (oldBlock.Instructions[pos].MatchStLoc(skipFinallyBodies, out value)) {