|
|
@ -110,23 +110,18 @@ namespace ICSharpCode.Decompiler.FlowAnalysis |
|
|
|
/// <code>this.isReachable |= incomingState.isReachable;</code>
|
|
|
|
/// <code>this.isReachable |= incomingState.isReachable;</code>
|
|
|
|
/// </example>
|
|
|
|
/// </example>
|
|
|
|
void JoinWith(Self incomingState); |
|
|
|
void JoinWith(Self incomingState); |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// The meet operation.
|
|
|
|
/// A special operation to merge the end-state of the finally-block with the end state of
|
|
|
|
|
|
|
|
/// a branch leaving the try-block.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// If possible, this method sets <c>this</c> to the greatest state that is smaller than (or equal to)
|
|
|
|
/// If either input state is unreachable, this call must result in an unreachable state.
|
|
|
|
/// both input states.
|
|
|
|
|
|
|
|
/// At a minimum, meeting with an unreachable state must result in an unreachable state.
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
|
|
|
|
/// <c>MeetWith()</c> is used when control flow passes out of a try-finally construct: the endpoint of the try-finally
|
|
|
|
|
|
|
|
/// is reachable only if both the endpoint of the <c>try</c> and the endpoint of the <c>finally</c> blocks are reachable.
|
|
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
|
|
/// <example>
|
|
|
|
/// <example>
|
|
|
|
/// The simple state "<c>bool isReachable</c>", would implement <c>MeetWith</c> as:
|
|
|
|
/// The simple state "<c>bool isReachable</c>", would implement <c>TriggerFinally</c> as:
|
|
|
|
/// <code>this.isReachable &= incomingState.isReachable;</code>
|
|
|
|
/// <code>this.isReachable &= finallyState.isReachable;</code>
|
|
|
|
/// </example>
|
|
|
|
/// </example>
|
|
|
|
void MeetWith(Self incomingState); |
|
|
|
void TriggerFinally(Self finallyState); |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets whether this is the bottom state.
|
|
|
|
/// Gets whether this is the bottom state.
|
|
|
@ -400,35 +395,56 @@ namespace ICSharpCode.Decompiler.FlowAnalysis |
|
|
|
DebugEndPoint(container); |
|
|
|
DebugEndPoint(container); |
|
|
|
workLists.Remove(container); |
|
|
|
workLists.Remove(container); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readonly List<(IBranchOrLeaveInstruction, State)> branchesTriggeringFinally = new List<(IBranchOrLeaveInstruction, State)>(); |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitBranch(Branch inst) |
|
|
|
protected internal override void VisitBranch(Branch inst) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (inst.TriggersFinallyBlock) { |
|
|
|
|
|
|
|
Debug.Assert(state.LessThanOrEqual(currentStateOnException)); |
|
|
|
|
|
|
|
branchesTriggeringFinally.Add((inst, state.Clone())); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
MergeBranchStateIntoTargetBlock(inst, state); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
MarkUnreachable(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MergeBranchStateIntoTargetBlock(Branch inst, State branchState) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var targetBlock = inst.TargetBlock; |
|
|
|
var targetBlock = inst.TargetBlock; |
|
|
|
var targetState = GetBlockInputState(targetBlock); |
|
|
|
var targetState = GetBlockInputState(targetBlock); |
|
|
|
if (!state.LessThanOrEqual(targetState)) { |
|
|
|
if (!branchState.LessThanOrEqual(targetState)) { |
|
|
|
targetState.JoinWith(state); |
|
|
|
targetState.JoinWith(branchState); |
|
|
|
|
|
|
|
|
|
|
|
BlockContainer container = (BlockContainer)targetBlock.Parent; |
|
|
|
BlockContainer container = (BlockContainer)targetBlock.Parent; |
|
|
|
workLists[container].Add(targetBlock.ChildIndex); |
|
|
|
workLists[container].Add(targetBlock.ChildIndex); |
|
|
|
} |
|
|
|
} |
|
|
|
MarkUnreachable(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitLeave(Leave inst) |
|
|
|
protected internal override void VisitLeave(Leave inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
inst.Value.AcceptVisitor(this); |
|
|
|
inst.Value.AcceptVisitor(this); |
|
|
|
State targetState; |
|
|
|
if (inst.TriggersFinallyBlock) { |
|
|
|
if (stateOnLeave.TryGetValue(inst.TargetContainer, out targetState)) { |
|
|
|
Debug.Assert(state.LessThanOrEqual(currentStateOnException)); |
|
|
|
targetState.JoinWith(state); |
|
|
|
branchesTriggeringFinally.Add((inst, state.Clone())); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
MergeBranchStateIntoStateOnLeave(inst, state); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
MarkUnreachable(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MergeBranchStateIntoStateOnLeave(Leave inst, State branchState) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (stateOnLeave.TryGetValue(inst.TargetContainer, out State targetState)) { |
|
|
|
|
|
|
|
targetState.JoinWith(branchState); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
stateOnLeave.Add(inst.TargetContainer, state.Clone()); |
|
|
|
stateOnLeave.Add(inst.TargetContainer, branchState.Clone()); |
|
|
|
} |
|
|
|
} |
|
|
|
// Note: We don't have to put the block container onto the work queue,
|
|
|
|
// Note: We don't have to put the block container onto the work queue,
|
|
|
|
// because it's an ancestor of the Leave instruction, and hence
|
|
|
|
// because it's an ancestor of the Leave instruction, and hence
|
|
|
|
// we are currently somewhere within the VisitBlockContainer() call.
|
|
|
|
// we are currently somewhere within the VisitBlockContainer() call.
|
|
|
|
MarkUnreachable(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitThrow(Throw inst) |
|
|
|
protected internal override void VisitThrow(Throw inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
inst.Argument.AcceptVisitor(this); |
|
|
|
inst.Argument.AcceptVisitor(this); |
|
|
@ -512,22 +528,57 @@ namespace ICSharpCode.Decompiler.FlowAnalysis |
|
|
|
{ |
|
|
|
{ |
|
|
|
throw new NotSupportedException(); |
|
|
|
throw new NotSupportedException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitTryFinally(TryFinally inst) |
|
|
|
protected internal override void VisitTryFinally(TryFinally inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
DebugStartPoint(inst); |
|
|
|
DebugStartPoint(inst); |
|
|
|
|
|
|
|
int branchesTriggeringFinallyOldCount = branchesTriggeringFinally.Count; |
|
|
|
// At first, handle 'try { .. } finally { .. }' like 'try { .. } catch {} .. if (?) rethrow; }'
|
|
|
|
// At first, handle 'try { .. } finally { .. }' like 'try { .. } catch {} .. if (?) rethrow; }'
|
|
|
|
State onException = HandleTryBlock(inst); |
|
|
|
State onException = HandleTryBlock(inst); |
|
|
|
State onSuccess = state.Clone(); |
|
|
|
State onSuccess = state.Clone(); |
|
|
|
state.JoinWith(onException); |
|
|
|
state.JoinWith(onException); |
|
|
|
inst.FinallyBlock.AcceptVisitor(this); |
|
|
|
inst.FinallyBlock.AcceptVisitor(this); |
|
|
|
PropagateStateOnException(); |
|
|
|
//PropagateStateOnException(); // rethrow the exception after the finally block -- should be redundant
|
|
|
|
// Use MeetWith() to ensure points after the try-finally are reachable only if both the
|
|
|
|
Debug.Assert(state.LessThanOrEqual(currentStateOnException)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ProcessBranchesLeavingTryFinally(inst, branchesTriggeringFinallyOldCount); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Use TriggerFinally() to ensure points after the try-finally are reachable only if both the
|
|
|
|
// try and the finally endpoints are reachable.
|
|
|
|
// try and the finally endpoints are reachable.
|
|
|
|
state.MeetWith(onSuccess); |
|
|
|
onSuccess.TriggerFinally(state); |
|
|
|
|
|
|
|
state = onSuccess; |
|
|
|
DebugEndPoint(inst); |
|
|
|
DebugEndPoint(inst); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
|
|
/// Process branches leaving the try-finally,
|
|
|
|
|
|
|
|
/// * Calls TriggerFinally() on each branchesTriggeringFinally
|
|
|
|
|
|
|
|
/// * Removes entries from branchesTriggeringFinally if they won't trigger additional finally blocks.
|
|
|
|
|
|
|
|
/// * After all finallies are applied, the branch state is merged into the target block.
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
void ProcessBranchesLeavingTryFinally(TryFinally tryFinally, int branchesTriggeringFinallyOldCount) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int outPos = branchesTriggeringFinallyOldCount; |
|
|
|
|
|
|
|
for (int i = branchesTriggeringFinallyOldCount; i < branchesTriggeringFinally.Count; ++i) { |
|
|
|
|
|
|
|
var (branch, stateOnBranch) = branchesTriggeringFinally[i]; |
|
|
|
|
|
|
|
Debug.Assert(((ILInstruction)branch).IsDescendantOf(tryFinally)); |
|
|
|
|
|
|
|
Debug.Assert(tryFinally.IsDescendantOf(branch.TargetContainer)); |
|
|
|
|
|
|
|
stateOnBranch.TriggerFinally(state); |
|
|
|
|
|
|
|
bool triggersAnotherFinally = Branch.GetExecutesFinallyBlock(tryFinally, branch.TargetContainer); |
|
|
|
|
|
|
|
if (triggersAnotherFinally) { |
|
|
|
|
|
|
|
branchesTriggeringFinally[outPos++] = (branch, stateOnBranch); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Merge state into target block.
|
|
|
|
|
|
|
|
if (branch is Leave leave) { |
|
|
|
|
|
|
|
MergeBranchStateIntoStateOnLeave((Leave)branch, stateOnBranch); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
MergeBranchStateIntoTargetBlock((Branch)branch, stateOnBranch); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
branchesTriggeringFinally.RemoveRange(outPos, branchesTriggeringFinally.Count - outPos); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitTryFault(TryFault inst) |
|
|
|
protected internal override void VisitTryFault(TryFault inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
DebugStartPoint(inst); |
|
|
|
DebugStartPoint(inst); |
|
|
@ -537,8 +588,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis |
|
|
|
State onSuccess = state; |
|
|
|
State onSuccess = state; |
|
|
|
state = onException; |
|
|
|
state = onException; |
|
|
|
inst.FaultBlock.AcceptVisitor(this); |
|
|
|
inst.FaultBlock.AcceptVisitor(this); |
|
|
|
PropagateStateOnException(); // rethrow the exception after the fault block
|
|
|
|
//PropagateStateOnException(); // rethrow the exception after the fault block
|
|
|
|
|
|
|
|
Debug.Assert(state.LessThanOrEqual(currentStateOnException)); |
|
|
|
|
|
|
|
|
|
|
|
// try-fault exits normally only if no exception occurred
|
|
|
|
// try-fault exits normally only if no exception occurred
|
|
|
|
state = onSuccess; |
|
|
|
state = onSuccess; |
|
|
|
DebugEndPoint(inst); |
|
|
|
DebugEndPoint(inst); |
|
|
@ -579,5 +631,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis |
|
|
|
inst.Value.AcceptVisitor(this); |
|
|
|
inst.Value.AcceptVisitor(this); |
|
|
|
DebugEndPoint(inst); |
|
|
|
DebugEndPoint(inst); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitILFunction(ILFunction function) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|