@@ -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. */
@@ -474,44 +484,60 @@ module FinallySplitting {
474484 class FinallySplitImpl extends SplitImpl , TFinallySplit {
475485 private FinallySplitType type ;
476486
477- FinallySplitImpl ( ) { this = TFinallySplit ( type ) }
487+ private int nestLevel ;
488+
489+ FinallySplitImpl ( ) { this = TFinallySplit ( type , nestLevel ) }
478490
479491 /**
480492 * Gets the type of this `finally` split, that is, how to continue execution after the
481493 * `finally` block.
482494 */
483495 FinallySplitType getType ( ) { result = type }
484496
497+ /** Gets the `finally` nesting level. */
498+ int getNestLevel ( ) { result = nestLevel }
499+
485500 override string toString ( ) {
486501 if type instanceof NormalSuccessor
487502 then result = ""
488- else result = "finally: " + type .toString ( )
503+ else
504+ if nestLevel > 1
505+ then result = "finally(" + nestLevel + "): " + type .toString ( )
506+ else result = "finally: " + type .toString ( )
489507 }
490508 }
491509
492- private class FinallySplitKind extends SplitKind , TFinallySplitKind {
493- override int getListOrder ( ) { result = InitializerSplitting:: getNextListOrder ( ) }
510+ private int getListOrder ( FinallySplitKind kind ) {
511+ result = InitializerSplitting:: getNextListOrder ( ) + kind .getNestLevel ( )
512+ }
494513
495- override string toString ( ) { result = "Finally" }
514+ int getNextListOrder ( ) {
515+ result = max ( int i | i = getListOrder ( _) + 1 or i = InitializerSplitting:: getNextListOrder ( ) )
496516 }
497517
498- int getNextListOrder ( ) { result = InitializerSplitting:: getNextListOrder ( ) + 1 }
518+ private class FinallySplitKind extends SplitKind , TFinallySplitKind {
519+ private int nestLevel ;
520+
521+ FinallySplitKind ( ) { this = TFinallySplitKind ( nestLevel ) }
522+
523+ /** Gets the `finally` nesting level. */
524+ int getNestLevel ( ) { result = nestLevel }
525+
526+ override int getListOrder ( ) { result = getListOrder ( this ) }
527+
528+ override string toString ( ) { result = "Finally (" + nestLevel + ")" }
529+ }
499530
500531 private class FinallySplitInternal extends SplitInternal , FinallySplitImpl {
501- override FinallySplitKind getKind ( ) { any ( ) }
532+ override FinallySplitKind getKind ( ) { result . getNestLevel ( ) = this . getNestLevel ( ) }
502533
503534 override predicate hasEntry ( ControlFlowElement pred , ControlFlowElement succ , Completion c ) {
504- succ .( FinallyControlFlowElement ) .isEntryNode ( ) and
535+ succ = any ( FinallyControlFlowElement entry |
536+ entry .isEntryNode ( ) and
537+ this .getNestLevel ( ) = nestLevel ( entry .getTryStmt ( ) )
538+ ) and
505539 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- )
540+ this .getType ( ) .isSplitForEntryCompletion ( c )
515541 }
516542
517543 override predicate hasEntry ( Callable c , ControlFlowElement succ ) { none ( ) }
@@ -525,53 +551,90 @@ module FinallySplitting {
525551 ( exists ( succ ( pred , _) ) or exists ( succExit ( pred , _) ) )
526552 }
527553
528- /** Holds if `pred` may exit this split with completion `c`. */
529- private predicate exit ( ControlFlowElement pred , Completion c ) {
554+ /**
555+ * Holds if `pred` may exit this split with completion `c`. The Boolean
556+ * `derived` indicates whether `c` is a derived completion from a `try`/
557+ * `catch` block.
558+ */
559+ private predicate exit ( ControlFlowElement pred , Completion c , boolean derived ) {
530560 this .appliesToPredecessor ( pred ) and
531561 exists ( TryStmt try , FinallySplitType type |
532562 type = this .getType ( ) and
563+ nestLevel ( try ) = this .getNestLevel ( ) and
533564 pred = last ( try , c )
534565 |
535- if pred . ( FinallyControlFlowElement ) . isExitNode ( try , c )
536- then (
566+ if pred = last ( try . getFinally ( ) , c )
567+ then
537568 // Finally block can itself exit with completion `c`: either `c` must
538569 // match this split, `c` must be an abnormal completion, or this split
539570 // 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 (
571+ derived = false and
572+ (
573+ type .matchesCompletion ( c )
574+ or
575+ not c instanceof NormalCompletion
576+ or
577+ type instanceof NormalSuccessor
578+ )
579+ else (
546580 // Finally block can exit with completion `c` derived from try/catch
547581 // block: must match this split
582+ derived = true and
548583 type .matchesCompletion ( c ) and
549584 not type instanceof NormalSuccessor
550585 )
551586 )
587+ or
588+ // If this split is normal, and an outer split can exit based on a derived
589+ // completion, we need to exit this split as well. For example, in
590+ //
591+ // ```
592+ // bool done;
593+ // try
594+ // {
595+ // if (b1) throw new ExceptionA();
596+ // }
597+ // finally
598+ // {
599+ // try
600+ // {
601+ // if (b2) throw new ExceptionB();
602+ // }
603+ // finally
604+ // {
605+ // done = true;
606+ // }
607+ // }
608+ // ```
609+ //
610+ // if the outer split for `done = true` is `ExceptionA` and the inner split
611+ // is "normal" (corresponding to `b1 = true` and `b2 = false`), then the inner
612+ // split must be able to exit with an `ExceptionA` completion.
613+ this .appliesToPredecessor ( pred ) and
614+ exists ( FinallySplitInternal outer |
615+ outer .getNestLevel ( ) = this .getNestLevel ( ) - 1 and
616+ outer .exit ( pred , c , derived ) and
617+ this .getType ( ) instanceof NormalSuccessor and
618+ derived = true
619+ )
552620 }
553621
554622 override predicate hasExit ( ControlFlowElement pred , ControlFlowElement succ , Completion c ) {
555- this .appliesToPredecessor ( pred ) and
556623 succ = succ ( pred , c ) and
557624 (
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 )
625+ exit ( pred , c , _)
563626 or
564- exit ( pred , any ( BreakCompletion bc ) ) and
627+ exit ( pred , any ( BreakCompletion bc ) , _ ) and
565628 c instanceof BreakNormalCompletion
566629 )
567630 }
568631
569632 override Callable hasExit ( ControlFlowElement pred , Completion c ) {
570633 result = succExit ( pred , c ) and
571634 (
572- exit ( pred , c )
635+ exit ( pred , c , _ )
573636 or
574- exit ( pred , any ( BreakCompletion bc ) ) and
637+ exit ( pred , any ( BreakCompletion bc ) , _ ) and
575638 c instanceof BreakNormalCompletion
576639 )
577640 }
@@ -582,11 +645,11 @@ module FinallySplitting {
582645 succ = any ( FinallyControlFlowElement fcfe |
583646 if fcfe .isEntryNode ( )
584647 then
585- // Entering a nested `finally` block normally must remember the outer split
586- c instanceof NormalCompletion
648+ // entering a nested `finally` block
649+ nestLevel ( fcfe . getTryStmt ( ) ) > this . getNestLevel ( )
587650 else
588- // Staying in the same `finally` block should maintain this split
589- not this . hasEntry ( pred , succ , c )
651+ // staying in the same (possibly nested) `finally` block as `pred`
652+ nestLevel ( fcfe . getTryStmt ( ) ) >= this . getNestLevel ( )
590653 )
591654 }
592655 }
0 commit comments