@@ -405,15 +405,36 @@ public class Block
405405 internal bool _returns ;
406406 internal bool _unreachable ;
407407
408+ // Only Entry block, that can be constructed via NewEntryBlock() is reachable initially.
409+ // all other blocks are unreachable.
410+ // reachability of block should be proved with FlowsTo() calls.
411+ public Block ( )
412+ {
413+ this . _unreachable = true ;
414+ }
415+
416+ public static Block NewEntryBlock ( )
417+ {
418+ return new Block ( unreachable : false ) ;
419+ }
420+
421+ private Block ( bool unreachable )
422+ {
423+ this . _unreachable = unreachable ;
424+ }
425+
426+ /// <summary>
427+ /// Tell flow analysis that this block can flow to next block.
428+ /// </summary>
429+ /// <param name="next"></param>
408430 internal void FlowsTo ( Block next )
409431 {
410432 if ( _successors . IndexOf ( next ) < 0 )
411433 {
412- if ( _unreachable )
434+ if ( ! _unreachable )
413435 {
414- next . _unreachable = true ;
436+ next . _unreachable = false ;
415437 }
416-
417438 _successors . Add ( next ) ;
418439 next . _predecessors . Add ( this ) ;
419440 }
@@ -725,6 +746,25 @@ internal static void RenameVariables(Block block)
725746 internal static void InitializeSSA ( Dictionary < string , VariableAnalysisDetails > VariableAnalysis , Block Entry , List < Block > Blocks )
726747 {
727748 VariablesDictionary = new Dictionary < string , VariableAnalysisDetails > ( StringComparer . OrdinalIgnoreCase ) ;
749+
750+ foreach ( var block in Blocks )
751+ {
752+ List < Block > _unreachables = new List < Block > ( ) ;
753+ foreach ( var pred in block . _predecessors )
754+ {
755+ if ( pred . _unreachable )
756+ {
757+ _unreachables . Add ( pred ) ;
758+ pred . _successors . Remove ( block ) ;
759+ }
760+ }
761+
762+ foreach ( var pred in _unreachables )
763+ {
764+ block . _predecessors . Remove ( pred ) ;
765+ }
766+ }
767+
728768 InternalVariablesDictionary . Clear ( ) ;
729769 SSADictionary . Clear ( ) ;
730770 Counters . Clear ( ) ;
@@ -1877,7 +1917,7 @@ public Block Exit
18771917 /// </summary>
18781918 public void Init ( )
18791919 {
1880- _entryBlock = new Block ( ) ;
1920+ _entryBlock = Block . NewEntryBlock ( ) ;
18811921 _exitBlock = new Block ( ) ;
18821922 }
18831923
@@ -2057,6 +2097,13 @@ public object VisitIfStatement(IfStatementAst ifStmtAst)
20572097 if ( ifStmtAst == null ) return null ;
20582098
20592099 Block afterStmt = new Block ( ) ;
2100+
2101+ if ( ifStmtAst . ElseClause == null )
2102+ {
2103+ // There is no else, flow can go straight to afterStmt.
2104+ _currentBlock . FlowsTo ( afterStmt ) ;
2105+ }
2106+
20602107 int clauseCount = ifStmtAst . Clauses . Count ;
20612108 for ( int i = 0 ; i < clauseCount ; i ++ )
20622109 {
@@ -2403,52 +2450,62 @@ public object VisitTryStatement(TryStatementAst tryStatementAst)
24032450
24042451 tryStatementAst . Body . Visit ( this . Decorator ) ;
24052452
2406- // This is either the first block in the finally, or the first block after all the catches if there is no finally.
2407- // For return analysis, we start off assuming this block is reachable only if the end of the try body
2408- // is reachable, but that can change if we find a catch that falls through.
2409- var afterTry = new Block { _unreachable = _currentBlock . _unreachable } ;
2453+ Block lastBlockInTry = _currentBlock ;
2454+ var finallyFirstBlock = tryStatementAst . Finally == null ? null : new Block ( ) ;
2455+ Block finallyLastBlock = null ;
2456+
2457+ // This is the first block after all the catches and finally (if present).
2458+ var afterTry = new Block ( ) ;
2459+
2460+ bool isCatchAllPresent = false ;
24102461
24112462 foreach ( var catchAst in tryStatementAst . CatchClauses )
24122463 {
2464+ if ( catchAst . IsCatchAll )
2465+ {
2466+ isCatchAllPresent = true ;
2467+ }
2468+
24132469 // Any statement in the try block could throw and reach the catch, so assume the worst (from a data
24142470 // flow perspective) and make the predecessor to the catch the block before entering the try.
24152471 _currentBlock = new Block ( ) ;
24162472 blockBeforeTry . FlowsTo ( _currentBlock ) ;
2417-
24182473 catchAst . Visit ( this . Decorator ) ;
2419-
2420- if ( ! _currentBlock . _unreachable )
2421- {
2422- // The last block of the catch falls through, so we can get reach blocks after the try.
2423- afterTry . _unreachable = false ;
2424- }
2474+ _currentBlock . FlowsTo ( finallyFirstBlock ?? afterTry ) ;
24252475 }
24262476
2427- // Assume nothing in the try was executed, so flow comes from before the try. We could have the catch blocks
2428- // also flow to this block, but that won't improve the analysis any, so skip that.
2429- _currentBlock = afterTry ;
2430- blockBeforeTry . FlowsTo ( _currentBlock ) ;
2431-
2432- if ( tryStatementAst . Finally != null )
2477+ if ( finallyFirstBlock != null )
24332478 {
2479+ lastBlockInTry . FlowsTo ( finallyFirstBlock ) ;
2480+
2481+ _currentBlock = finallyFirstBlock ;
24342482 tryStatementAst . Finally . Visit ( this . Decorator ) ;
2483+ _currentBlock . FlowsTo ( afterTry ) ;
2484+
2485+ finallyLastBlock = _currentBlock ;
24352486
2436- if ( ! _currentBlock . _unreachable )
2487+ // For finally block, there are 2 cases: when try-body throw and when it doesn't.
2488+ // For these two cases value of 'finallyLastBlock._throws' would be different.
2489+ if ( ! isCatchAllPresent )
24372490 {
2438- // If the finally throws (it can't have other flow like return, break, or continue)
2439- // then after the try/catch/finally is unreachable, otherwise it is reachable.
2440- afterTry . _unreachable = false ;
2491+ // This flow exist only, if there is no catch for all exceptions.
2492+ blockBeforeTry . FlowsTo ( finallyFirstBlock ) ;
2493+
2494+ var rethrowAfterFinallyBlock = new Block ( ) ;
2495+ finallyLastBlock . FlowsTo ( rethrowAfterFinallyBlock ) ;
2496+ rethrowAfterFinallyBlock . _throws = true ;
2497+ rethrowAfterFinallyBlock . FlowsTo ( _exitBlock ) ;
24412498 }
2442- // We can assume that flow from the finally reaches the code following the finally. Of course, if an exception occurs,
2443- // then the code can't be reached, but our conservative data flow modeling assures us correctness, the exception will
2444- // either leave the current function (meaning it doesn't matter that we assumed flow reached after the finally), or
2445- // it will be caught and handled by a containing catch, and our conversative data flow ensures the correctness of the
2446- // generated code.
24472499
2448- var newBlock = new Block ( ) ;
2449- _currentBlock . FlowsTo ( newBlock ) ;
2450- _currentBlock = newBlock ;
2500+ // This flow always exists.
2501+ finallyLastBlock . FlowsTo ( afterTry ) ;
24512502 }
2503+ else
2504+ {
2505+ lastBlockInTry . FlowsTo ( afterTry ) ;
2506+ }
2507+
2508+ _currentBlock = afterTry ;
24522509
24532510 return null ;
24542511 }
@@ -2486,7 +2543,7 @@ where t.Label.Equals(labelStrAst.Value, StringComparison.OrdinalIgnoreCase)
24862543 }
24872544
24882545 // The next block is unreachable, but is necessary to keep the flow graph correct.
2489- _currentBlock = new Block { _unreachable = true } ;
2546+ _currentBlock = new Block ( ) ;
24902547 }
24912548
24922549 /// <summary>
@@ -2525,7 +2582,7 @@ internal Block ControlFlowStatement(PipelineBaseAst pipelineAst)
25252582 var lastBlockInStatement = _currentBlock ;
25262583
25272584 // The next block is unreachable, but is necessary to keep the flow graph correct.
2528- _currentBlock = new Block { _unreachable = true } ;
2585+ _currentBlock = new Block ( ) ;
25292586 return lastBlockInStatement ;
25302587 }
25312588
0 commit comments