@@ -27,15 +27,17 @@ private module Cached {
2727 cached
2828 newtype TSplitKind =
2929 TInitializerSplitKind ( ) or
30- TFinallySplitKind ( ) or
30+ TFinallySplitKind ( int nestLevel ) { nestLevel = FinallySplitting :: nestLevel ( _ ) } or
3131 TExceptionHandlerSplitKind ( ) or
3232 TBooleanSplitKind ( BooleanSplitting:: BooleanSplitSubKind kind ) { kind .startsSplit ( _) } or
3333 TLoopUnrollingSplitKind ( LoopUnrollingSplitting:: UnrollableLoopStmt loop )
3434
3535 cached
3636 newtype TSplit =
3737 TInitializerSplit ( Constructor c ) { InitializerSplitting:: constructorInitializes ( c , _) } or
38- TFinallySplit ( FinallySplitting:: FinallySplitType type ) or
38+ TFinallySplit ( FinallySplitting:: FinallySplitType type , int nestLevel ) {
39+ nestLevel = FinallySplitting:: nestLevel ( _)
40+ } or
3941 TExceptionHandlerSplit ( ExceptionClass ec ) or
4042 TBooleanSplit ( BooleanSplitting:: BooleanSplitSubKind kind , boolean branch ) {
4143 kind .startsSplit ( _) and
@@ -427,23 +429,31 @@ module FinallySplitting {
427429 )
428430 }
429431
432+ /**
433+ * Holds if `innerTry` has a `finally` block and is immediately nested inside the
434+ * `finally` block of `outerTry`.
435+ */
436+ private predicate nestedFinally ( TryStmt outerTry , TryStmt innerTry ) {
437+ exists ( ControlFlowElement innerFinally |
438+ innerFinally = getAChild ( getAFinallyDescendant ( outerTry ) , outerTry .getEnclosingCallable ( ) ) and
439+ innerFinally = innerTry .getFinally ( )
440+ )
441+ }
442+
443+ /** Gets the nesting level of the `finally` block for `try`. */
444+ int nestLevel ( TryStmt try ) { result = strictcount ( TryStmt outer | nestedFinally * ( outer , try ) ) }
445+
430446 /** A control flow element that belongs to a `finally` block. */
431447 private class FinallyControlFlowElement extends ControlFlowElement {
432- FinallyControlFlowElement ( ) { this = getAFinallyDescendant ( _ ) }
448+ private TryStmt try ;
433449
434- /** Holds if this node is the entry node in the `finally` block it belongs to. */
435- predicate isEntryNode ( ) {
436- exists ( TryStmt try | this = getAFinallyDescendant ( try ) | this = first ( try .getFinally ( ) ) )
437- }
450+ FinallyControlFlowElement ( ) { this = getAFinallyDescendant ( try ) }
438451
439- /**
440- * Holds if this node is a last element in the `finally` block belonging to
441- * `try` statement `try`, with completion `c`.
442- */
443- predicate isExitNode ( TryStmt try , Completion c ) {
444- this = getAFinallyDescendant ( try ) and
445- this = last ( try .getFinally ( ) , c )
446- }
452+ /** Gets the immediate `try` block that this node belongs to. */
453+ TryStmt getTryStmt ( ) { result = try }
454+
455+ /** Holds if this node is the entry node in the `finally` block it belongs to. */
456+ predicate isEntryNode ( ) { this = first ( try .getFinally ( ) ) }
447457 }
448458
449459 /** A control flow element that does not belong to a `finally` block. */
@@ -473,45 +483,60 @@ module FinallySplitting {
473483 */
474484 class FinallySplitImpl extends SplitImpl , TFinallySplit {
475485 private FinallySplitType type ;
486+ private int nestLevel ;
476487
477- FinallySplitImpl ( ) { this = TFinallySplit ( type ) }
488+ FinallySplitImpl ( ) { this = TFinallySplit ( type , nestLevel ) }
478489
479490 /**
480491 * Gets the type of this `finally` split, that is, how to continue execution after the
481492 * `finally` block.
482493 */
483494 FinallySplitType getType ( ) { result = type }
484495
496+ /** Gets the `finally` nesting level. */
497+ int getNestLevel ( ) { result = nestLevel }
498+
485499 override string toString ( ) {
486500 if type instanceof NormalSuccessor
487501 then result = ""
488- else result = "finally: " + type .toString ( )
502+ else
503+ if nestLevel > 1
504+ then result = "finally(" + nestLevel + "): " + type .toString ( )
505+ else result = "finally: " + type .toString ( )
489506 }
490507 }
491508
492- private class FinallySplitKind extends SplitKind , TFinallySplitKind {
493- override int getListOrder ( ) { result = InitializerSplitting:: getNextListOrder ( ) }
509+ private int getListOrder ( FinallySplitKind kind ) {
510+ result = InitializerSplitting:: getNextListOrder ( ) + kind .getNestLevel ( )
511+ }
494512
495- override string toString ( ) { result = "Finally" }
513+ int getNextListOrder ( ) {
514+ result = max ( int i | i = getListOrder ( _) + 1 or i = InitializerSplitting:: getNextListOrder ( ) )
496515 }
497516
498- int getNextListOrder ( ) { result = InitializerSplitting:: getNextListOrder ( ) + 1 }
517+ private class FinallySplitKind extends SplitKind , TFinallySplitKind {
518+ private int nestLevel ;
519+
520+ FinallySplitKind ( ) { this = TFinallySplitKind ( nestLevel ) }
521+
522+ /** Gets the `finally` nesting level. */
523+ int getNestLevel ( ) { result = nestLevel }
524+
525+ override int getListOrder ( ) { result = getListOrder ( this ) }
526+
527+ override string toString ( ) { result = "Finally (" + nestLevel + ")" }
528+ }
499529
500530 private class FinallySplitInternal extends SplitInternal , FinallySplitImpl {
501- override FinallySplitKind getKind ( ) { any ( ) }
531+ override FinallySplitKind getKind ( ) { result . getNestLevel ( ) = this . getNestLevel ( ) }
502532
503533 override predicate hasEntry ( ControlFlowElement pred , ControlFlowElement succ , Completion c ) {
504- succ .( FinallyControlFlowElement ) .isEntryNode ( ) and
534+ succ = any ( FinallyControlFlowElement entry |
535+ entry .isEntryNode ( ) and
536+ this .getNestLevel ( ) = nestLevel ( entry .getTryStmt ( ) )
537+ ) and
505538 succ = succ ( pred , c ) and
506- this .getType ( ) .isSplitForEntryCompletion ( c ) and
507- (
508- // Abnormal entry must enter the correct splitting
509- not c instanceof NormalCompletion
510- or
511- // Normal entry only when not entering a nested `finally` block; in that case,
512- // the outer split must be maintained (see `hasSuccessor()`)
513- pred instanceof NonFinallyControlFlowElement
514- )
539+ this .getType ( ) .isSplitForEntryCompletion ( c )
515540 }
516541
517542 override predicate hasEntry ( Callable c , ControlFlowElement succ ) { none ( ) }
@@ -525,53 +550,90 @@ module FinallySplitting {
525550 ( exists ( succ ( pred , _) ) or exists ( succExit ( pred , _) ) )
526551 }
527552
528- /** Holds if `pred` may exit this split with completion `c`. */
529- private predicate exit ( ControlFlowElement pred , Completion c ) {
553+ /**
554+ * Holds if `pred` may exit this split with completion `c`. The Boolean
555+ * `inherited` indicates whether `c` is an inherited completion from a `try`/
556+ * `catch` block.
557+ */
558+ private predicate exit ( ControlFlowElement pred , Completion c , boolean inherited ) {
530559 this .appliesToPredecessor ( pred ) and
531560 exists ( TryStmt try , FinallySplitType type |
532561 type = this .getType ( ) and
562+ nestLevel ( try ) = this .getNestLevel ( ) and
533563 pred = last ( try , c )
534564 |
535- if pred . ( FinallyControlFlowElement ) . isExitNode ( try , c )
536- then (
565+ if pred = last ( try . getFinally ( ) , c )
566+ then
537567 // Finally block can itself exit with completion `c`: either `c` must
538568 // match this split, `c` must be an abnormal completion, or this split
539569 // does not require another completion to be recovered
540- type .matchesCompletion ( c )
541- or
542- not c instanceof NormalCompletion
543- or
544- type instanceof NormalSuccessor
545- ) else (
546- // Finally block can exit with completion `c` derived from try/catch
570+ inherited = false and
571+ (
572+ type .matchesCompletion ( c )
573+ or
574+ not c instanceof NormalCompletion
575+ or
576+ type instanceof NormalSuccessor
577+ )
578+ else (
579+ // Finally block can exit with completion `c` inherited from try/catch
547580 // block: must match this split
581+ inherited = true and
548582 type .matchesCompletion ( c ) and
549583 not type instanceof NormalSuccessor
550584 )
551585 )
586+ or
587+ // If this split is normal, and an outer split can exit based on a inherited
588+ // completion, we need to exit this split as well. For example, in
589+ //
590+ // ```
591+ // bool done;
592+ // try
593+ // {
594+ // if (b1) throw new ExceptionA();
595+ // }
596+ // finally
597+ // {
598+ // try
599+ // {
600+ // if (b2) throw new ExceptionB();
601+ // }
602+ // finally
603+ // {
604+ // done = true;
605+ // }
606+ // }
607+ // ```
608+ //
609+ // if the outer split for `done = true` is `ExceptionA` and the inner split
610+ // is "normal" (corresponding to `b1 = true` and `b2 = false`), then the inner
611+ // split must be able to exit with an `ExceptionA` completion.
612+ this .appliesToPredecessor ( pred ) and
613+ exists ( FinallySplitInternal outer |
614+ outer .getNestLevel ( ) = this .getNestLevel ( ) - 1 and
615+ outer .exit ( pred , c , inherited ) and
616+ this .getType ( ) instanceof NormalSuccessor and
617+ inherited = true
618+ )
552619 }
553620
554621 override predicate hasExit ( ControlFlowElement pred , ControlFlowElement succ , Completion c ) {
555- this .appliesToPredecessor ( pred ) and
556622 succ = succ ( pred , c ) and
557623 (
558- // Entering a nested `finally` block abnormally means that we should exit this split
559- succ .( FinallyControlFlowElement ) .isEntryNode ( ) and
560- not c instanceof NormalCompletion
561- or
562- exit ( pred , c )
624+ exit ( pred , c , _)
563625 or
564- exit ( pred , any ( BreakCompletion bc ) ) and
626+ exit ( pred , any ( BreakCompletion bc ) , _ ) and
565627 c instanceof BreakNormalCompletion
566628 )
567629 }
568630
569631 override Callable hasExit ( ControlFlowElement pred , Completion c ) {
570632 result = succExit ( pred , c ) and
571633 (
572- exit ( pred , c )
634+ exit ( pred , c , _ )
573635 or
574- exit ( pred , any ( BreakCompletion bc ) ) and
636+ exit ( pred , any ( BreakCompletion bc ) , _ ) and
575637 c instanceof BreakNormalCompletion
576638 )
577639 }
@@ -582,11 +644,11 @@ module FinallySplitting {
582644 succ = any ( FinallyControlFlowElement fcfe |
583645 if fcfe .isEntryNode ( )
584646 then
585- // Entering a nested `finally` block normally must remember the outer split
586- c instanceof NormalCompletion
647+ // entering a nested `finally` block
648+ nestLevel ( fcfe . getTryStmt ( ) ) > this . getNestLevel ( )
587649 else
588- // Staying in the same `finally` block should maintain this split
589- not this . hasEntry ( pred , succ , c )
650+ // staying in the same (possibly nested) `finally` block as `pred`
651+ nestLevel ( fcfe . getTryStmt ( ) ) >= this . getNestLevel ( )
590652 )
591653 }
592654 }
0 commit comments