Browse Source

Merge branch 'PR1303'

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

106
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>();
@ -112,6 +116,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -112,6 +116,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
this.enumeratorCtor = default;
this.stateField = null;
this.currentField = null;
this.disposingField = null;
this.fieldToParameterMap.Clear();
this.finallyMethodToStateRange = null;
this.decompiledFinallyMethods.Clear();
@ -147,22 +152,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -147,22 +152,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
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) {
// 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>()) {
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();
ReconstructTryFinallyBlocks(function);
}
context.Step("Translate fields to local accesses", function);
TranslateFieldsToLocalAccess(function, function, fieldToParameterMap);
TranslateFieldsToLocalAccess(function, function, fieldToParameterMap, isCompiledWithMono);
CleanSkipFinallyBodies(function);
@ -459,18 +468,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -459,18 +468,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void ConstructExceptionTable()
{
if (isCompiledWithMono) {
disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "Dispose");
var function = CreateILAst(disposeMethod, context);
BlockContainer body = (BlockContainer)function.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;
return;
} 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");
var function = CreateILAst(disposeMethod, context);
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorDispose, stateField);
rangeAnalysis.AssignStateRanges(function.Body, LongSet.Universe);
finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
}
disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "System.IDisposable.Dispose");
var function = CreateILAst(disposeMethod, context);
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorDispose, stateField);
rangeAnalysis.AssignStateRanges(function.Body, LongSet.Universe);
finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
}
[Conditional("DEBUG")]
@ -633,7 +655,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -633,7 +655,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];
@ -656,7 +678,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -656,7 +678,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} else if (field.MemberDefinition.Equals(currentField)) {
// create yield return
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
}
} else if (oldInst is Call call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis()
@ -687,16 +709,51 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -687,16 +709,51 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
});
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()
&& 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"));
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 (!value.MatchLdcI4(1)) {
newBlock.Instructions.Add(new InvalidExpression {
@ -706,9 +763,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -706,9 +763,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
pos++;
}
if (oldBlock.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdcI4(1)) {
// 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)) {
// OK, jump to common return block (e.g. on Mono)
} else {
@ -782,7 +840,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -782,7 +840,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary>
/// Translates all field accesses in `function` to local variable accesses.
/// </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()) {
var fieldDef = (IField)ldflda.Field.MemberDefinition;
@ -804,11 +862,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -804,11 +862,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} else {
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 });
} else {
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) {
LdLoc ldloc = new LdLoc(ldloca.Variable);

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

@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILFunction f = TransformDelegateConstruction(call, out ILInstruction target);
if (f != null) {
call.ReplaceWith(f);
if (target is IInstructionWithVariableOperand && !target.MatchLdThis())
if (target is IInstructionWithVariableOperand)
targetsToReplace.Add((IInstructionWithVariableOperand)target);
}
}
@ -269,6 +269,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -269,6 +269,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStLoc(StLoc inst)
{
base.VisitStLoc(inst);
if (targetLoad is ILInstruction instruction && instruction.MatchLdThis())
return;
if (inst.Variable == targetLoad.Variable)
orphanedVariableInits.Add(inst);
if (MatchesTargetOrCopyLoad(inst.Value)) {
@ -285,7 +287,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -285,7 +287,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStObj(StObj 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;
field = (IField)field.MemberDefinition;
ILInstruction value;
@ -321,6 +323,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -321,6 +323,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitLdFlda(LdFlda 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)
return;
if (!MatchesTargetOrCopyLoad(inst.Target))

Loading…
Cancel
Save