@ -204,7 +204,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
///
///
/// Within a try block, <c>currentStateOnException == stateOnException[tryBlock.Parent]</c>.
/// Within a try block, <c>currentStateOnException == stateOnException[tryBlock.Parent]</c>.
/// </summary>
/// </summary>
State currentStateOnException ;
/// <seealso cref="PropagateStateOnException"/>
protected State currentStateOnException ;
/// <summary>
/// <summary>
/// Creates a new DataFlowVisitor.
/// Creates a new DataFlowVisitor.
@ -216,7 +217,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
this . bottomState = initialState . Clone ( ) ;
this . bottomState = initialState . Clone ( ) ;
this . bottomState . ReplaceWithBottom ( ) ;
this . bottomState . ReplaceWithBottom ( ) ;
Debug . Assert ( bottomState . IsBottom ) ;
Debug . Assert ( bottomState . IsBottom ) ;
this . currentStateOnException = bottomS tate. Clone ( ) ;
this . currentStateOnException = s tate. Clone ( ) ;
}
}
#if DEBUG
#if DEBUG
@ -236,6 +237,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
debugDict . Add ( inst , state . Clone ( ) ) ;
debugDict . Add ( inst , state . Clone ( ) ) ;
}
}
}
}
// currentStateOnException should be all states within the try block joined together
// -> state should already have been joined into currentStateOnException.
Debug . Assert ( state . LessThanOrEqual ( currentStateOnException ) ) ;
#endif
#endif
}
}
#endif
#endif
@ -271,10 +276,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
"Unreachable code must be in the bottom state." ) ;
"Unreachable code must be in the bottom state." ) ;
}
}
// If this instruction can throw an exception, handle the exceptional control flow edge.
if ( ( inst . DirectFlags & InstructionFlags . MayThrow ) ! = 0 ) {
MayThrow ( ) ;
}
DebugEndPoint ( inst ) ;
DebugEndPoint ( inst ) ;
}
}
@ -282,7 +283,18 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// Handle control flow when the current instruction throws an exception:
/// Handle control flow when the current instruction throws an exception:
/// joins the current state into the "exception state" of the current try block.
/// joins the current state into the "exception state" of the current try block.
/// </summary>
/// </summary>
protected void MayThrow ( )
/// <remarks>
/// This should not only be called for instructions that may throw an exception,
/// but for all instructions (due to async exceptions like ThreadAbortException)!
///
/// To avoid redundant calls, every Visit() call may assume that the current state
/// is already propagated, and has to guarantee the same at the end.
/// This means this method should be called after every state change.
/// Alternatively, derived classes may directly modify both <c>state</c>
/// and <c>currentStateOnException</c>, so that a full <c>JoinWith()</c> call
/// is not necessary.
/// </remarks>
protected void PropagateStateOnException ( )
{
{
currentStateOnException . JoinWith ( state ) ;
currentStateOnException . JoinWith ( state ) ;
}
}
@ -409,13 +421,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected internal override void VisitThrow ( Throw inst )
protected internal override void VisitThrow ( Throw inst )
{
{
inst . Argument . AcceptVisitor ( this ) ;
inst . Argument . AcceptVisitor ( this ) ;
MayThrow ( ) ;
MarkUnreachable ( ) ;
MarkUnreachable ( ) ;
}
}
protected internal override void VisitRethrow ( Rethrow inst )
protected internal override void VisitRethrow ( Rethrow inst )
{
{
MayThrow ( ) ;
PropagateStateOnException ( ) ;
MarkUnreachable ( ) ;
MarkUnreachable ( ) ;
}
}
@ -434,14 +445,20 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
State oldStateOnException = currentStateOnException ;
State oldStateOnException = currentStateOnException ;
State newStateOnException ;
State newStateOnException ;
if ( ! stateOnException . TryGetValue ( inst , out newStateOnException ) ) {
if ( ! stateOnException . TryGetValue ( inst , out newStateOnException ) ) {
newStateOnException = bottomS tate. Clone ( ) ;
newStateOnException = s tate. Clone ( ) ;
stateOnException . Add ( inst , newStateOnException ) ;
stateOnException . Add ( inst , newStateOnException ) ;
}
}
currentStateOnException = newStateOnException ;
currentStateOnException = newStateOnException ;
inst . TryBlock . AcceptVisitor ( this ) ;
inst . TryBlock . AcceptVisitor ( this ) ;
// swap back to the old object instance
currentStateOnException = oldStateOnException ;
currentStateOnException = oldStateOnException ;
// No matter what kind of try-instruction this is, it's possible
// that an async exception is thrown immediately in the handler block,
// so propagate the state:
oldStateOnException . JoinWith ( newStateOnException ) ;
return newStateOnException ;
return newStateOnException ;
}
}
@ -450,8 +467,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
DebugStartPoint ( inst ) ;
DebugStartPoint ( inst ) ;
State onException = HandleTryBlock ( inst ) ;
State onException = HandleTryBlock ( inst ) ;
State endpoint = state . Clone ( ) ;
State endpoint = state . Clone ( ) ;
// The exception might get propagated if no handler matches the type:
currentStateOnException . JoinWith ( onException ) ;
foreach ( var handler in inst . Handlers ) {
foreach ( var handler in inst . Handlers ) {
state . ReplaceWith ( onException ) ;
state . ReplaceWith ( onException ) ;
BeginTryCatchHandler ( handler ) ;
BeginTryCatchHandler ( handler ) ;
@ -489,7 +504,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
State onSuccess = state . Clone ( ) ;
State onSuccess = state . Clone ( ) ;
state . JoinWith ( onException ) ;
state . JoinWith ( onException ) ;
inst . FinallyBlock . AcceptVisitor ( this ) ;
inst . FinallyBlock . AcceptVisitor ( this ) ;
MayThrow ( ) ;
PropagateStateOnException ( ) ;
// Use MeetWith() to ensure points after the try-finally are reachable only if both the
// Use MeetWith() 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 ) ;
state . MeetWith ( onSuccess ) ;
@ -505,7 +520,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
State onSuccess = state ;
State onSuccess = state ;
state = onException ;
state = onException ;
inst . FaultBlock . AcceptVisitor ( this ) ;
inst . FaultBlock . AcceptVisitor ( this ) ;
MayThrow ( ) ; // rethrow the exception after the fault block
PropagateStateOnException ( ) ; // rethrow the exception after the fault block
// try-fault exits normally only if no exception occurred
// try-fault exits normally only if no exception occurred
state = onSuccess ;
state = onSuccess ;