@@ -405,15 +405,36 @@ public class Block
405
405
internal bool _returns ;
406
406
internal bool _unreachable ;
407
407
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>
408
430
internal void FlowsTo ( Block next )
409
431
{
410
432
if ( _successors . IndexOf ( next ) < 0 )
411
433
{
412
- if ( _unreachable )
434
+ if ( ! _unreachable )
413
435
{
414
- next . _unreachable = true ;
436
+ next . _unreachable = false ;
415
437
}
416
-
417
438
_successors . Add ( next ) ;
418
439
next . _predecessors . Add ( this ) ;
419
440
}
@@ -725,6 +746,25 @@ internal static void RenameVariables(Block block)
725
746
internal static void InitializeSSA ( Dictionary < string , VariableAnalysisDetails > VariableAnalysis , Block Entry , List < Block > Blocks )
726
747
{
727
748
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
+
728
768
InternalVariablesDictionary . Clear ( ) ;
729
769
SSADictionary . Clear ( ) ;
730
770
Counters . Clear ( ) ;
@@ -1877,7 +1917,7 @@ public Block Exit
1877
1917
/// </summary>
1878
1918
public void Init ( )
1879
1919
{
1880
- _entryBlock = new Block ( ) ;
1920
+ _entryBlock = Block . NewEntryBlock ( ) ;
1881
1921
_exitBlock = new Block ( ) ;
1882
1922
}
1883
1923
@@ -2057,6 +2097,13 @@ public object VisitIfStatement(IfStatementAst ifStmtAst)
2057
2097
if ( ifStmtAst == null ) return null ;
2058
2098
2059
2099
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
+
2060
2107
int clauseCount = ifStmtAst . Clauses . Count ;
2061
2108
for ( int i = 0 ; i < clauseCount ; i ++ )
2062
2109
{
@@ -2403,52 +2450,62 @@ public object VisitTryStatement(TryStatementAst tryStatementAst)
2403
2450
2404
2451
tryStatementAst . Body . Visit ( this . Decorator ) ;
2405
2452
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 ;
2410
2461
2411
2462
foreach ( var catchAst in tryStatementAst . CatchClauses )
2412
2463
{
2464
+ if ( catchAst . IsCatchAll )
2465
+ {
2466
+ isCatchAllPresent = true ;
2467
+ }
2468
+
2413
2469
// Any statement in the try block could throw and reach the catch, so assume the worst (from a data
2414
2470
// flow perspective) and make the predecessor to the catch the block before entering the try.
2415
2471
_currentBlock = new Block ( ) ;
2416
2472
blockBeforeTry . FlowsTo ( _currentBlock ) ;
2417
-
2418
2473
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 ) ;
2425
2475
}
2426
2476
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 )
2433
2478
{
2479
+ lastBlockInTry . FlowsTo ( finallyFirstBlock ) ;
2480
+
2481
+ _currentBlock = finallyFirstBlock ;
2434
2482
tryStatementAst . Finally . Visit ( this . Decorator ) ;
2483
+ _currentBlock . FlowsTo ( afterTry ) ;
2484
+
2485
+ finallyLastBlock = _currentBlock ;
2435
2486
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 )
2437
2490
{
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 ) ;
2441
2498
}
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.
2447
2499
2448
- var newBlock = new Block ( ) ;
2449
- _currentBlock . FlowsTo ( newBlock ) ;
2450
- _currentBlock = newBlock ;
2500
+ // This flow always exists.
2501
+ finallyLastBlock . FlowsTo ( afterTry ) ;
2451
2502
}
2503
+ else
2504
+ {
2505
+ lastBlockInTry . FlowsTo ( afterTry ) ;
2506
+ }
2507
+
2508
+ _currentBlock = afterTry ;
2452
2509
2453
2510
return null ;
2454
2511
}
@@ -2486,7 +2543,7 @@ where t.Label.Equals(labelStrAst.Value, StringComparison.OrdinalIgnoreCase)
2486
2543
}
2487
2544
2488
2545
// The next block is unreachable, but is necessary to keep the flow graph correct.
2489
- _currentBlock = new Block { _unreachable = true } ;
2546
+ _currentBlock = new Block ( ) ;
2490
2547
}
2491
2548
2492
2549
/// <summary>
@@ -2525,7 +2582,7 @@ internal Block ControlFlowStatement(PipelineBaseAst pipelineAst)
2525
2582
var lastBlockInStatement = _currentBlock ;
2526
2583
2527
2584
// The next block is unreachable, but is necessary to keep the flow graph correct.
2528
- _currentBlock = new Block { _unreachable = true } ;
2585
+ _currentBlock = new Block ( ) ;
2529
2586
return lastBlockInStatement ;
2530
2587
}
2531
2588
0 commit comments