Browse Source

Fix yield return for assembly compiled with Mono

pull/1303/head
Wenxuan Zhao 7 years ago
parent
commit
9228e0102c
No known key found for this signature in database
GPG Key ID: B45B13F10587A57
  1. 47
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

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

@ -70,6 +70,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -70,6 +70,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <remarks>Set in AnalyzeCurrentProperty()</remarks>
IField currentField;
/// <summary>The disposing field of the compiler-generated enumerator class./summary>
/// <remarks>Set in ConstructExceptionTable() for assembly compiled with Mono</remarks>
IField disposingField;
/// <summary>Maps the fields of the compiler-generated class to the original parameters.</summary>
/// <remarks>Set in MatchEnumeratorCreationPattern() and ResolveIEnumerableIEnumeratorFieldMapping()</remarks>
readonly Dictionary<IField, ILVariable> fieldToParameterMap = new Dictionary<IField, ILVariable>();
@ -459,6 +463,20 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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 @@ -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 @@ -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)) {

Loading…
Cancel
Save