Browse Source

Merge branch 'PR1303'

pull/1317/head
Daniel Grunwald 7 years ago
parent
commit
b0bc67adb4
  1. 96
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  2. 11
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

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

@ -70,6 +70,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <remarks>Set in AnalyzeCurrentProperty()</remarks> /// <remarks>Set in AnalyzeCurrentProperty()</remarks>
IField currentField; 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> /// <summary>Maps the fields of the compiler-generated class to the original parameters.</summary>
/// <remarks>Set in MatchEnumeratorCreationPattern() and ResolveIEnumerableIEnumeratorFieldMapping()</remarks> /// <remarks>Set in MatchEnumeratorCreationPattern() and ResolveIEnumerableIEnumeratorFieldMapping()</remarks>
readonly Dictionary<IField, ILVariable> fieldToParameterMap = new Dictionary<IField, ILVariable>(); readonly Dictionary<IField, ILVariable> fieldToParameterMap = new Dictionary<IField, ILVariable>();
@ -112,6 +116,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
this.enumeratorCtor = default; this.enumeratorCtor = default;
this.stateField = null; this.stateField = null;
this.currentField = null; this.currentField = null;
this.disposingField = null;
this.fieldToParameterMap.Clear(); this.fieldToParameterMap.Clear();
this.finallyMethodToStateRange = null; this.finallyMethodToStateRange = null;
this.decompiledFinallyMethods.Clear(); this.decompiledFinallyMethods.Clear();
@ -147,22 +152,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
context.Step("Delete unreachable blocks", function); context.Step("Delete unreachable blocks", function);
// Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid
// (though some may point to now-deleted blocks)
newBody.SortBlocks(deleteUnreachableBlocks: true);
if (isCompiledWithMono) { if (isCompiledWithMono) {
// mono has try-finally inline (like async on MS); we also need to sort nested blocks: // mono has try-finally inline (like async on MS); we also need to sort nested blocks:
foreach (var nestedContainer in newBody.Blocks.SelectMany(c => c.Descendants).OfType<BlockContainer>()) { foreach (var nestedContainer in newBody.Blocks.SelectMany(c => c.Descendants).OfType<BlockContainer>()) {
nestedContainer.SortBlocks(deleteUnreachableBlocks: true); nestedContainer.SortBlocks(deleteUnreachableBlocks: true);
} }
} else { // We need to clean up nested blocks before the main block, so that edges from unreachable code
// in nested containers into the main container are removed before we clean up the main container.
}
// Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid
// (though some may point to now-deleted blocks)
newBody.SortBlocks(deleteUnreachableBlocks: true);
if (!isCompiledWithMono) {
DecompileFinallyBlocks(); DecompileFinallyBlocks();
ReconstructTryFinallyBlocks(function); 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, isCompiledWithMono);
CleanSkipFinallyBodies(function); CleanSkipFinallyBodies(function);
@ -459,19 +468,32 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void ConstructExceptionTable() void ConstructExceptionTable()
{ {
if (isCompiledWithMono) { if (isCompiledWithMono) {
// On mono, we don't need to analyse Dispose() to reconstruct the try-finally structure. disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "Dispose");
disposeMethod = default; var function = CreateILAst(disposeMethod, context);
finallyMethodToStateRange = default; BlockContainer body = (BlockContainer)function.Body;
return;
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.
finallyMethodToStateRange = default;
} else {
// Non-Mono: analyze try-finally structure in Dispose()
disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "System.IDisposable.Dispose"); disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "System.IDisposable.Dispose");
var function = CreateILAst(disposeMethod, context); var function = CreateILAst(disposeMethod, context);
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorDispose, stateField); var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorDispose, stateField);
rangeAnalysis.AssignStateRanges(function.Body, LongSet.Universe); rangeAnalysis.AssignStateRanges(function.Body, LongSet.Universe);
finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange; finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
} }
}
[Conditional("DEBUG")] [Conditional("DEBUG")]
void PrintFinallyMethodStateRanges(BlockContainer bc) void PrintFinallyMethodStateRanges(BlockContainer bc)
@ -656,7 +678,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} else if (field.MemberDefinition.Equals(currentField)) { } else if (field.MemberDefinition.Equals(currentField)) {
// create yield return // create yield return
newBlock.Instructions.Add(new YieldReturn(value) { ILRange = oldInst.ILRange }); newBlock.Instructions.Add(new YieldReturn(value) { ILRange = oldInst.ILRange });
ConvertBranchAfterYieldReturn(newBlock, oldBlock, oldInst.ChildIndex); ConvertBranchAfterYieldReturn(newBlock, oldBlock, oldInst.ChildIndex + 1);
break; // we're done with this basic block break; // we're done with this basic block
} }
} else if (oldInst is Call call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis() } else if (oldInst is Call call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis()
@ -687,16 +709,51 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}); });
return newBody; return newBody;
void ConvertBranchAfterYieldReturn(Block newBlock, Block oldBlock, int i) void ConvertBranchAfterYieldReturn(Block newBlock, Block oldBlock, int pos)
{ {
if (!(oldBlock.Instructions[i + 1].MatchStFld(out var target, out var field, out var value) Block targetBlock;
if (isCompiledWithMono && disposingField != null) {
// Mono skips over the state assignment if 'this.disposing' is set:
// ...
// stfld $current(ldloc this, yield-expr)
// if (ldfld $disposing(ldloc this)) br IL_007c
// br targetBlock
// }
//
// Block targetBlock (incoming: 1) {
// stfld $PC(ldloc this, ldc.i4 1)
// br setSkipFinallyBodies
// }
//
// Block setSkipFinallyBodies (incoming: 2) {
// stloc skipFinallyBodies(ldc.i4 1)
// br returnBlock
// }
if (oldBlock.Instructions[pos].MatchIfInstruction(out var condition, out _)
&& condition.MatchLdFld(out var condTarget, out var condField)
&& condTarget.MatchLdThis() && condField.MemberDefinition.Equals(disposingField)
&& oldBlock.Instructions[pos + 1].MatchBranch(out targetBlock)
&& targetBlock.Parent == oldBlock.Parent) {
// Keep looking at the target block:
oldBlock = targetBlock;
pos = 0;
}
}
if (oldBlock.Instructions[pos].MatchStFld(out var target, out var field, out var value)
&& target.MatchLdThis() && target.MatchLdThis()
&& field.MemberDefinition == stateField && field.MemberDefinition == stateField
&& value.MatchLdcI4(out int newState))) { && value.MatchLdcI4(out int newState)) {
pos++;
} else {
newBlock.Instructions.Add(new InvalidBranch("Unable to find new state assignment for yield return")); newBlock.Instructions.Add(new InvalidBranch("Unable to find new state assignment for yield return"));
return; return;
} }
int pos = i + 2; // Mono may have 'br setSkipFinallyBodies' here, so follow the branch
if (oldBlock.Instructions[pos].MatchBranch(out targetBlock) && targetBlock.Parent == oldBlock.Parent) {
oldBlock = targetBlock;
pos = 0;
}
if (oldBlock.Instructions[pos].MatchStLoc(skipFinallyBodies, out value)) { if (oldBlock.Instructions[pos].MatchStLoc(skipFinallyBodies, out value)) {
if (!value.MatchLdcI4(1)) { if (!value.MatchLdcI4(1)) {
newBlock.Instructions.Add(new InvalidExpression { newBlock.Instructions.Add(new InvalidExpression {
@ -706,9 +763,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
pos++; pos++;
} }
if (oldBlock.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdcI4(1)) { if (oldBlock.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdcI4(1)) {
// OK, found return directly after state assignment // OK, found return directly after state assignment
} else if (oldBlock.Instructions[pos].MatchBranch(out var targetBlock) } else if (oldBlock.Instructions[pos].MatchBranch(out targetBlock)
&& targetBlock.Instructions[0].MatchReturn(out retVal) && retVal.MatchLdcI4(1)) { && targetBlock.Instructions[0].MatchReturn(out retVal) && retVal.MatchLdcI4(1)) {
// OK, jump to common return block (e.g. on Mono) // OK, jump to common return block (e.g. on Mono)
} else { } else {
@ -782,7 +840,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary> /// <summary>
/// Translates all field accesses in `function` to local variable accesses. /// Translates all field accesses in `function` to local variable accesses.
/// </summary> /// </summary>
internal static void TranslateFieldsToLocalAccess(ILFunction function, ILInstruction inst, Dictionary<IField, ILVariable> fieldToVariableMap) internal static void TranslateFieldsToLocalAccess(ILFunction function, ILInstruction inst, Dictionary<IField, ILVariable> fieldToVariableMap, bool isCompiledWithMono = false)
{ {
if (inst is LdFlda ldflda && ldflda.Target.MatchLdThis()) { if (inst is LdFlda ldflda && ldflda.Target.MatchLdThis()) {
var fieldDef = (IField)ldflda.Field.MemberDefinition; var fieldDef = (IField)ldflda.Field.MemberDefinition;
@ -804,11 +862,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} else { } else {
inst.ReplaceWith(new LdLoca(v) { ILRange = inst.ILRange }); inst.ReplaceWith(new LdLoca(v) { ILRange = inst.ILRange });
} }
} else if (inst.MatchLdThis()) { } else if (!isCompiledWithMono && 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 {
foreach (var child in inst.Children) { foreach (var child in inst.Children) {
TranslateFieldsToLocalAccess(function, child, fieldToVariableMap); TranslateFieldsToLocalAccess(function, child, fieldToVariableMap, isCompiledWithMono);
} }
if (inst is LdObj ldobj && ldobj.Target is LdLoca ldloca && ldloca.Variable.StateMachineField != null) { if (inst is LdObj ldobj && ldobj.Target is LdLoca ldloca && ldloca.Variable.StateMachineField != null) {
LdLoc ldloc = new LdLoc(ldloca.Variable); LdLoc ldloc = new LdLoc(ldloca.Variable);

11
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILFunction f = TransformDelegateConstruction(call, out ILInstruction target); ILFunction f = TransformDelegateConstruction(call, out ILInstruction target);
if (f != null) { if (f != null) {
call.ReplaceWith(f); call.ReplaceWith(f);
if (target is IInstructionWithVariableOperand && !target.MatchLdThis()) if (target is IInstructionWithVariableOperand)
targetsToReplace.Add((IInstructionWithVariableOperand)target); targetsToReplace.Add((IInstructionWithVariableOperand)target);
} }
} }
@ -269,6 +269,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStLoc(StLoc inst) protected internal override void VisitStLoc(StLoc inst)
{ {
base.VisitStLoc(inst); base.VisitStLoc(inst);
if (targetLoad is ILInstruction instruction && instruction.MatchLdThis())
return;
if (inst.Variable == targetLoad.Variable) if (inst.Variable == targetLoad.Variable)
orphanedVariableInits.Add(inst); orphanedVariableInits.Add(inst);
if (MatchesTargetOrCopyLoad(inst.Value)) { if (MatchesTargetOrCopyLoad(inst.Value)) {
@ -285,7 +287,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStObj(StObj inst) protected internal override void VisitStObj(StObj inst)
{ {
base.VisitStObj(inst); base.VisitStObj(inst);
if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field) || !MatchesTargetOrCopyLoad(target)) if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field) || !MatchesTargetOrCopyLoad(target) || target.MatchLdThis())
return; return;
field = (IField)field.MemberDefinition; field = (IField)field.MemberDefinition;
ILInstruction value; ILInstruction value;
@ -321,6 +323,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitLdFlda(LdFlda inst) protected internal override void VisitLdFlda(LdFlda inst)
{ {
base.VisitLdFlda(inst); base.VisitLdFlda(inst);
if (inst.Target.MatchLdThis() && inst.Field.Name == "$this"
&& inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) {
var variable = currentFunction.Variables.First((f) => f.Index == -1);
inst.ReplaceWith(new LdLoca(variable) { ILRange = inst.ILRange });
}
if (inst.Parent is LdObj || inst.Parent is StObj) if (inst.Parent is LdObj || inst.Parent is StObj)
return; return;
if (!MatchesTargetOrCopyLoad(inst.Target)) if (!MatchesTargetOrCopyLoad(inst.Target))

Loading…
Cancel
Save