@@ -431,115 +431,132 @@ private module ControlFlowGraphImpl {
431
431
}
432
432
433
433
/**
434
- * A virtual method with a unique implementation. That is, the method does not
435
- * participate in overriding and there are no call targets that could dispatch
436
- * to both this and another method .
434
+ * Module containing a global non-returning method analysis. The result is injected back into
435
+ * a local predicate `nonReturningMethodCall()`, which is used in further local predicates
436
+ * within this file .
437
437
*/
438
- private class EffectivelyNonVirtualMethod extends SrcMethod {
439
- EffectivelyNonVirtualMethod ( ) {
440
- exists ( this .getBody ( ) ) and
441
- this .isVirtual ( ) and
442
- not this = any ( Method m ) .getASourceOverriddenMethod ( ) and
443
- not this .overrides ( _) and
444
- // guard against implicit overrides of default methods
445
- not this .getAPossibleImplementationOfSrcMethod ( ) != this and
446
- // guard against interface implementations in inheriting subclasses
447
- not exists ( SrcMethod m |
448
- 1 < strictcount ( m .getAPossibleImplementationOfSrcMethod ( ) ) and
449
- this = m .getAPossibleImplementationOfSrcMethod ( )
450
- ) and
451
- // UnsupportedOperationException could indicate that this is meant to be overridden
452
- not exists ( ClassInstanceExpr ex |
453
- this .getBody ( ) .getLastStmt ( ) .( ThrowStmt ) .getExpr ( ) = ex and
454
- ex .getConstructedType ( ) .hasQualifiedName ( "java.lang" , "UnsupportedOperationException" )
455
- ) and
456
- // an unused parameter could indicate that this is meant to be overridden
457
- forall ( Parameter p | p = this .getAParameter ( ) | exists ( p .getAnAccess ( ) ) )
438
+ overlay [ global]
439
+ private module NonReturningAnalysis {
440
+ /**
441
+ * A virtual method with a unique implementation. That is, the method does not
442
+ * participate in overriding and there are no call targets that could dispatch
443
+ * to both this and another method.
444
+ */
445
+ private class EffectivelyNonVirtualMethod extends SrcMethod {
446
+ EffectivelyNonVirtualMethod ( ) {
447
+ exists ( this .getBody ( ) ) and
448
+ this .isVirtual ( ) and
449
+ not this = any ( Method m ) .getASourceOverriddenMethod ( ) and
450
+ not this .overrides ( _) and
451
+ // guard against implicit overrides of default methods
452
+ not this .getAPossibleImplementationOfSrcMethod ( ) != this and
453
+ // guard against interface implementations in inheriting subclasses
454
+ not exists ( SrcMethod m |
455
+ 1 < strictcount ( m .getAPossibleImplementationOfSrcMethod ( ) ) and
456
+ this = m .getAPossibleImplementationOfSrcMethod ( )
457
+ ) and
458
+ // UnsupportedOperationException could indicate that this is meant to be overridden
459
+ not exists ( ClassInstanceExpr ex |
460
+ this .getBody ( ) .getLastStmt ( ) .( ThrowStmt ) .getExpr ( ) = ex and
461
+ ex .getConstructedType ( ) .hasQualifiedName ( "java.lang" , "UnsupportedOperationException" )
462
+ ) and
463
+ // an unused parameter could indicate that this is meant to be overridden
464
+ forall ( Parameter p | p = this .getAParameter ( ) | exists ( p .getAnAccess ( ) ) )
465
+ }
466
+
467
+ /** Gets a `MethodCall` that calls this method. */
468
+ MethodCall getAnAccess ( ) { result .getMethod ( ) .getAPossibleImplementation ( ) = this }
458
469
}
459
470
460
- /** Gets a `MethodCall` that calls this method. */
461
- MethodCall getAnAccess ( ) { result .getMethod ( ) .getAPossibleImplementation ( ) = this }
462
- }
471
+ /** Holds if a call to `m` indicates that `m` is expected to return. */
472
+ private predicate expectedReturn ( EffectivelyNonVirtualMethod m ) {
473
+ exists ( Stmt s , BlockStmt b |
474
+ m .getAnAccess ( ) .getEnclosingStmt ( ) = s and
475
+ b .getAStmt ( ) = s and
476
+ not b .getLastStmt ( ) = s
477
+ )
478
+ }
463
479
464
- /** Holds if a call to `m` indicates that `m` is expected to return. */
465
- private predicate expectedReturn ( EffectivelyNonVirtualMethod m ) {
466
- exists ( Stmt s , BlockStmt b |
467
- m .getAnAccess ( ) .getEnclosingStmt ( ) = s and
468
- b .getAStmt ( ) = s and
469
- not b .getLastStmt ( ) = s
470
- )
471
- }
480
+ /**
481
+ * Gets a non-overridable method that always throws an exception or calls `exit`.
482
+ */
483
+ private Method nonReturningMethod ( ) {
484
+ result instanceof MethodExit
485
+ or
486
+ not result .isOverridable ( ) and
487
+ exists ( BlockStmt body |
488
+ body = result .getBody ( ) and
489
+ not exists ( ReturnStmt ret | ret .getEnclosingCallable ( ) = result )
490
+ |
491
+ not result .getReturnType ( ) instanceof VoidType or
492
+ body .getLastStmt ( ) = nonReturningStmt ( )
493
+ )
494
+ }
472
495
473
- /**
474
- * Gets a non-overridable method that always throws an exception or calls `exit`.
475
- */
476
- private Method nonReturningMethod ( ) {
477
- result instanceof MethodExit
478
- or
479
- not result .isOverridable ( ) and
480
- exists ( BlockStmt body |
481
- body = result .getBody ( ) and
482
- not exists ( ReturnStmt ret | ret .getEnclosingCallable ( ) = result )
483
- |
484
- not result .getReturnType ( ) instanceof VoidType or
485
- body .getLastStmt ( ) = nonReturningStmt ( )
486
- )
487
- }
496
+ /**
497
+ * Gets a virtual method that always throws an exception or calls `exit`.
498
+ */
499
+ private EffectivelyNonVirtualMethod likelyNonReturningMethod ( ) {
500
+ result .getReturnType ( ) instanceof VoidType and
501
+ not exists ( ReturnStmt ret | ret .getEnclosingCallable ( ) = result ) and
502
+ not expectedReturn ( result ) and
503
+ forall ( Parameter p | p = result .getAParameter ( ) | exists ( p .getAnAccess ( ) ) ) and
504
+ result .getBody ( ) .getLastStmt ( ) = nonReturningStmt ( )
505
+ }
488
506
489
- /**
490
- * Gets a virtual method that always throws an exception or calls `exit`.
491
- */
492
- private EffectivelyNonVirtualMethod likelyNonReturningMethod ( ) {
493
- result .getReturnType ( ) instanceof VoidType and
494
- not exists ( ReturnStmt ret | ret .getEnclosingCallable ( ) = result ) and
495
- not expectedReturn ( result ) and
496
- forall ( Parameter p | p = result .getAParameter ( ) | exists ( p .getAnAccess ( ) ) ) and
497
- result .getBody ( ) .getLastStmt ( ) = nonReturningStmt ( )
498
- }
507
+ /**
508
+ * Gets a `MethodCall` that always throws an exception or calls `exit`.
509
+ */
510
+ private MethodCall nonReturningMethodCallGlobal ( ) {
511
+ result .getMethod ( ) .getSourceDeclaration ( ) = nonReturningMethod ( ) or
512
+ result = likelyNonReturningMethod ( ) .getAnAccess ( )
513
+ }
499
514
500
- /**
501
- * Gets a `MethodCall` that always throws an exception or calls `exit`.
502
- */
503
- private MethodCall nonReturningMethodCall ( ) {
504
- result .getMethod ( ) .getSourceDeclaration ( ) = nonReturningMethod ( ) or
505
- result = likelyNonReturningMethod ( ) .getAnAccess ( )
506
- }
515
+ /**
516
+ * Gets a `MethodCall` that always throws an exception or calls `exit`.
517
+ *
518
+ * This predicate is local so gives the local anaylyis for base and the global
519
+ * analyis for overlay cases.
520
+ */
521
+ overlay [ local]
522
+ MethodCall nonReturningMethodCall ( ) = localToGlobal( nonReturningMethodCallGlobal / 0 ) ( result )
507
523
508
- /**
509
- * Gets a statement that always throws an exception or calls `exit`.
510
- */
511
- private Stmt nonReturningStmt ( ) {
512
- result instanceof ThrowStmt
513
- or
514
- result .( ExprStmt ) .getExpr ( ) = nonReturningExpr ( )
515
- or
516
- result .( BlockStmt ) .getLastStmt ( ) = nonReturningStmt ( )
517
- or
518
- exists ( IfStmt ifstmt | ifstmt = result |
519
- ifstmt .getThen ( ) = nonReturningStmt ( ) and
520
- ifstmt .getElse ( ) = nonReturningStmt ( )
521
- )
522
- or
523
- exists ( TryStmt try | try = result |
524
- try .getBlock ( ) = nonReturningStmt ( ) and
525
- forall ( CatchClause cc | cc = try .getACatchClause ( ) | cc .getBlock ( ) = nonReturningStmt ( ) )
526
- )
527
- }
524
+ /**
525
+ * Gets a statement that always throws an exception or calls `exit`.
526
+ */
527
+ private Stmt nonReturningStmt ( ) {
528
+ result instanceof ThrowStmt
529
+ or
530
+ result .( ExprStmt ) .getExpr ( ) = nonReturningExpr ( )
531
+ or
532
+ result .( BlockStmt ) .getLastStmt ( ) = nonReturningStmt ( )
533
+ or
534
+ exists ( IfStmt ifstmt | ifstmt = result |
535
+ ifstmt .getThen ( ) = nonReturningStmt ( ) and
536
+ ifstmt .getElse ( ) = nonReturningStmt ( )
537
+ )
538
+ or
539
+ exists ( TryStmt try | try = result |
540
+ try .getBlock ( ) = nonReturningStmt ( ) and
541
+ forall ( CatchClause cc | cc = try .getACatchClause ( ) | cc .getBlock ( ) = nonReturningStmt ( ) )
542
+ )
543
+ }
528
544
529
- /**
530
- * Gets an expression that always throws an exception or calls `exit`.
531
- */
532
- private Expr nonReturningExpr ( ) {
533
- result = nonReturningMethodCall ( )
534
- or
535
- result .( StmtExpr ) .getStmt ( ) = nonReturningStmt ( )
536
- or
537
- exists ( WhenExpr whenexpr | whenexpr = result |
538
- whenexpr .getBranch ( _) .isElseBranch ( ) and
539
- forex ( WhenBranch whenbranch | whenbranch = whenexpr .getBranch ( _) |
540
- whenbranch .getRhs ( ) = nonReturningStmt ( )
545
+ /**
546
+ * Gets an expression that always throws an exception or calls `exit`.
547
+ */
548
+ private Expr nonReturningExpr ( ) {
549
+ result = nonReturningMethodCallGlobal ( )
550
+ or
551
+ result .( StmtExpr ) .getStmt ( ) = nonReturningStmt ( )
552
+ or
553
+ exists ( WhenExpr whenexpr | whenexpr = result |
554
+ whenexpr .getBranch ( _) .isElseBranch ( ) and
555
+ forex ( WhenBranch whenbranch | whenbranch = whenexpr .getBranch ( _) |
556
+ whenbranch .getRhs ( ) = nonReturningStmt ( )
557
+ )
541
558
)
542
- )
559
+ }
543
560
}
544
561
545
562
// Join order engineering -- first determine the switch block and the case indices required, then retrieve them.
@@ -766,7 +783,7 @@ private module ControlFlowGraphImpl {
766
783
not this instanceof BooleanLiteral and
767
784
not this instanceof ReturnStmt and
768
785
not this instanceof ThrowStmt and
769
- not this = nonReturningMethodCall ( )
786
+ not this = NonReturningAnalysis :: nonReturningMethodCall ( )
770
787
}
771
788
}
772
789
0 commit comments