Flow.java revision 3828:d30434bde0a8
1128710Sru/* 2128710Sru * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. 3128710Sru * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4128710Sru * 5128710Sru * This code is free software; you can redistribute it and/or modify it 6128710Sru * under the terms of the GNU General Public License version 2 only, as 7128710Sru * published by the Free Software Foundation. Oracle designates this 8128710Sru * particular file as subject to the "Classpath" exception as provided 9128710Sru * by Oracle in the LICENSE file that accompanied this code. 10128710Sru * 11128710Sru * This code is distributed in the hope that it will be useful, but WITHOUT 12128710Sru * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13128710Sru * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14128710Sru * version 2 for more details (a copy is included in the LICENSE file that 15128710Sru * accompanied this code). 16128710Sru * 1743561Skato * You should have received a copy of the GNU General Public License version 18239064Snyan * 2 along with this work; if not, write to the Free Software Foundation, 19239064Snyan * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20128710Sru * 21128710Sru * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22128710Sru * or visit www.oracle.com if you need additional information or have any 23128710Sru * questions. 2443561Skato */ 25128710Sru 26128710Sru//todo: one might eliminate uninits.andSets when monotonic 27128710Sru 2843561Skatopackage com.sun.tools.javac.comp; 2943561Skato 3043561Skatoimport java.util.HashMap; 3143561Skato 3258871Skatoimport com.sun.source.tree.LambdaExpressionTree.BodyKind; 33128710Sruimport com.sun.tools.javac.code.*; 34128710Sruimport com.sun.tools.javac.code.Scope.WriteableScope; 35128710Sruimport com.sun.tools.javac.tree.*; 3643561Skatoimport com.sun.tools.javac.util.*; 3743561Skatoimport com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 3843561Skato 3943561Skatoimport com.sun.tools.javac.code.Symbol.*; 40128710Sruimport com.sun.tools.javac.tree.JCTree.*; 41128710Sru 42128710Sruimport static com.sun.tools.javac.code.Flags.*; 4343561Skatoimport static com.sun.tools.javac.code.Flags.BLOCK; 4443561Skatoimport static com.sun.tools.javac.code.Kinds.Kind.*; 45128710Sruimport static com.sun.tools.javac.code.TypeTag.BOOLEAN; 46128710Sruimport static com.sun.tools.javac.code.TypeTag.VOID; 47128710Sruimport static com.sun.tools.javac.tree.JCTree.Tag.*; 4843561Skato 4943561Skato/** This pass implements dataflow analysis for Java programs though 5043561Skato * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that 51128710Sru * every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that 52128710Sru * every checked exception that is thrown is declared or caught. Definite assignment analysis 53128710Sru * (see AssignAnalyzer) ensures that each variable is assigned when used. Definite 5443561Skato * unassignment analysis (see AssignAnalyzer) in ensures that no final variable 5543561Skato * is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer) 56128710Sru * determines that local variables accessed within the scope of an inner class/lambda 57128710Sru * are either final or effectively-final. 58128710Sru * 5943561Skato * <p>The JLS has a number of problems in the 60128710Sru * specification of these flow analysis problems. This implementation 61128710Sru * attempts to address those issues. 62128710Sru * 6343561Skato * <p>First, there is no accommodation for a finally clause that cannot 64128710Sru * complete normally. For liveness analysis, an intervening finally 65128710Sru * clause can cause a break, continue, or return not to reach its 66128710Sru * target. For exception analysis, an intervening finally clause can 6743561Skato * cause any exception to be "caught". For DA/DU analysis, the finally 6843561Skato * clause can prevent a transfer of control from propagating DA/DU 6943561Skato * state to the target. In addition, code in the finally clause can 7043561Skato * affect the DA/DU status of variables. 7143561Skato * 7243561Skato * <p>For try statements, we introduce the idea of a variable being 7343561Skato * definitely unassigned "everywhere" in a block. A variable V is 7443561Skato * "unassigned everywhere" in a block iff it is unassigned at the 7543561Skato * beginning of the block and there is no reachable assignment to V 7643561Skato * in the block. An assignment V=e is reachable iff V is not DA 7743561Skato * after e. Then we can say that V is DU at the beginning of the 7843561Skato * catch block iff V is DU everywhere in the try block. Similarly, V 7943561Skato * is DU at the beginning of the finally block iff V is DU everywhere 8043561Skato * in the try block and in every catch block. Specifically, the 8143561Skato * following bullet is added to 16.2.2 8243561Skato * <pre> 8343561Skato * V is <em>unassigned everywhere</em> in a block if it is 8443561Skato * unassigned before the block and there is no reachable 8543561Skato * assignment to V within the block. 8643561Skato * </pre> 8743561Skato * <p>In 16.2.15, the third bullet (and all of its sub-bullets) for all 8843561Skato * try blocks is changed to 8943561Skato * <pre> 9043561Skato * V is definitely unassigned before a catch block iff V is 9143561Skato * definitely unassigned everywhere in the try block. 9243561Skato * </pre> 9343561Skato * <p>The last bullet (and all of its sub-bullets) for try blocks that 9443561Skato * have a finally block is changed to 95125780Snyan * <pre> 9643561Skato * V is definitely unassigned before the finally block iff 9758871Skato * V is definitely unassigned everywhere in the try block 9858871Skato * and everywhere in each catch block of the try statement. 99125780Snyan * </pre> 10043561Skato * <p>In addition, 101128710Sru * <pre> 102128710Sru * V is definitely assigned at the end of a constructor iff 103128710Sru * V is definitely assigned after the block that is the body 104125780Snyan * of the constructor and V is definitely assigned at every 10543561Skato * return that can return from the constructor. 10658871Skato * </pre> 10758871Skato * <p>In addition, each continue statement with the loop as its target 10858871Skato * is treated as a jump to the end of the loop body, and "intervening" 10943561Skato * finally clauses are treated as follows: V is DA "due to the 110235264Savg * continue" iff V is DA before the continue statement or V is DA at 11143561Skato * the end of any intervening finally block. V is DU "due to the 11243561Skato * continue" iff any intervening finally cannot complete normally or V 11343561Skato * is DU at the end of every intervening finally block. This "due to 11458871Skato * the continue" concept is then used in the spec for the loops. 11543561Skato * 11658871Skato * <p>Similarly, break statements must consider intervening finally 117125780Snyan * blocks. For liveness analysis, a break statement for which any 118235264Savg * intervening finally cannot complete normally is not considered to 11958871Skato * cause the target statement to be able to complete normally. Then 12058871Skato * we say V is DA "due to the break" iff V is DA before the break or 121235264Savg * V is DA at the end of any intervening finally block. V is DU "due 122235264Savg * to the break" iff any intervening finally cannot complete normally 12343561Skato * or V is DU at the break and at the end of every intervening 12443561Skato * finally block. (I suspect this latter condition can be 12543561Skato * simplified.) This "due to the break" is then used in the spec for 126235264Savg * all statements that can be "broken". 127235264Savg * 128125780Snyan * <p>The return statement is treated similarly. V is DA "due to a 12943561Skato * return statement" iff V is DA before the return statement or V is 13043561Skato * DA at the end of any intervening finally block. Note that we 13158871Skato * don't have to worry about the return expression because this 13258871Skato * concept is only used for construcrors. 133125780Snyan * 134235264Savg * <p>There is no spec in the JLS for when a variable is definitely 135235264Savg * assigned at the end of a constructor, which is needed for final 136235264Savg * fields (8.3.1.2). We implement the rule that V is DA at the end 137235264Savg * of the constructor iff it is DA and the end of the body of the 138235264Savg * constructor and V is DA "due to" every return of the constructor. 139235264Savg * 14043561Skato * <p>Intervening finally blocks similarly affect exception analysis. An 14143561Skato * intervening finally that cannot complete normally allows us to ignore 14243561Skato * an otherwise uncaught exception. 143125780Snyan * 14443561Skato * <p>To implement the semantics of intervening finally clauses, all 14543561Skato * nonlocal transfers (break, continue, return, throw, method call that 14658871Skato * can throw a checked exception, and a constructor invocation that can 14758871Skato * thrown a checked exception) are recorded in a queue, and removed 148125780Snyan * from the queue when we complete processing the target of the 149128710Sru * nonlocal transfer. This allows us to modify the queue in accordance 150128710Sru * with the above rules when we encounter a finally clause. The only 151128710Sru * exception to this [no pun intended] is that checked exceptions that 15243561Skato * are known to be caught or declared to be caught in the enclosing 15343561Skato * method are not recorded in the queue, but instead are recorded in a 15443561Skato * global variable "{@code Set<Type> thrown}" that records the type of all 15543561Skato * exceptions that can be thrown. 15643561Skato * 15743561Skato * <p>Other minor issues the treatment of members of other classes 15843561Skato * (always considered DA except that within an anonymous class 15943561Skato * constructor, where DA status from the enclosing scope is 16043561Skato * preserved), treatment of the case expression (V is DA before the 16143561Skato * case expression iff V is DA after the switch expression), 16243561Skato * treatment of variables declared in a switch block (the implied 16343561Skato * DA/DU status after the switch expression is DU and not DA for 16443561Skato * variables defined in a switch block), the treatment of boolean ?: 16543561Skato * expressions (The JLS rules only handle b and c non-boolean; the 16643561Skato * new rule is that if b and c are boolean valued, then V is 16743561Skato * (un)assigned after a?b:c when true/false iff V is (un)assigned 16843561Skato * after b when true/false and V is (un)assigned after c when 16943561Skato * true/false). 17043561Skato * 17143561Skato * <p>There is the remaining question of what syntactic forms constitute a 17243561Skato * reference to a variable. It is conventional to allow this.x on the 17343561Skato * left-hand-side to initialize a final instance field named x, yet 17443561Skato * this.x isn't considered a "use" when appearing on a right-hand-side 17543561Skato * in most implementations. Should parentheses affect what is 17643561Skato * considered a variable reference? The simplest rule would be to 17758871Skato * allow unqualified forms only, parentheses optional, and phase out 17843561Skato * support for assigning to a final field via this.x. 179125780Snyan * 18058871Skato * <p><b>This is NOT part of any supported API. 181125780Snyan * If you write code that depends on this, you do so at your own risk. 18243561Skato * This code and its internal interfaces are subject to change or 18343561Skato * deletion without notice.</b> 18443561Skato */ 185125780Snyanpublic class Flow { 18643561Skato protected static final Context.Key<Flow> flowKey = new Context.Key<>(); 18743561Skato 18858871Skato private final Names names; 189125780Snyan private final Log log; 19043561Skato private final Symtab syms; 191125780Snyan private final Types types; 19243561Skato private final Check chk; 19358871Skato private TreeMaker make; 19458871Skato private final Resolve rs; 195125780Snyan private final JCDiagnostic.Factory diags; 19643561Skato private Env<AttrContext> attrEnv; 197125780Snyan private Lint lint; 19843561Skato private final boolean allowImprovedRethrowAnalysis; 19943561Skato private final boolean allowImprovedCatchAnalysis; 20058871Skato private final boolean allowEffectivelyFinalInInnerClasses; 20158871Skato private final boolean enforceThisDotInit; 202125780Snyan 203128710Sru public static Flow instance(Context context) { 204128710Sru Flow instance = context.get(flowKey); 205128710Sru if (instance == null) 20643561Skato instance = new Flow(context); 20743561Skato return instance; 20843561Skato } 20943561Skato 21043561Skato public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 21158871Skato new AliveAnalyzer().analyzeTree(env, make); 212125780Snyan new AssignAnalyzer().analyzeTree(env); 21358871Skato new FlowAnalyzer().analyzeTree(env, make); 21458871Skato new CaptureAnalyzer().analyzeTree(env, make); 21543561Skato } 216125780Snyan 21743561Skato public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) { 21843561Skato Log.DiagnosticHandler diagHandler = null; 21943561Skato //we need to disable diagnostics temporarily; the problem is that if 22043561Skato //a lambda expression contains e.g. an unreachable statement, an error 22143561Skato //message will be reported and will cause compilation to skip the flow analyis 22243561Skato //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 223125780Snyan //related errors, which will allow for more errors to be detected 22443561Skato if (!speculative) { 22558871Skato diagHandler = new Log.DiscardDiagnosticHandler(log); 22643561Skato } 22758871Skato try { 22843561Skato new LambdaAliveAnalyzer().analyzeTree(env, that, make); 22958871Skato } finally { 23043561Skato if (!speculative) { 23158871Skato log.popDiagnosticHandler(diagHandler); 23258871Skato } 233125780Snyan } 23443561Skato } 23543561Skato 23643561Skato public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env, 23743561Skato JCLambda that, TreeMaker make) { 23843561Skato //we need to disable diagnostics temporarily; the problem is that if 23943561Skato //a lambda expression contains e.g. an unreachable statement, an error 24043561Skato //message will be reported and will cause compilation to skip the flow analyis 24143561Skato //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 24243561Skato //related errors, which will allow for more errors to be detected 24343561Skato Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 24443561Skato try { 24543561Skato new LambdaAssignAnalyzer(env).analyzeTree(env, that); 24643561Skato LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); 24743561Skato flowAnalyzer.analyzeTree(env, that, make); 24843561Skato return flowAnalyzer.inferredThrownTypes; 24943561Skato } finally { 25043561Skato log.popDiagnosticHandler(diagHandler); 25143561Skato } 25243561Skato } 25343561Skato 25443561Skato /** 25543561Skato * Definite assignment scan mode 25643561Skato */ 25758871Skato enum FlowKind { 258125780Snyan /** 25958871Skato * This is the normal DA/DU analysis mode 26058871Skato */ 261125780Snyan NORMAL("var.might.already.be.assigned", false), 26243561Skato /** 26343561Skato * This is the speculative DA/DU analysis mode used to speculatively 26461064Snyan * derive assertions within loop bodies 26543561Skato */ 26643561Skato SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true); 26743561Skato 26861064Snyan final String errKey; 26961064Snyan final boolean isFinal; 27043561Skato 27161064Snyan FlowKind(String errKey, boolean isFinal) { 27261064Snyan this.errKey = errKey; 27361064Snyan this.isFinal = isFinal; 27461064Snyan } 27561064Snyan 27643561Skato boolean isFinal() { 27761064Snyan return isFinal; 27843561Skato } 27961064Snyan } 28043561Skato 28161064Snyan protected Flow(Context context) { 282128710Sru context.put(flowKey, this); 283128710Sru names = Names.instance(context); 284128710Sru log = Log.instance(context); 28543561Skato syms = Symtab.instance(context); 28643561Skato types = Types.instance(context); 28743561Skato chk = Check.instance(context); 28843561Skato lint = Lint.instance(context); 28943561Skato rs = Resolve.instance(context); 29043561Skato diags = JCDiagnostic.Factory.instance(context); 29143561Skato Source source = Source.instance(context); 29243561Skato allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis(); 29343561Skato allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis(); 29443561Skato allowEffectivelyFinalInInnerClasses = source.allowEffectivelyFinalInInnerClasses(); 29543561Skato enforceThisDotInit = source.enforceThisDotInit(); 29643561Skato } 29743561Skato 29843561Skato /** 29943561Skato * Base visitor class for all visitors implementing dataflow analysis logic. 30043561Skato * This class define the shared logic for handling jumps (break/continue statements). 30143561Skato */ 30243561Skato static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner { 30343561Skato 30443561Skato enum JumpKind { 30543561Skato BREAK(JCTree.Tag.BREAK) { 30643561Skato @Override 307128710Sru JCTree getTarget(JCTree tree) { 308128710Sru return ((JCBreak)tree).target; 309128710Sru } 31043561Skato }, 31143561Skato CONTINUE(JCTree.Tag.CONTINUE) { 31243561Skato @Override 31343561Skato JCTree getTarget(JCTree tree) { 31443561Skato return ((JCContinue)tree).target; 315128710Sru } 316128710Sru }; 317128710Sru 31843561Skato final JCTree.Tag treeTag; 31943561Skato 32043561Skato private JumpKind(Tag treeTag) { 32143561Skato this.treeTag = treeTag; 32243561Skato } 32343561Skato 32443561Skato abstract JCTree getTarget(JCTree tree); 32543561Skato } 32643561Skato 32743561Skato /** The currently pending exits that go from current inner blocks 32843561Skato * to an enclosing block, in source order. 32943561Skato */ 33043561Skato ListBuffer<P> pendingExits; 33143561Skato 33243561Skato /** A pending exit. These are the statements return, break, and 33343561Skato * continue. In addition, exception-throwing expressions or 33443561Skato * statements are put here when not known to be caught. This 33543561Skato * will typically result in an error unless it is within a 33643561Skato * try-finally whose finally block cannot complete normally. 33743561Skato */ 33843561Skato static class PendingExit { 33943561Skato JCTree tree; 34043561Skato 34143561Skato PendingExit(JCTree tree) { 34243561Skato this.tree = tree; 34343561Skato } 34443561Skato 34543561Skato void resolveJump() { 34643561Skato //do nothing 34743561Skato } 34843561Skato } 34943561Skato 35043561Skato abstract void markDead(); 35143561Skato 35243561Skato /** Record an outward transfer of control. */ 35343561Skato void recordExit(P pe) { 35443561Skato pendingExits.append(pe); 35543561Skato markDead(); 35643561Skato } 35743561Skato 35843561Skato /** Resolve all jumps of this statement. */ 35943561Skato private boolean resolveJump(JCTree tree, 36043561Skato ListBuffer<P> oldPendingExits, 36143561Skato JumpKind jk) { 36243561Skato boolean resolved = false; 363128710Sru List<P> exits = pendingExits.toList(); 364128710Sru pendingExits = oldPendingExits; 365128710Sru for (; exits.nonEmpty(); exits = exits.tail) { 36643561Skato P exit = exits.head; 36743561Skato if (exit.tree.hasTag(jk.treeTag) && 36843561Skato jk.getTarget(exit.tree) == tree) { 36943561Skato exit.resolveJump(); 37043561Skato resolved = true; 37143561Skato } else { 37243561Skato pendingExits.append(exit); 37343561Skato } 37443561Skato } 37543561Skato return resolved; 37643561Skato } 37743561Skato 37843561Skato /** Resolve all continues of this statement. */ 37943561Skato boolean resolveContinues(JCTree tree) { 38043561Skato return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE); 38143561Skato } 38243561Skato 38343561Skato /** Resolve all breaks of this statement. */ 38443561Skato boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) { 38543561Skato return resolveJump(tree, oldPendingExits, JumpKind.BREAK); 386128710Sru } 387128710Sru 388128710Sru @Override 38943561Skato public void scan(JCTree tree) { 39043561Skato if (tree != null && ( 39143561Skato tree.type == null || 39243561Skato tree.type != Type.stuckType)) { 39343561Skato super.scan(tree); 39443561Skato } 39543561Skato } 39643561Skato 397128710Sru public void visitPackageDef(JCPackageDecl tree) { 398128710Sru // Do nothing for PackageDecl 399128710Sru } 40052148Sbrian } 40143561Skato 40243561Skato /** 403125780Snyan * This pass implements the first step of the dataflow analysis, namely 40443561Skato * the liveness analysis check. This checks that every statement is reachable. 40543561Skato * The output of this analysis pass are used by other analyzers. This analyzer 40643561Skato * sets the 'finallyCanCompleteNormally' field in the JCTry class. 40743561Skato */ 40843561Skato class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { 40943561Skato 41043561Skato /** A flag that indicates whether the last statement could 41143561Skato * complete normally. 41243561Skato */ 41343561Skato private boolean alive; 41443561Skato 41543561Skato @Override 41643561Skato void markDead() { 41743561Skato alive = false; 41843561Skato } 41943561Skato 42043561Skato /************************************************************************* 42143561Skato * Visitor methods for statements and definitions 42243561Skato *************************************************************************/ 42343561Skato 42443561Skato /** Analyze a definition. 42543561Skato */ 426125780Snyan void scanDef(JCTree tree) { 427128710Sru scanStat(tree); 428128710Sru if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { 429128710Sru log.error(tree.pos(), 43043561Skato "initializer.must.be.able.to.complete.normally"); 431 } 432 } 433 434 /** Analyze a statement. Check that statement is reachable. 435 */ 436 void scanStat(JCTree tree) { 437 if (!alive && tree != null) { 438 log.error(tree.pos(), "unreachable.stmt"); 439 if (!tree.hasTag(SKIP)) alive = true; 440 } 441 scan(tree); 442 } 443 444 /** Analyze list of statements. 445 */ 446 void scanStats(List<? extends JCStatement> trees) { 447 if (trees != null) 448 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) 449 scanStat(l.head); 450 } 451 452 /* ------------ Visitor methods for various sorts of trees -------------*/ 453 454 public void visitClassDef(JCClassDecl tree) { 455 if (tree.sym == null) return; 456 boolean alivePrev = alive; 457 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; 458 Lint lintPrev = lint; 459 460 pendingExits = new ListBuffer<>(); 461 lint = lint.augment(tree.sym); 462 463 try { 464 // process all the static initializers 465 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 466 if (!l.head.hasTag(METHODDEF) && 467 (TreeInfo.flags(l.head) & STATIC) != 0) { 468 scanDef(l.head); 469 } 470 } 471 472 // process all the instance initializers 473 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 474 if (!l.head.hasTag(METHODDEF) && 475 (TreeInfo.flags(l.head) & STATIC) == 0) { 476 scanDef(l.head); 477 } 478 } 479 480 // process all the methods 481 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 482 if (l.head.hasTag(METHODDEF)) { 483 scan(l.head); 484 } 485 } 486 } finally { 487 pendingExits = pendingExitsPrev; 488 alive = alivePrev; 489 lint = lintPrev; 490 } 491 } 492 493 public void visitMethodDef(JCMethodDecl tree) { 494 if (tree.body == null) return; 495 Lint lintPrev = lint; 496 497 lint = lint.augment(tree.sym); 498 499 Assert.check(pendingExits.isEmpty()); 500 501 try { 502 alive = true; 503 scanStat(tree.body); 504 505 if (alive && !tree.sym.type.getReturnType().hasTag(VOID)) 506 log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt"); 507 508 List<PendingExit> exits = pendingExits.toList(); 509 pendingExits = new ListBuffer<>(); 510 while (exits.nonEmpty()) { 511 PendingExit exit = exits.head; 512 exits = exits.tail; 513 Assert.check(exit.tree.hasTag(RETURN)); 514 } 515 } finally { 516 lint = lintPrev; 517 } 518 } 519 520 public void visitVarDef(JCVariableDecl tree) { 521 if (tree.init != null) { 522 Lint lintPrev = lint; 523 lint = lint.augment(tree.sym); 524 try{ 525 scan(tree.init); 526 } finally { 527 lint = lintPrev; 528 } 529 } 530 } 531 532 public void visitBlock(JCBlock tree) { 533 scanStats(tree.stats); 534 } 535 536 public void visitDoLoop(JCDoWhileLoop tree) { 537 ListBuffer<PendingExit> prevPendingExits = pendingExits; 538 pendingExits = new ListBuffer<>(); 539 scanStat(tree.body); 540 alive |= resolveContinues(tree); 541 scan(tree.cond); 542 alive = alive && !tree.cond.type.isTrue(); 543 alive |= resolveBreaks(tree, prevPendingExits); 544 } 545 546 public void visitWhileLoop(JCWhileLoop tree) { 547 ListBuffer<PendingExit> prevPendingExits = pendingExits; 548 pendingExits = new ListBuffer<>(); 549 scan(tree.cond); 550 alive = !tree.cond.type.isFalse(); 551 scanStat(tree.body); 552 alive |= resolveContinues(tree); 553 alive = resolveBreaks(tree, prevPendingExits) || 554 !tree.cond.type.isTrue(); 555 } 556 557 public void visitForLoop(JCForLoop tree) { 558 ListBuffer<PendingExit> prevPendingExits = pendingExits; 559 scanStats(tree.init); 560 pendingExits = new ListBuffer<>(); 561 if (tree.cond != null) { 562 scan(tree.cond); 563 alive = !tree.cond.type.isFalse(); 564 } else { 565 alive = true; 566 } 567 scanStat(tree.body); 568 alive |= resolveContinues(tree); 569 scan(tree.step); 570 alive = resolveBreaks(tree, prevPendingExits) || 571 tree.cond != null && !tree.cond.type.isTrue(); 572 } 573 574 public void visitForeachLoop(JCEnhancedForLoop tree) { 575 visitVarDef(tree.var); 576 ListBuffer<PendingExit> prevPendingExits = pendingExits; 577 scan(tree.expr); 578 pendingExits = new ListBuffer<>(); 579 scanStat(tree.body); 580 alive |= resolveContinues(tree); 581 resolveBreaks(tree, prevPendingExits); 582 alive = true; 583 } 584 585 public void visitLabelled(JCLabeledStatement tree) { 586 ListBuffer<PendingExit> prevPendingExits = pendingExits; 587 pendingExits = new ListBuffer<>(); 588 scanStat(tree.body); 589 alive |= resolveBreaks(tree, prevPendingExits); 590 } 591 592 public void visitSwitch(JCSwitch tree) { 593 ListBuffer<PendingExit> prevPendingExits = pendingExits; 594 pendingExits = new ListBuffer<>(); 595 scan(tree.selector); 596 boolean hasDefault = false; 597 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 598 alive = true; 599 JCCase c = l.head; 600 if (c.pat == null) 601 hasDefault = true; 602 else 603 scan(c.pat); 604 scanStats(c.stats); 605 // Warn about fall-through if lint switch fallthrough enabled. 606 if (alive && 607 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && 608 c.stats.nonEmpty() && l.tail.nonEmpty()) 609 log.warning(Lint.LintCategory.FALLTHROUGH, 610 l.tail.head.pos(), 611 "possible.fall-through.into.case"); 612 } 613 if (!hasDefault) { 614 alive = true; 615 } 616 alive |= resolveBreaks(tree, prevPendingExits); 617 } 618 619 public void visitTry(JCTry tree) { 620 ListBuffer<PendingExit> prevPendingExits = pendingExits; 621 pendingExits = new ListBuffer<>(); 622 for (JCTree resource : tree.resources) { 623 if (resource instanceof JCVariableDecl) { 624 JCVariableDecl vdecl = (JCVariableDecl) resource; 625 visitVarDef(vdecl); 626 } else if (resource instanceof JCExpression) { 627 scan((JCExpression) resource); 628 } else { 629 throw new AssertionError(tree); // parser error 630 } 631 } 632 633 scanStat(tree.body); 634 boolean aliveEnd = alive; 635 636 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 637 alive = true; 638 JCVariableDecl param = l.head.param; 639 scan(param); 640 scanStat(l.head.body); 641 aliveEnd |= alive; 642 } 643 if (tree.finalizer != null) { 644 ListBuffer<PendingExit> exits = pendingExits; 645 pendingExits = prevPendingExits; 646 alive = true; 647 scanStat(tree.finalizer); 648 tree.finallyCanCompleteNormally = alive; 649 if (!alive) { 650 if (lint.isEnabled(Lint.LintCategory.FINALLY)) { 651 log.warning(Lint.LintCategory.FINALLY, 652 TreeInfo.diagEndPos(tree.finalizer), 653 "finally.cannot.complete"); 654 } 655 } else { 656 while (exits.nonEmpty()) { 657 pendingExits.append(exits.next()); 658 } 659 alive = aliveEnd; 660 } 661 } else { 662 alive = aliveEnd; 663 ListBuffer<PendingExit> exits = pendingExits; 664 pendingExits = prevPendingExits; 665 while (exits.nonEmpty()) pendingExits.append(exits.next()); 666 } 667 } 668 669 @Override 670 public void visitIf(JCIf tree) { 671 scan(tree.cond); 672 scanStat(tree.thenpart); 673 if (tree.elsepart != null) { 674 boolean aliveAfterThen = alive; 675 alive = true; 676 scanStat(tree.elsepart); 677 alive = alive | aliveAfterThen; 678 } else { 679 alive = true; 680 } 681 } 682 683 public void visitBreak(JCBreak tree) { 684 recordExit(new PendingExit(tree)); 685 } 686 687 public void visitContinue(JCContinue tree) { 688 recordExit(new PendingExit(tree)); 689 } 690 691 public void visitReturn(JCReturn tree) { 692 scan(tree.expr); 693 recordExit(new PendingExit(tree)); 694 } 695 696 public void visitThrow(JCThrow tree) { 697 scan(tree.expr); 698 markDead(); 699 } 700 701 public void visitApply(JCMethodInvocation tree) { 702 scan(tree.meth); 703 scan(tree.args); 704 } 705 706 public void visitNewClass(JCNewClass tree) { 707 scan(tree.encl); 708 scan(tree.args); 709 if (tree.def != null) { 710 scan(tree.def); 711 } 712 } 713 714 @Override 715 public void visitLambda(JCLambda tree) { 716 if (tree.type != null && 717 tree.type.isErroneous()) { 718 return; 719 } 720 721 ListBuffer<PendingExit> prevPending = pendingExits; 722 boolean prevAlive = alive; 723 try { 724 pendingExits = new ListBuffer<>(); 725 alive = true; 726 scanStat(tree.body); 727 tree.canCompleteNormally = alive; 728 } 729 finally { 730 pendingExits = prevPending; 731 alive = prevAlive; 732 } 733 } 734 735 public void visitModuleDef(JCModuleDecl tree) { 736 // Do nothing for modules 737 } 738 739 /************************************************************************** 740 * main method 741 *************************************************************************/ 742 743 /** Perform definite assignment/unassignment analysis on a tree. 744 */ 745 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 746 analyzeTree(env, env.tree, make); 747 } 748 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 749 try { 750 attrEnv = env; 751 Flow.this.make = make; 752 pendingExits = new ListBuffer<>(); 753 alive = true; 754 scan(tree); 755 } finally { 756 pendingExits = null; 757 Flow.this.make = null; 758 } 759 } 760 } 761 762 /** 763 * This pass implements the second step of the dataflow analysis, namely 764 * the exception analysis. This is to ensure that every checked exception that is 765 * thrown is declared or caught. The analyzer uses some info that has been set by 766 * the liveliness analyzer. 767 */ 768 class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> { 769 770 /** A flag that indicates whether the last statement could 771 * complete normally. 772 */ 773 HashMap<Symbol, List<Type>> preciseRethrowTypes; 774 775 /** The current class being defined. 776 */ 777 JCClassDecl classDef; 778 779 /** The list of possibly thrown declarable exceptions. 780 */ 781 List<Type> thrown; 782 783 /** The list of exceptions that are either caught or declared to be 784 * thrown. 785 */ 786 List<Type> caught; 787 788 class FlowPendingExit extends BaseAnalyzer.PendingExit { 789 790 Type thrown; 791 792 FlowPendingExit(JCTree tree, Type thrown) { 793 super(tree); 794 this.thrown = thrown; 795 } 796 } 797 798 @Override 799 void markDead() { 800 //do nothing 801 } 802 803 /*-------------------- Exceptions ----------------------*/ 804 805 /** Complain that pending exceptions are not caught. 806 */ 807 void errorUncaught() { 808 for (FlowPendingExit exit = pendingExits.next(); 809 exit != null; 810 exit = pendingExits.next()) { 811 if (classDef != null && 812 classDef.pos == exit.tree.pos) { 813 log.error(exit.tree.pos(), 814 "unreported.exception.default.constructor", 815 exit.thrown); 816 } else if (exit.tree.hasTag(VARDEF) && 817 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { 818 log.error(exit.tree.pos(), 819 "unreported.exception.implicit.close", 820 exit.thrown, 821 ((JCVariableDecl)exit.tree).sym.name); 822 } else { 823 log.error(exit.tree.pos(), 824 "unreported.exception.need.to.catch.or.throw", 825 exit.thrown); 826 } 827 } 828 } 829 830 /** Record that exception is potentially thrown and check that it 831 * is caught. 832 */ 833 void markThrown(JCTree tree, Type exc) { 834 if (!chk.isUnchecked(tree.pos(), exc)) { 835 if (!chk.isHandled(exc, caught)) { 836 pendingExits.append(new FlowPendingExit(tree, exc)); 837 } 838 thrown = chk.incl(exc, thrown); 839 } 840 } 841 842 /************************************************************************* 843 * Visitor methods for statements and definitions 844 *************************************************************************/ 845 846 /* ------------ Visitor methods for various sorts of trees -------------*/ 847 848 public void visitClassDef(JCClassDecl tree) { 849 if (tree.sym == null) return; 850 851 JCClassDecl classDefPrev = classDef; 852 List<Type> thrownPrev = thrown; 853 List<Type> caughtPrev = caught; 854 ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits; 855 Lint lintPrev = lint; 856 boolean anonymousClass = tree.name == names.empty; 857 pendingExits = new ListBuffer<>(); 858 if (!anonymousClass) { 859 caught = List.nil(); 860 } 861 classDef = tree; 862 thrown = List.nil(); 863 lint = lint.augment(tree.sym); 864 865 try { 866 // process all the static initializers 867 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 868 if (!l.head.hasTag(METHODDEF) && 869 (TreeInfo.flags(l.head) & STATIC) != 0) { 870 scan(l.head); 871 errorUncaught(); 872 } 873 } 874 875 // add intersection of all thrown clauses of initial constructors 876 // to set of caught exceptions, unless class is anonymous. 877 if (!anonymousClass) { 878 boolean firstConstructor = true; 879 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 880 if (TreeInfo.isInitialConstructor(l.head)) { 881 List<Type> mthrown = 882 ((JCMethodDecl) l.head).sym.type.getThrownTypes(); 883 if (firstConstructor) { 884 caught = mthrown; 885 firstConstructor = false; 886 } else { 887 caught = chk.intersect(mthrown, caught); 888 } 889 } 890 } 891 } 892 893 // process all the instance initializers 894 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 895 if (!l.head.hasTag(METHODDEF) && 896 (TreeInfo.flags(l.head) & STATIC) == 0) { 897 scan(l.head); 898 errorUncaught(); 899 } 900 } 901 902 // in an anonymous class, add the set of thrown exceptions to 903 // the throws clause of the synthetic constructor and propagate 904 // outwards. 905 // Changing the throws clause on the fly is okay here because 906 // the anonymous constructor can't be invoked anywhere else, 907 // and its type hasn't been cached. 908 if (anonymousClass) { 909 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 910 if (TreeInfo.isConstructor(l.head)) { 911 JCMethodDecl mdef = (JCMethodDecl)l.head; 912 scan(mdef); 913 mdef.thrown = make.Types(thrown); 914 mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown); 915 } 916 } 917 thrownPrev = chk.union(thrown, thrownPrev); 918 } 919 920 // process all the methods 921 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 922 if (anonymousClass && TreeInfo.isConstructor(l.head)) 923 continue; // there can never be an uncaught exception. 924 if (l.head.hasTag(METHODDEF)) { 925 scan(l.head); 926 errorUncaught(); 927 } 928 } 929 930 thrown = thrownPrev; 931 } finally { 932 pendingExits = pendingExitsPrev; 933 caught = caughtPrev; 934 classDef = classDefPrev; 935 lint = lintPrev; 936 } 937 } 938 939 public void visitMethodDef(JCMethodDecl tree) { 940 if (tree.body == null) return; 941 942 List<Type> caughtPrev = caught; 943 List<Type> mthrown = tree.sym.type.getThrownTypes(); 944 Lint lintPrev = lint; 945 946 lint = lint.augment(tree.sym); 947 948 Assert.check(pendingExits.isEmpty()); 949 950 try { 951 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 952 JCVariableDecl def = l.head; 953 scan(def); 954 } 955 if (TreeInfo.isInitialConstructor(tree)) 956 caught = chk.union(caught, mthrown); 957 else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) 958 caught = mthrown; 959 // else we are in an instance initializer block; 960 // leave caught unchanged. 961 962 scan(tree.body); 963 964 List<FlowPendingExit> exits = pendingExits.toList(); 965 pendingExits = new ListBuffer<>(); 966 while (exits.nonEmpty()) { 967 FlowPendingExit exit = exits.head; 968 exits = exits.tail; 969 if (exit.thrown == null) { 970 Assert.check(exit.tree.hasTag(RETURN)); 971 } else { 972 // uncaught throws will be reported later 973 pendingExits.append(exit); 974 } 975 } 976 } finally { 977 caught = caughtPrev; 978 lint = lintPrev; 979 } 980 } 981 982 public void visitVarDef(JCVariableDecl tree) { 983 if (tree.init != null) { 984 Lint lintPrev = lint; 985 lint = lint.augment(tree.sym); 986 try{ 987 scan(tree.init); 988 } finally { 989 lint = lintPrev; 990 } 991 } 992 } 993 994 public void visitBlock(JCBlock tree) { 995 scan(tree.stats); 996 } 997 998 public void visitDoLoop(JCDoWhileLoop tree) { 999 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1000 pendingExits = new ListBuffer<>(); 1001 scan(tree.body); 1002 resolveContinues(tree); 1003 scan(tree.cond); 1004 resolveBreaks(tree, prevPendingExits); 1005 } 1006 1007 public void visitWhileLoop(JCWhileLoop tree) { 1008 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1009 pendingExits = new ListBuffer<>(); 1010 scan(tree.cond); 1011 scan(tree.body); 1012 resolveContinues(tree); 1013 resolveBreaks(tree, prevPendingExits); 1014 } 1015 1016 public void visitForLoop(JCForLoop tree) { 1017 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1018 scan(tree.init); 1019 pendingExits = new ListBuffer<>(); 1020 if (tree.cond != null) { 1021 scan(tree.cond); 1022 } 1023 scan(tree.body); 1024 resolveContinues(tree); 1025 scan(tree.step); 1026 resolveBreaks(tree, prevPendingExits); 1027 } 1028 1029 public void visitForeachLoop(JCEnhancedForLoop tree) { 1030 visitVarDef(tree.var); 1031 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1032 scan(tree.expr); 1033 pendingExits = new ListBuffer<>(); 1034 scan(tree.body); 1035 resolveContinues(tree); 1036 resolveBreaks(tree, prevPendingExits); 1037 } 1038 1039 public void visitLabelled(JCLabeledStatement tree) { 1040 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1041 pendingExits = new ListBuffer<>(); 1042 scan(tree.body); 1043 resolveBreaks(tree, prevPendingExits); 1044 } 1045 1046 public void visitSwitch(JCSwitch tree) { 1047 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1048 pendingExits = new ListBuffer<>(); 1049 scan(tree.selector); 1050 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 1051 JCCase c = l.head; 1052 if (c.pat != null) { 1053 scan(c.pat); 1054 } 1055 scan(c.stats); 1056 } 1057 resolveBreaks(tree, prevPendingExits); 1058 } 1059 1060 public void visitTry(JCTry tree) { 1061 List<Type> caughtPrev = caught; 1062 List<Type> thrownPrev = thrown; 1063 thrown = List.nil(); 1064 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1065 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1066 ((JCTypeUnion)l.head.param.vartype).alternatives : 1067 List.of(l.head.param.vartype); 1068 for (JCExpression ct : subClauses) { 1069 caught = chk.incl(ct.type, caught); 1070 } 1071 } 1072 1073 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1074 pendingExits = new ListBuffer<>(); 1075 for (JCTree resource : tree.resources) { 1076 if (resource instanceof JCVariableDecl) { 1077 JCVariableDecl vdecl = (JCVariableDecl) resource; 1078 visitVarDef(vdecl); 1079 } else if (resource instanceof JCExpression) { 1080 scan((JCExpression) resource); 1081 } else { 1082 throw new AssertionError(tree); // parser error 1083 } 1084 } 1085 for (JCTree resource : tree.resources) { 1086 List<Type> closeableSupertypes = resource.type.isCompound() ? 1087 types.interfaces(resource.type).prepend(types.supertype(resource.type)) : 1088 List.of(resource.type); 1089 for (Type sup : closeableSupertypes) { 1090 if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { 1091 Symbol closeMethod = rs.resolveQualifiedMethod(tree, 1092 attrEnv, 1093 types.skipTypeVars(sup, false), 1094 names.close, 1095 List.nil(), 1096 List.nil()); 1097 Type mt = types.memberType(resource.type, closeMethod); 1098 if (closeMethod.kind == MTH) { 1099 for (Type t : mt.getThrownTypes()) { 1100 markThrown(resource, t); 1101 } 1102 } 1103 } 1104 } 1105 } 1106 scan(tree.body); 1107 List<Type> thrownInTry = allowImprovedCatchAnalysis ? 1108 chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) : 1109 thrown; 1110 thrown = thrownPrev; 1111 caught = caughtPrev; 1112 1113 List<Type> caughtInTry = List.nil(); 1114 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1115 JCVariableDecl param = l.head.param; 1116 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1117 ((JCTypeUnion)l.head.param.vartype).alternatives : 1118 List.of(l.head.param.vartype); 1119 List<Type> ctypes = List.nil(); 1120 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry); 1121 for (JCExpression ct : subClauses) { 1122 Type exc = ct.type; 1123 if (exc != syms.unknownType) { 1124 ctypes = ctypes.append(exc); 1125 if (types.isSameType(exc, syms.objectType)) 1126 continue; 1127 checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry); 1128 caughtInTry = chk.incl(exc, caughtInTry); 1129 } 1130 } 1131 scan(param); 1132 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); 1133 scan(l.head.body); 1134 preciseRethrowTypes.remove(param.sym); 1135 } 1136 if (tree.finalizer != null) { 1137 List<Type> savedThrown = thrown; 1138 thrown = List.nil(); 1139 ListBuffer<FlowPendingExit> exits = pendingExits; 1140 pendingExits = prevPendingExits; 1141 scan(tree.finalizer); 1142 if (!tree.finallyCanCompleteNormally) { 1143 // discard exits and exceptions from try and finally 1144 thrown = chk.union(thrown, thrownPrev); 1145 } else { 1146 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1147 thrown = chk.union(thrown, savedThrown); 1148 // FIX: this doesn't preserve source order of exits in catch 1149 // versus finally! 1150 while (exits.nonEmpty()) { 1151 pendingExits.append(exits.next()); 1152 } 1153 } 1154 } else { 1155 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1156 ListBuffer<FlowPendingExit> exits = pendingExits; 1157 pendingExits = prevPendingExits; 1158 while (exits.nonEmpty()) pendingExits.append(exits.next()); 1159 } 1160 } 1161 1162 @Override 1163 public void visitIf(JCIf tree) { 1164 scan(tree.cond); 1165 scan(tree.thenpart); 1166 if (tree.elsepart != null) { 1167 scan(tree.elsepart); 1168 } 1169 } 1170 1171 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { 1172 if (chk.subset(exc, caughtInTry)) { 1173 log.error(pos, "except.already.caught", exc); 1174 } else if (!chk.isUnchecked(pos, exc) && 1175 !isExceptionOrThrowable(exc) && 1176 !chk.intersects(exc, thrownInTry)) { 1177 log.error(pos, "except.never.thrown.in.try", exc); 1178 } else if (allowImprovedCatchAnalysis) { 1179 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); 1180 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an 1181 // unchecked exception, the result list would not be empty, as the augmented 1182 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked 1183 // exception, that would have been covered in the branch above 1184 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() && 1185 !isExceptionOrThrowable(exc)) { 1186 String key = catchableThrownTypes.length() == 1 ? 1187 "unreachable.catch" : 1188 "unreachable.catch.1"; 1189 log.warning(pos, key, catchableThrownTypes); 1190 } 1191 } 1192 } 1193 //where 1194 private boolean isExceptionOrThrowable(Type exc) { 1195 return exc.tsym == syms.throwableType.tsym || 1196 exc.tsym == syms.exceptionType.tsym; 1197 } 1198 1199 public void visitBreak(JCBreak tree) { 1200 recordExit(new FlowPendingExit(tree, null)); 1201 } 1202 1203 public void visitContinue(JCContinue tree) { 1204 recordExit(new FlowPendingExit(tree, null)); 1205 } 1206 1207 public void visitReturn(JCReturn tree) { 1208 scan(tree.expr); 1209 recordExit(new FlowPendingExit(tree, null)); 1210 } 1211 1212 public void visitThrow(JCThrow tree) { 1213 scan(tree.expr); 1214 Symbol sym = TreeInfo.symbol(tree.expr); 1215 if (sym != null && 1216 sym.kind == VAR && 1217 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && 1218 preciseRethrowTypes.get(sym) != null && 1219 allowImprovedRethrowAnalysis) { 1220 for (Type t : preciseRethrowTypes.get(sym)) { 1221 markThrown(tree, t); 1222 } 1223 } 1224 else { 1225 markThrown(tree, tree.expr.type); 1226 } 1227 markDead(); 1228 } 1229 1230 public void visitApply(JCMethodInvocation tree) { 1231 scan(tree.meth); 1232 scan(tree.args); 1233 for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) 1234 markThrown(tree, l.head); 1235 } 1236 1237 public void visitNewClass(JCNewClass tree) { 1238 scan(tree.encl); 1239 scan(tree.args); 1240 // scan(tree.def); 1241 for (List<Type> l = tree.constructorType.getThrownTypes(); 1242 l.nonEmpty(); 1243 l = l.tail) { 1244 markThrown(tree, l.head); 1245 } 1246 List<Type> caughtPrev = caught; 1247 try { 1248 // If the new class expression defines an anonymous class, 1249 // analysis of the anonymous constructor may encounter thrown 1250 // types which are unsubstituted type variables. 1251 // However, since the constructor's actual thrown types have 1252 // already been marked as thrown, it is safe to simply include 1253 // each of the constructor's formal thrown types in the set of 1254 // 'caught/declared to be thrown' types, for the duration of 1255 // the class def analysis. 1256 if (tree.def != null) 1257 for (List<Type> l = tree.constructor.type.getThrownTypes(); 1258 l.nonEmpty(); 1259 l = l.tail) { 1260 caught = chk.incl(l.head, caught); 1261 } 1262 scan(tree.def); 1263 } 1264 finally { 1265 caught = caughtPrev; 1266 } 1267 } 1268 1269 @Override 1270 public void visitLambda(JCLambda tree) { 1271 if (tree.type != null && 1272 tree.type.isErroneous()) { 1273 return; 1274 } 1275 List<Type> prevCaught = caught; 1276 List<Type> prevThrown = thrown; 1277 ListBuffer<FlowPendingExit> prevPending = pendingExits; 1278 try { 1279 pendingExits = new ListBuffer<>(); 1280 caught = tree.getDescriptorType(types).getThrownTypes(); 1281 thrown = List.nil(); 1282 scan(tree.body); 1283 List<FlowPendingExit> exits = pendingExits.toList(); 1284 pendingExits = new ListBuffer<>(); 1285 while (exits.nonEmpty()) { 1286 FlowPendingExit exit = exits.head; 1287 exits = exits.tail; 1288 if (exit.thrown == null) { 1289 Assert.check(exit.tree.hasTag(RETURN)); 1290 } else { 1291 // uncaught throws will be reported later 1292 pendingExits.append(exit); 1293 } 1294 } 1295 1296 errorUncaught(); 1297 } finally { 1298 pendingExits = prevPending; 1299 caught = prevCaught; 1300 thrown = prevThrown; 1301 } 1302 } 1303 1304 public void visitModuleDef(JCModuleDecl tree) { 1305 // Do nothing for modules 1306 } 1307 1308 /************************************************************************** 1309 * main method 1310 *************************************************************************/ 1311 1312 /** Perform definite assignment/unassignment analysis on a tree. 1313 */ 1314 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 1315 analyzeTree(env, env.tree, make); 1316 } 1317 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 1318 try { 1319 attrEnv = env; 1320 Flow.this.make = make; 1321 pendingExits = new ListBuffer<>(); 1322 preciseRethrowTypes = new HashMap<>(); 1323 this.thrown = this.caught = null; 1324 this.classDef = null; 1325 scan(tree); 1326 } finally { 1327 pendingExits = null; 1328 Flow.this.make = null; 1329 this.thrown = this.caught = null; 1330 this.classDef = null; 1331 } 1332 } 1333 } 1334 1335 /** 1336 * Specialized pass that performs reachability analysis on a lambda 1337 */ 1338 class LambdaAliveAnalyzer extends AliveAnalyzer { 1339 1340 boolean inLambda; 1341 1342 @Override 1343 public void visitReturn(JCReturn tree) { 1344 //ignore lambda return expression (which might not even be attributed) 1345 recordExit(new PendingExit(tree)); 1346 } 1347 1348 @Override 1349 public void visitLambda(JCLambda tree) { 1350 if (inLambda || tree.getBodyKind() == BodyKind.EXPRESSION) { 1351 return; 1352 } 1353 inLambda = true; 1354 try { 1355 super.visitLambda(tree); 1356 } finally { 1357 inLambda = false; 1358 } 1359 } 1360 1361 @Override 1362 public void visitClassDef(JCClassDecl tree) { 1363 //skip 1364 } 1365 } 1366 1367 /** 1368 * Specialized pass that performs DA/DU on a lambda 1369 */ 1370 class LambdaAssignAnalyzer extends AssignAnalyzer { 1371 WriteableScope enclosedSymbols; 1372 boolean inLambda; 1373 1374 LambdaAssignAnalyzer(Env<AttrContext> env) { 1375 enclosedSymbols = WriteableScope.create(env.enclClass.sym); 1376 } 1377 1378 @Override 1379 public void visitLambda(JCLambda tree) { 1380 if (inLambda) { 1381 return; 1382 } 1383 inLambda = true; 1384 try { 1385 super.visitLambda(tree); 1386 } finally { 1387 inLambda = false; 1388 } 1389 } 1390 1391 @Override 1392 public void visitVarDef(JCVariableDecl tree) { 1393 enclosedSymbols.enter(tree.sym); 1394 super.visitVarDef(tree); 1395 } 1396 @Override 1397 protected boolean trackable(VarSymbol sym) { 1398 return enclosedSymbols.includes(sym) && 1399 sym.owner.kind == MTH; 1400 } 1401 1402 @Override 1403 public void visitClassDef(JCClassDecl tree) { 1404 //skip 1405 } 1406 } 1407 1408 /** 1409 * Specialized pass that performs inference of thrown types for lambdas. 1410 */ 1411 class LambdaFlowAnalyzer extends FlowAnalyzer { 1412 List<Type> inferredThrownTypes; 1413 boolean inLambda; 1414 @Override 1415 public void visitLambda(JCLambda tree) { 1416 if ((tree.type != null && 1417 tree.type.isErroneous()) || inLambda) { 1418 return; 1419 } 1420 List<Type> prevCaught = caught; 1421 List<Type> prevThrown = thrown; 1422 ListBuffer<FlowPendingExit> prevPending = pendingExits; 1423 inLambda = true; 1424 try { 1425 pendingExits = new ListBuffer<>(); 1426 caught = List.of(syms.throwableType); 1427 thrown = List.nil(); 1428 scan(tree.body); 1429 inferredThrownTypes = thrown; 1430 } finally { 1431 pendingExits = prevPending; 1432 caught = prevCaught; 1433 thrown = prevThrown; 1434 inLambda = false; 1435 } 1436 } 1437 @Override 1438 public void visitClassDef(JCClassDecl tree) { 1439 //skip 1440 } 1441 } 1442 1443 /** 1444 * This pass implements (i) definite assignment analysis, which ensures that 1445 * each variable is assigned when used and (ii) definite unassignment analysis, 1446 * which ensures that no final variable is assigned more than once. This visitor 1447 * depends on the results of the liveliness analyzer. This pass is also used to mark 1448 * effectively-final local variables/parameters. 1449 */ 1450 1451 public class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> { 1452 1453 /** The set of definitely assigned variables. 1454 */ 1455 final Bits inits; 1456 1457 /** The set of definitely unassigned variables. 1458 */ 1459 final Bits uninits; 1460 1461 /** The set of variables that are definitely unassigned everywhere 1462 * in current try block. This variable is maintained lazily; it is 1463 * updated only when something gets removed from uninits, 1464 * typically by being assigned in reachable code. To obtain the 1465 * correct set of variables which are definitely unassigned 1466 * anywhere in current try block, intersect uninitsTry and 1467 * uninits. 1468 */ 1469 final Bits uninitsTry; 1470 1471 /** When analyzing a condition, inits and uninits are null. 1472 * Instead we have: 1473 */ 1474 final Bits initsWhenTrue; 1475 final Bits initsWhenFalse; 1476 final Bits uninitsWhenTrue; 1477 final Bits uninitsWhenFalse; 1478 1479 /** A mapping from addresses to variable symbols. 1480 */ 1481 protected JCVariableDecl[] vardecls; 1482 1483 /** The current class being defined. 1484 */ 1485 JCClassDecl classDef; 1486 1487 /** The first variable sequence number in this class definition. 1488 */ 1489 int firstadr; 1490 1491 /** The next available variable sequence number. 1492 */ 1493 protected int nextadr; 1494 1495 /** The first variable sequence number in a block that can return. 1496 */ 1497 protected int returnadr; 1498 1499 /** The list of unreferenced automatic resources. 1500 */ 1501 WriteableScope unrefdResources; 1502 1503 /** Modified when processing a loop body the second time for DU analysis. */ 1504 FlowKind flowKind = FlowKind.NORMAL; 1505 1506 /** The starting position of the analyzed tree */ 1507 int startPos; 1508 1509 public class AssignPendingExit extends BaseAnalyzer.PendingExit { 1510 1511 final Bits inits; 1512 final Bits uninits; 1513 final Bits exit_inits = new Bits(true); 1514 final Bits exit_uninits = new Bits(true); 1515 1516 public AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { 1517 super(tree); 1518 this.inits = inits; 1519 this.uninits = uninits; 1520 this.exit_inits.assign(inits); 1521 this.exit_uninits.assign(uninits); 1522 } 1523 1524 @Override 1525 public void resolveJump() { 1526 inits.andSet(exit_inits); 1527 uninits.andSet(exit_uninits); 1528 } 1529 } 1530 1531 public AssignAnalyzer() { 1532 this.inits = new Bits(); 1533 uninits = new Bits(); 1534 uninitsTry = new Bits(); 1535 initsWhenTrue = new Bits(true); 1536 initsWhenFalse = new Bits(true); 1537 uninitsWhenTrue = new Bits(true); 1538 uninitsWhenFalse = new Bits(true); 1539 } 1540 1541 private boolean isInitialConstructor = false; 1542 1543 @Override 1544 protected void markDead() { 1545 if (!isInitialConstructor) { 1546 inits.inclRange(returnadr, nextadr); 1547 } else { 1548 for (int address = returnadr; address < nextadr; address++) { 1549 if (!(isFinalUninitializedStaticField(vardecls[address].sym))) { 1550 inits.incl(address); 1551 } 1552 } 1553 } 1554 uninits.inclRange(returnadr, nextadr); 1555 } 1556 1557 /*-------------- Processing variables ----------------------*/ 1558 1559 /** Do we need to track init/uninit state of this symbol? 1560 * I.e. is symbol either a local or a blank final variable? 1561 */ 1562 protected boolean trackable(VarSymbol sym) { 1563 return 1564 sym.pos >= startPos && 1565 ((sym.owner.kind == MTH || 1566 isFinalUninitializedField(sym))); 1567 } 1568 1569 boolean isFinalUninitializedField(VarSymbol sym) { 1570 return sym.owner.kind == TYP && 1571 ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && 1572 classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); 1573 } 1574 1575 boolean isFinalUninitializedStaticField(VarSymbol sym) { 1576 return isFinalUninitializedField(sym) && sym.isStatic(); 1577 } 1578 1579 /** Initialize new trackable variable by setting its address field 1580 * to the next available sequence number and entering it under that 1581 * index into the vars array. 1582 */ 1583 void newVar(JCVariableDecl varDecl) { 1584 VarSymbol sym = varDecl.sym; 1585 vardecls = ArrayUtils.ensureCapacity(vardecls, nextadr); 1586 if ((sym.flags() & FINAL) == 0) { 1587 sym.flags_field |= EFFECTIVELY_FINAL; 1588 } 1589 sym.adr = nextadr; 1590 vardecls[nextadr] = varDecl; 1591 inits.excl(nextadr); 1592 uninits.incl(nextadr); 1593 nextadr++; 1594 } 1595 1596 /** Record an initialization of a trackable variable. 1597 */ 1598 void letInit(DiagnosticPosition pos, VarSymbol sym) { 1599 if (sym.adr >= firstadr && trackable(sym)) { 1600 if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { 1601 if (!uninits.isMember(sym.adr)) { 1602 //assignment targeting an effectively final variable 1603 //makes the variable lose its status of effectively final 1604 //if the variable is _not_ definitively unassigned 1605 sym.flags_field &= ~EFFECTIVELY_FINAL; 1606 } else { 1607 uninit(sym); 1608 } 1609 } 1610 else if ((sym.flags() & FINAL) != 0) { 1611 if ((sym.flags() & PARAMETER) != 0) { 1612 if ((sym.flags() & UNION) != 0) { //multi-catch parameter 1613 log.error(pos, "multicatch.parameter.may.not.be.assigned", sym); 1614 } 1615 else { 1616 log.error(pos, "final.parameter.may.not.be.assigned", 1617 sym); 1618 } 1619 } else if (!uninits.isMember(sym.adr)) { 1620 log.error(pos, flowKind.errKey, sym); 1621 } else { 1622 uninit(sym); 1623 } 1624 } 1625 inits.incl(sym.adr); 1626 } else if ((sym.flags() & FINAL) != 0) { 1627 log.error(pos, "var.might.already.be.assigned", sym); 1628 } 1629 } 1630 //where 1631 void uninit(VarSymbol sym) { 1632 if (!inits.isMember(sym.adr)) { 1633 // reachable assignment 1634 uninits.excl(sym.adr); 1635 uninitsTry.excl(sym.adr); 1636 } else { 1637 //log.rawWarning(pos, "unreachable assignment");//DEBUG 1638 uninits.excl(sym.adr); 1639 } 1640 } 1641 1642 /** If tree is either a simple name or of the form this.name or 1643 * C.this.name, and tree represents a trackable variable, 1644 * record an initialization of the variable. 1645 */ 1646 void letInit(JCTree tree) { 1647 tree = TreeInfo.skipParens(tree); 1648 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 1649 Symbol sym = TreeInfo.symbol(tree); 1650 if (sym.kind == VAR) { 1651 letInit(tree.pos(), (VarSymbol)sym); 1652 } 1653 } 1654 } 1655 1656 /** Check that trackable variable is initialized. 1657 */ 1658 void checkInit(DiagnosticPosition pos, VarSymbol sym) { 1659 checkInit(pos, sym, "var.might.not.have.been.initialized"); 1660 } 1661 1662 void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) { 1663 if ((sym.adr >= firstadr || sym.owner.kind != TYP) && 1664 trackable(sym) && 1665 !inits.isMember(sym.adr)) { 1666 log.error(pos, errkey, sym); 1667 inits.incl(sym.adr); 1668 } 1669 } 1670 1671 /** Utility method to reset several Bits instances. 1672 */ 1673 private void resetBits(Bits... bits) { 1674 for (Bits b : bits) { 1675 b.reset(); 1676 } 1677 } 1678 1679 /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets 1680 */ 1681 void split(boolean setToNull) { 1682 initsWhenFalse.assign(inits); 1683 uninitsWhenFalse.assign(uninits); 1684 initsWhenTrue.assign(inits); 1685 uninitsWhenTrue.assign(uninits); 1686 if (setToNull) { 1687 resetBits(inits, uninits); 1688 } 1689 } 1690 1691 /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. 1692 */ 1693 protected void merge() { 1694 inits.assign(initsWhenFalse.andSet(initsWhenTrue)); 1695 uninits.assign(uninitsWhenFalse.andSet(uninitsWhenTrue)); 1696 } 1697 1698 /* ************************************************************************ 1699 * Visitor methods for statements and definitions 1700 *************************************************************************/ 1701 1702 /** Analyze an expression. Make sure to set (un)inits rather than 1703 * (un)initsWhenTrue(WhenFalse) on exit. 1704 */ 1705 void scanExpr(JCTree tree) { 1706 if (tree != null) { 1707 scan(tree); 1708 if (inits.isReset()) { 1709 merge(); 1710 } 1711 } 1712 } 1713 1714 /** Analyze a list of expressions. 1715 */ 1716 void scanExprs(List<? extends JCExpression> trees) { 1717 if (trees != null) 1718 for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail) 1719 scanExpr(l.head); 1720 } 1721 1722 /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) 1723 * rather than (un)inits on exit. 1724 */ 1725 void scanCond(JCTree tree) { 1726 if (tree.type.isFalse()) { 1727 if (inits.isReset()) merge(); 1728 initsWhenTrue.assign(inits); 1729 initsWhenTrue.inclRange(firstadr, nextadr); 1730 uninitsWhenTrue.assign(uninits); 1731 uninitsWhenTrue.inclRange(firstadr, nextadr); 1732 initsWhenFalse.assign(inits); 1733 uninitsWhenFalse.assign(uninits); 1734 } else if (tree.type.isTrue()) { 1735 if (inits.isReset()) merge(); 1736 initsWhenFalse.assign(inits); 1737 initsWhenFalse.inclRange(firstadr, nextadr); 1738 uninitsWhenFalse.assign(uninits); 1739 uninitsWhenFalse.inclRange(firstadr, nextadr); 1740 initsWhenTrue.assign(inits); 1741 uninitsWhenTrue.assign(uninits); 1742 } else { 1743 scan(tree); 1744 if (!inits.isReset()) 1745 split(tree.type != syms.unknownType); 1746 } 1747 if (tree.type != syms.unknownType) { 1748 resetBits(inits, uninits); 1749 } 1750 } 1751 1752 /* ------------ Visitor methods for various sorts of trees -------------*/ 1753 1754 public void visitClassDef(JCClassDecl tree) { 1755 if (tree.sym == null) { 1756 return; 1757 } 1758 1759 Lint lintPrev = lint; 1760 lint = lint.augment(tree.sym); 1761 try { 1762 if (tree.sym == null) { 1763 return; 1764 } 1765 1766 JCClassDecl classDefPrev = classDef; 1767 int firstadrPrev = firstadr; 1768 int nextadrPrev = nextadr; 1769 ListBuffer<AssignPendingExit> pendingExitsPrev = pendingExits; 1770 1771 pendingExits = new ListBuffer<>(); 1772 if (tree.name != names.empty) { 1773 firstadr = nextadr; 1774 } 1775 classDef = tree; 1776 try { 1777 // define all the static fields 1778 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1779 if (l.head.hasTag(VARDEF)) { 1780 JCVariableDecl def = (JCVariableDecl)l.head; 1781 if ((def.mods.flags & STATIC) != 0) { 1782 VarSymbol sym = def.sym; 1783 if (trackable(sym)) { 1784 newVar(def); 1785 } 1786 } 1787 } 1788 } 1789 1790 // process all the static initializers 1791 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1792 if (!l.head.hasTag(METHODDEF) && 1793 (TreeInfo.flags(l.head) & STATIC) != 0) { 1794 scan(l.head); 1795 } 1796 } 1797 1798 // define all the instance fields 1799 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1800 if (l.head.hasTag(VARDEF)) { 1801 JCVariableDecl def = (JCVariableDecl)l.head; 1802 if ((def.mods.flags & STATIC) == 0) { 1803 VarSymbol sym = def.sym; 1804 if (trackable(sym)) { 1805 newVar(def); 1806 } 1807 } 1808 } 1809 } 1810 1811 // process all the instance initializers 1812 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1813 if (!l.head.hasTag(METHODDEF) && 1814 (TreeInfo.flags(l.head) & STATIC) == 0) { 1815 scan(l.head); 1816 } 1817 } 1818 1819 // process all the methods 1820 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1821 if (l.head.hasTag(METHODDEF)) { 1822 scan(l.head); 1823 } 1824 } 1825 } finally { 1826 pendingExits = pendingExitsPrev; 1827 nextadr = nextadrPrev; 1828 firstadr = firstadrPrev; 1829 classDef = classDefPrev; 1830 } 1831 } finally { 1832 lint = lintPrev; 1833 } 1834 } 1835 1836 public void visitMethodDef(JCMethodDecl tree) { 1837 if (tree.body == null) { 1838 return; 1839 } 1840 1841 /* MemberEnter can generate synthetic methods ignore them 1842 */ 1843 if ((tree.sym.flags() & SYNTHETIC) != 0) { 1844 return; 1845 } 1846 1847 Lint lintPrev = lint; 1848 lint = lint.augment(tree.sym); 1849 try { 1850 if (tree.body == null) { 1851 return; 1852 } 1853 /* Ignore synthetic methods, except for translated lambda methods. 1854 */ 1855 if ((tree.sym.flags() & (SYNTHETIC | LAMBDA_METHOD)) == SYNTHETIC) { 1856 return; 1857 } 1858 1859 final Bits initsPrev = new Bits(inits); 1860 final Bits uninitsPrev = new Bits(uninits); 1861 int nextadrPrev = nextadr; 1862 int firstadrPrev = firstadr; 1863 int returnadrPrev = returnadr; 1864 1865 Assert.check(pendingExits.isEmpty()); 1866 boolean lastInitialConstructor = isInitialConstructor; 1867 try { 1868 isInitialConstructor = TreeInfo.isInitialConstructor(tree); 1869 1870 if (!isInitialConstructor) { 1871 firstadr = nextadr; 1872 } 1873 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 1874 JCVariableDecl def = l.head; 1875 scan(def); 1876 Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); 1877 /* If we are executing the code from Gen, then there can be 1878 * synthetic or mandated variables, ignore them. 1879 */ 1880 initParam(def); 1881 } 1882 // else we are in an instance initializer block; 1883 // leave caught unchanged. 1884 scan(tree.body); 1885 1886 if (isInitialConstructor) { 1887 boolean isSynthesized = (tree.sym.flags() & 1888 GENERATEDCONSTR) != 0; 1889 for (int i = firstadr; i < nextadr; i++) { 1890 JCVariableDecl vardecl = vardecls[i]; 1891 VarSymbol var = vardecl.sym; 1892 if (var.owner == classDef.sym) { 1893 // choose the diagnostic position based on whether 1894 // the ctor is default(synthesized) or not 1895 if (isSynthesized) { 1896 checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), 1897 var, "var.not.initialized.in.default.constructor"); 1898 } else { 1899 checkInit(TreeInfo.diagEndPos(tree.body), var); 1900 } 1901 } 1902 } 1903 } 1904 List<AssignPendingExit> exits = pendingExits.toList(); 1905 pendingExits = new ListBuffer<>(); 1906 while (exits.nonEmpty()) { 1907 AssignPendingExit exit = exits.head; 1908 exits = exits.tail; 1909 Assert.check(exit.tree.hasTag(RETURN), exit.tree); 1910 if (isInitialConstructor) { 1911 inits.assign(exit.exit_inits); 1912 for (int i = firstadr; i < nextadr; i++) { 1913 checkInit(exit.tree.pos(), vardecls[i].sym); 1914 } 1915 } 1916 } 1917 } finally { 1918 inits.assign(initsPrev); 1919 uninits.assign(uninitsPrev); 1920 nextadr = nextadrPrev; 1921 firstadr = firstadrPrev; 1922 returnadr = returnadrPrev; 1923 isInitialConstructor = lastInitialConstructor; 1924 } 1925 } finally { 1926 lint = lintPrev; 1927 } 1928 } 1929 1930 protected void initParam(JCVariableDecl def) { 1931 inits.incl(def.sym.adr); 1932 uninits.excl(def.sym.adr); 1933 } 1934 1935 public void visitVarDef(JCVariableDecl tree) { 1936 Lint lintPrev = lint; 1937 lint = lint.augment(tree.sym); 1938 try{ 1939 boolean track = trackable(tree.sym); 1940 if (track && tree.sym.owner.kind == MTH) { 1941 newVar(tree); 1942 } 1943 if (tree.init != null) { 1944 scanExpr(tree.init); 1945 if (track) { 1946 letInit(tree.pos(), tree.sym); 1947 } 1948 } 1949 } finally { 1950 lint = lintPrev; 1951 } 1952 } 1953 1954 public void visitBlock(JCBlock tree) { 1955 int nextadrPrev = nextadr; 1956 scan(tree.stats); 1957 nextadr = nextadrPrev; 1958 } 1959 1960 public void visitDoLoop(JCDoWhileLoop tree) { 1961 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 1962 FlowKind prevFlowKind = flowKind; 1963 flowKind = FlowKind.NORMAL; 1964 final Bits initsSkip = new Bits(true); 1965 final Bits uninitsSkip = new Bits(true); 1966 pendingExits = new ListBuffer<>(); 1967 int prevErrors = log.nerrors; 1968 do { 1969 final Bits uninitsEntry = new Bits(uninits); 1970 uninitsEntry.excludeFrom(nextadr); 1971 scan(tree.body); 1972 resolveContinues(tree); 1973 scanCond(tree.cond); 1974 if (!flowKind.isFinal()) { 1975 initsSkip.assign(initsWhenFalse); 1976 uninitsSkip.assign(uninitsWhenFalse); 1977 } 1978 if (log.nerrors != prevErrors || 1979 flowKind.isFinal() || 1980 new Bits(uninitsEntry).diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) 1981 break; 1982 inits.assign(initsWhenTrue); 1983 uninits.assign(uninitsEntry.andSet(uninitsWhenTrue)); 1984 flowKind = FlowKind.SPECULATIVE_LOOP; 1985 } while (true); 1986 flowKind = prevFlowKind; 1987 inits.assign(initsSkip); 1988 uninits.assign(uninitsSkip); 1989 resolveBreaks(tree, prevPendingExits); 1990 } 1991 1992 public void visitWhileLoop(JCWhileLoop tree) { 1993 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 1994 FlowKind prevFlowKind = flowKind; 1995 flowKind = FlowKind.NORMAL; 1996 final Bits initsSkip = new Bits(true); 1997 final Bits uninitsSkip = new Bits(true); 1998 pendingExits = new ListBuffer<>(); 1999 int prevErrors = log.nerrors; 2000 final Bits uninitsEntry = new Bits(uninits); 2001 uninitsEntry.excludeFrom(nextadr); 2002 do { 2003 scanCond(tree.cond); 2004 if (!flowKind.isFinal()) { 2005 initsSkip.assign(initsWhenFalse) ; 2006 uninitsSkip.assign(uninitsWhenFalse); 2007 } 2008 inits.assign(initsWhenTrue); 2009 uninits.assign(uninitsWhenTrue); 2010 scan(tree.body); 2011 resolveContinues(tree); 2012 if (log.nerrors != prevErrors || 2013 flowKind.isFinal() || 2014 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) { 2015 break; 2016 } 2017 uninits.assign(uninitsEntry.andSet(uninits)); 2018 flowKind = FlowKind.SPECULATIVE_LOOP; 2019 } while (true); 2020 flowKind = prevFlowKind; 2021 //a variable is DA/DU after the while statement, if it's DA/DU assuming the 2022 //branch is not taken AND if it's DA/DU before any break statement 2023 inits.assign(initsSkip); 2024 uninits.assign(uninitsSkip); 2025 resolveBreaks(tree, prevPendingExits); 2026 } 2027 2028 public void visitForLoop(JCForLoop tree) { 2029 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2030 FlowKind prevFlowKind = flowKind; 2031 flowKind = FlowKind.NORMAL; 2032 int nextadrPrev = nextadr; 2033 scan(tree.init); 2034 final Bits initsSkip = new Bits(true); 2035 final Bits uninitsSkip = new Bits(true); 2036 pendingExits = new ListBuffer<>(); 2037 int prevErrors = log.nerrors; 2038 do { 2039 final Bits uninitsEntry = new Bits(uninits); 2040 uninitsEntry.excludeFrom(nextadr); 2041 if (tree.cond != null) { 2042 scanCond(tree.cond); 2043 if (!flowKind.isFinal()) { 2044 initsSkip.assign(initsWhenFalse); 2045 uninitsSkip.assign(uninitsWhenFalse); 2046 } 2047 inits.assign(initsWhenTrue); 2048 uninits.assign(uninitsWhenTrue); 2049 } else if (!flowKind.isFinal()) { 2050 initsSkip.assign(inits); 2051 initsSkip.inclRange(firstadr, nextadr); 2052 uninitsSkip.assign(uninits); 2053 uninitsSkip.inclRange(firstadr, nextadr); 2054 } 2055 scan(tree.body); 2056 resolveContinues(tree); 2057 scan(tree.step); 2058 if (log.nerrors != prevErrors || 2059 flowKind.isFinal() || 2060 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2061 break; 2062 uninits.assign(uninitsEntry.andSet(uninits)); 2063 flowKind = FlowKind.SPECULATIVE_LOOP; 2064 } while (true); 2065 flowKind = prevFlowKind; 2066 //a variable is DA/DU after a for loop, if it's DA/DU assuming the 2067 //branch is not taken AND if it's DA/DU before any break statement 2068 inits.assign(initsSkip); 2069 uninits.assign(uninitsSkip); 2070 resolveBreaks(tree, prevPendingExits); 2071 nextadr = nextadrPrev; 2072 } 2073 2074 public void visitForeachLoop(JCEnhancedForLoop tree) { 2075 visitVarDef(tree.var); 2076 2077 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2078 FlowKind prevFlowKind = flowKind; 2079 flowKind = FlowKind.NORMAL; 2080 int nextadrPrev = nextadr; 2081 scan(tree.expr); 2082 final Bits initsStart = new Bits(inits); 2083 final Bits uninitsStart = new Bits(uninits); 2084 2085 letInit(tree.pos(), tree.var.sym); 2086 pendingExits = new ListBuffer<>(); 2087 int prevErrors = log.nerrors; 2088 do { 2089 final Bits uninitsEntry = new Bits(uninits); 2090 uninitsEntry.excludeFrom(nextadr); 2091 scan(tree.body); 2092 resolveContinues(tree); 2093 if (log.nerrors != prevErrors || 2094 flowKind.isFinal() || 2095 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2096 break; 2097 uninits.assign(uninitsEntry.andSet(uninits)); 2098 flowKind = FlowKind.SPECULATIVE_LOOP; 2099 } while (true); 2100 flowKind = prevFlowKind; 2101 inits.assign(initsStart); 2102 uninits.assign(uninitsStart.andSet(uninits)); 2103 resolveBreaks(tree, prevPendingExits); 2104 nextadr = nextadrPrev; 2105 } 2106 2107 public void visitLabelled(JCLabeledStatement tree) { 2108 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2109 pendingExits = new ListBuffer<>(); 2110 scan(tree.body); 2111 resolveBreaks(tree, prevPendingExits); 2112 } 2113 2114 public void visitSwitch(JCSwitch tree) { 2115 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2116 pendingExits = new ListBuffer<>(); 2117 int nextadrPrev = nextadr; 2118 scanExpr(tree.selector); 2119 final Bits initsSwitch = new Bits(inits); 2120 final Bits uninitsSwitch = new Bits(uninits); 2121 boolean hasDefault = false; 2122 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 2123 inits.assign(initsSwitch); 2124 uninits.assign(uninits.andSet(uninitsSwitch)); 2125 JCCase c = l.head; 2126 if (c.pat == null) { 2127 hasDefault = true; 2128 } else { 2129 scanExpr(c.pat); 2130 } 2131 if (hasDefault) { 2132 inits.assign(initsSwitch); 2133 uninits.assign(uninits.andSet(uninitsSwitch)); 2134 } 2135 scan(c.stats); 2136 addVars(c.stats, initsSwitch, uninitsSwitch); 2137 if (!hasDefault) { 2138 inits.assign(initsSwitch); 2139 uninits.assign(uninits.andSet(uninitsSwitch)); 2140 } 2141 // Warn about fall-through if lint switch fallthrough enabled. 2142 } 2143 if (!hasDefault) { 2144 inits.andSet(initsSwitch); 2145 } 2146 resolveBreaks(tree, prevPendingExits); 2147 nextadr = nextadrPrev; 2148 } 2149 // where 2150 /** Add any variables defined in stats to inits and uninits. */ 2151 private void addVars(List<JCStatement> stats, final Bits inits, 2152 final Bits uninits) { 2153 for (;stats.nonEmpty(); stats = stats.tail) { 2154 JCTree stat = stats.head; 2155 if (stat.hasTag(VARDEF)) { 2156 int adr = ((JCVariableDecl) stat).sym.adr; 2157 inits.excl(adr); 2158 uninits.incl(adr); 2159 } 2160 } 2161 } 2162 2163 public void visitTry(JCTry tree) { 2164 ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>(); 2165 final Bits uninitsTryPrev = new Bits(uninitsTry); 2166 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2167 pendingExits = new ListBuffer<>(); 2168 final Bits initsTry = new Bits(inits); 2169 uninitsTry.assign(uninits); 2170 for (JCTree resource : tree.resources) { 2171 if (resource instanceof JCVariableDecl) { 2172 JCVariableDecl vdecl = (JCVariableDecl) resource; 2173 visitVarDef(vdecl); 2174 unrefdResources.enter(vdecl.sym); 2175 resourceVarDecls.append(vdecl); 2176 } else if (resource instanceof JCExpression) { 2177 scanExpr((JCExpression) resource); 2178 } else { 2179 throw new AssertionError(tree); // parser error 2180 } 2181 } 2182 scan(tree.body); 2183 uninitsTry.andSet(uninits); 2184 final Bits initsEnd = new Bits(inits); 2185 final Bits uninitsEnd = new Bits(uninits); 2186 int nextadrCatch = nextadr; 2187 2188 if (!resourceVarDecls.isEmpty() && 2189 lint.isEnabled(Lint.LintCategory.TRY)) { 2190 for (JCVariableDecl resVar : resourceVarDecls) { 2191 if (unrefdResources.includes(resVar.sym)) { 2192 log.warning(Lint.LintCategory.TRY, resVar.pos(), 2193 "try.resource.not.referenced", resVar.sym); 2194 unrefdResources.remove(resVar.sym); 2195 } 2196 } 2197 } 2198 2199 /* The analysis of each catch should be independent. 2200 * Each one should have the same initial values of inits and 2201 * uninits. 2202 */ 2203 final Bits initsCatchPrev = new Bits(initsTry); 2204 final Bits uninitsCatchPrev = new Bits(uninitsTry); 2205 2206 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 2207 JCVariableDecl param = l.head.param; 2208 inits.assign(initsCatchPrev); 2209 uninits.assign(uninitsCatchPrev); 2210 scan(param); 2211 /* If this is a TWR and we are executing the code from Gen, 2212 * then there can be synthetic variables, ignore them. 2213 */ 2214 initParam(param); 2215 scan(l.head.body); 2216 initsEnd.andSet(inits); 2217 uninitsEnd.andSet(uninits); 2218 nextadr = nextadrCatch; 2219 } 2220 if (tree.finalizer != null) { 2221 inits.assign(initsTry); 2222 uninits.assign(uninitsTry); 2223 ListBuffer<AssignPendingExit> exits = pendingExits; 2224 pendingExits = prevPendingExits; 2225 scan(tree.finalizer); 2226 if (!tree.finallyCanCompleteNormally) { 2227 // discard exits and exceptions from try and finally 2228 } else { 2229 uninits.andSet(uninitsEnd); 2230 // FIX: this doesn't preserve source order of exits in catch 2231 // versus finally! 2232 while (exits.nonEmpty()) { 2233 AssignPendingExit exit = exits.next(); 2234 if (exit.exit_inits != null) { 2235 exit.exit_inits.orSet(inits); 2236 exit.exit_uninits.andSet(uninits); 2237 } 2238 pendingExits.append(exit); 2239 } 2240 inits.orSet(initsEnd); 2241 } 2242 } else { 2243 inits.assign(initsEnd); 2244 uninits.assign(uninitsEnd); 2245 ListBuffer<AssignPendingExit> exits = pendingExits; 2246 pendingExits = prevPendingExits; 2247 while (exits.nonEmpty()) pendingExits.append(exits.next()); 2248 } 2249 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); 2250 } 2251 2252 public void visitConditional(JCConditional tree) { 2253 scanCond(tree.cond); 2254 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2255 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2256 inits.assign(initsWhenTrue); 2257 uninits.assign(uninitsWhenTrue); 2258 if (tree.truepart.type.hasTag(BOOLEAN) && 2259 tree.falsepart.type.hasTag(BOOLEAN)) { 2260 // if b and c are boolean valued, then 2261 // v is (un)assigned after a?b:c when true iff 2262 // v is (un)assigned after b when true and 2263 // v is (un)assigned after c when true 2264 scanCond(tree.truepart); 2265 final Bits initsAfterThenWhenTrue = new Bits(initsWhenTrue); 2266 final Bits initsAfterThenWhenFalse = new Bits(initsWhenFalse); 2267 final Bits uninitsAfterThenWhenTrue = new Bits(uninitsWhenTrue); 2268 final Bits uninitsAfterThenWhenFalse = new Bits(uninitsWhenFalse); 2269 inits.assign(initsBeforeElse); 2270 uninits.assign(uninitsBeforeElse); 2271 scanCond(tree.falsepart); 2272 initsWhenTrue.andSet(initsAfterThenWhenTrue); 2273 initsWhenFalse.andSet(initsAfterThenWhenFalse); 2274 uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); 2275 uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); 2276 } else { 2277 scanExpr(tree.truepart); 2278 final Bits initsAfterThen = new Bits(inits); 2279 final Bits uninitsAfterThen = new Bits(uninits); 2280 inits.assign(initsBeforeElse); 2281 uninits.assign(uninitsBeforeElse); 2282 scanExpr(tree.falsepart); 2283 inits.andSet(initsAfterThen); 2284 uninits.andSet(uninitsAfterThen); 2285 } 2286 } 2287 2288 public void visitIf(JCIf tree) { 2289 scanCond(tree.cond); 2290 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2291 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2292 inits.assign(initsWhenTrue); 2293 uninits.assign(uninitsWhenTrue); 2294 scan(tree.thenpart); 2295 if (tree.elsepart != null) { 2296 final Bits initsAfterThen = new Bits(inits); 2297 final Bits uninitsAfterThen = new Bits(uninits); 2298 inits.assign(initsBeforeElse); 2299 uninits.assign(uninitsBeforeElse); 2300 scan(tree.elsepart); 2301 inits.andSet(initsAfterThen); 2302 uninits.andSet(uninitsAfterThen); 2303 } else { 2304 inits.andSet(initsBeforeElse); 2305 uninits.andSet(uninitsBeforeElse); 2306 } 2307 } 2308 2309 @Override 2310 public void visitBreak(JCBreak tree) { 2311 recordExit(new AssignPendingExit(tree, inits, uninits)); 2312 } 2313 2314 @Override 2315 public void visitContinue(JCContinue tree) { 2316 recordExit(new AssignPendingExit(tree, inits, uninits)); 2317 } 2318 2319 @Override 2320 public void visitReturn(JCReturn tree) { 2321 scanExpr(tree.expr); 2322 recordExit(new AssignPendingExit(tree, inits, uninits)); 2323 } 2324 2325 public void visitThrow(JCThrow tree) { 2326 scanExpr(tree.expr); 2327 markDead(); 2328 } 2329 2330 public void visitApply(JCMethodInvocation tree) { 2331 scanExpr(tree.meth); 2332 scanExprs(tree.args); 2333 } 2334 2335 public void visitNewClass(JCNewClass tree) { 2336 scanExpr(tree.encl); 2337 scanExprs(tree.args); 2338 scan(tree.def); 2339 } 2340 2341 @Override 2342 public void visitLambda(JCLambda tree) { 2343 final Bits prevUninits = new Bits(uninits); 2344 final Bits prevInits = new Bits(inits); 2345 int returnadrPrev = returnadr; 2346 int nextadrPrev = nextadr; 2347 ListBuffer<AssignPendingExit> prevPending = pendingExits; 2348 try { 2349 returnadr = nextadr; 2350 pendingExits = new ListBuffer<>(); 2351 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 2352 JCVariableDecl def = l.head; 2353 scan(def); 2354 inits.incl(def.sym.adr); 2355 uninits.excl(def.sym.adr); 2356 } 2357 if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { 2358 scanExpr(tree.body); 2359 } else { 2360 scan(tree.body); 2361 } 2362 } 2363 finally { 2364 returnadr = returnadrPrev; 2365 uninits.assign(prevUninits); 2366 inits.assign(prevInits); 2367 pendingExits = prevPending; 2368 nextadr = nextadrPrev; 2369 } 2370 } 2371 2372 public void visitNewArray(JCNewArray tree) { 2373 scanExprs(tree.dims); 2374 scanExprs(tree.elems); 2375 } 2376 2377 public void visitAssert(JCAssert tree) { 2378 final Bits initsExit = new Bits(inits); 2379 final Bits uninitsExit = new Bits(uninits); 2380 scanCond(tree.cond); 2381 uninitsExit.andSet(uninitsWhenTrue); 2382 if (tree.detail != null) { 2383 inits.assign(initsWhenFalse); 2384 uninits.assign(uninitsWhenFalse); 2385 scanExpr(tree.detail); 2386 } 2387 inits.assign(initsExit); 2388 uninits.assign(uninitsExit); 2389 } 2390 2391 public void visitAssign(JCAssign tree) { 2392 JCTree lhs = TreeInfo.skipParens(tree.lhs); 2393 if (!isIdentOrThisDotIdent(lhs)) 2394 scanExpr(lhs); 2395 scanExpr(tree.rhs); 2396 letInit(lhs); 2397 } 2398 private boolean isIdentOrThisDotIdent(JCTree lhs) { 2399 if (lhs.hasTag(IDENT)) 2400 return true; 2401 if (!lhs.hasTag(SELECT)) 2402 return false; 2403 2404 JCFieldAccess fa = (JCFieldAccess)lhs; 2405 return fa.selected.hasTag(IDENT) && 2406 ((JCIdent)fa.selected).name == names._this; 2407 } 2408 2409 // check fields accessed through this.<field> are definitely 2410 // assigned before reading their value 2411 public void visitSelect(JCFieldAccess tree) { 2412 super.visitSelect(tree); 2413 JCTree sel = TreeInfo.skipParens(tree.selected); 2414 if (enforceThisDotInit && 2415 sel.hasTag(IDENT) && 2416 ((JCIdent)sel).name == names._this && 2417 tree.sym.kind == VAR) { 2418 checkInit(tree.pos(), (VarSymbol)tree.sym); 2419 } 2420 } 2421 2422 public void visitAssignop(JCAssignOp tree) { 2423 scanExpr(tree.lhs); 2424 scanExpr(tree.rhs); 2425 letInit(tree.lhs); 2426 } 2427 2428 public void visitUnary(JCUnary tree) { 2429 switch (tree.getTag()) { 2430 case NOT: 2431 scanCond(tree.arg); 2432 final Bits t = new Bits(initsWhenFalse); 2433 initsWhenFalse.assign(initsWhenTrue); 2434 initsWhenTrue.assign(t); 2435 t.assign(uninitsWhenFalse); 2436 uninitsWhenFalse.assign(uninitsWhenTrue); 2437 uninitsWhenTrue.assign(t); 2438 break; 2439 case PREINC: case POSTINC: 2440 case PREDEC: case POSTDEC: 2441 scanExpr(tree.arg); 2442 letInit(tree.arg); 2443 break; 2444 default: 2445 scanExpr(tree.arg); 2446 } 2447 } 2448 2449 public void visitBinary(JCBinary tree) { 2450 switch (tree.getTag()) { 2451 case AND: 2452 scanCond(tree.lhs); 2453 final Bits initsWhenFalseLeft = new Bits(initsWhenFalse); 2454 final Bits uninitsWhenFalseLeft = new Bits(uninitsWhenFalse); 2455 inits.assign(initsWhenTrue); 2456 uninits.assign(uninitsWhenTrue); 2457 scanCond(tree.rhs); 2458 initsWhenFalse.andSet(initsWhenFalseLeft); 2459 uninitsWhenFalse.andSet(uninitsWhenFalseLeft); 2460 break; 2461 case OR: 2462 scanCond(tree.lhs); 2463 final Bits initsWhenTrueLeft = new Bits(initsWhenTrue); 2464 final Bits uninitsWhenTrueLeft = new Bits(uninitsWhenTrue); 2465 inits.assign(initsWhenFalse); 2466 uninits.assign(uninitsWhenFalse); 2467 scanCond(tree.rhs); 2468 initsWhenTrue.andSet(initsWhenTrueLeft); 2469 uninitsWhenTrue.andSet(uninitsWhenTrueLeft); 2470 break; 2471 default: 2472 scanExpr(tree.lhs); 2473 scanExpr(tree.rhs); 2474 } 2475 } 2476 2477 public void visitIdent(JCIdent tree) { 2478 if (tree.sym.kind == VAR) { 2479 checkInit(tree.pos(), (VarSymbol)tree.sym); 2480 referenced(tree.sym); 2481 } 2482 } 2483 2484 void referenced(Symbol sym) { 2485 unrefdResources.remove(sym); 2486 } 2487 2488 public void visitAnnotatedType(JCAnnotatedType tree) { 2489 // annotations don't get scanned 2490 tree.underlyingType.accept(this); 2491 } 2492 2493 public void visitModuleDef(JCModuleDecl tree) { 2494 // Do nothing for modules 2495 } 2496 2497 /************************************************************************** 2498 * main method 2499 *************************************************************************/ 2500 2501 /** Perform definite assignment/unassignment analysis on a tree. 2502 */ 2503 public void analyzeTree(Env<?> env) { 2504 analyzeTree(env, env.tree); 2505 } 2506 2507 public void analyzeTree(Env<?> env, JCTree tree) { 2508 try { 2509 startPos = tree.pos().getStartPosition(); 2510 2511 if (vardecls == null) 2512 vardecls = new JCVariableDecl[32]; 2513 else 2514 for (int i=0; i<vardecls.length; i++) 2515 vardecls[i] = null; 2516 firstadr = 0; 2517 nextadr = 0; 2518 pendingExits = new ListBuffer<>(); 2519 this.classDef = null; 2520 unrefdResources = WriteableScope.create(env.enclClass.sym); 2521 scan(tree); 2522 } finally { 2523 // note that recursive invocations of this method fail hard 2524 startPos = -1; 2525 resetBits(inits, uninits, uninitsTry, initsWhenTrue, 2526 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse); 2527 if (vardecls != null) { 2528 for (int i=0; i<vardecls.length; i++) 2529 vardecls[i] = null; 2530 } 2531 firstadr = 0; 2532 nextadr = 0; 2533 pendingExits = null; 2534 this.classDef = null; 2535 unrefdResources = null; 2536 } 2537 } 2538 } 2539 2540 /** 2541 * This pass implements the last step of the dataflow analysis, namely 2542 * the effectively-final analysis check. This checks that every local variable 2543 * reference from a lambda body/local inner class is either final or effectively final. 2544 * Additional this also checks that every variable that is used as an operand to 2545 * try-with-resources is final or effectively final. 2546 * As effectively final variables are marked as such during DA/DU, this pass must run after 2547 * AssignAnalyzer. 2548 */ 2549 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { 2550 2551 JCTree currentTree; //local class or lambda 2552 2553 @Override 2554 void markDead() { 2555 //do nothing 2556 } 2557 2558 @SuppressWarnings("fallthrough") 2559 void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) { 2560 if (currentTree != null && 2561 sym.owner.kind == MTH && 2562 sym.pos < currentTree.getStartPosition()) { 2563 switch (currentTree.getTag()) { 2564 case CLASSDEF: 2565 if (!allowEffectivelyFinalInInnerClasses) { 2566 if ((sym.flags() & FINAL) == 0) { 2567 reportInnerClsNeedsFinalError(pos, sym); 2568 } 2569 break; 2570 } 2571 case LAMBDA: 2572 if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { 2573 reportEffectivelyFinalError(pos, sym); 2574 } 2575 } 2576 } 2577 } 2578 2579 @SuppressWarnings("fallthrough") 2580 void letInit(JCTree tree) { 2581 tree = TreeInfo.skipParens(tree); 2582 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 2583 Symbol sym = TreeInfo.symbol(tree); 2584 if (currentTree != null && 2585 sym.kind == VAR && 2586 sym.owner.kind == MTH && 2587 ((VarSymbol)sym).pos < currentTree.getStartPosition()) { 2588 switch (currentTree.getTag()) { 2589 case CLASSDEF: 2590 if (!allowEffectivelyFinalInInnerClasses) { 2591 reportInnerClsNeedsFinalError(tree, sym); 2592 break; 2593 } 2594 case LAMBDA: 2595 reportEffectivelyFinalError(tree, sym); 2596 } 2597 } 2598 } 2599 } 2600 2601 void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { 2602 String subKey = currentTree.hasTag(LAMBDA) ? 2603 "lambda" : "inner.cls"; 2604 log.error(pos, "cant.ref.non.effectively.final.var", sym, diags.fragment(subKey)); 2605 } 2606 2607 void reportInnerClsNeedsFinalError(DiagnosticPosition pos, Symbol sym) { 2608 log.error(pos, 2609 "local.var.accessed.from.icls.needs.final", 2610 sym); 2611 } 2612 2613 /************************************************************************* 2614 * Visitor methods for statements and definitions 2615 *************************************************************************/ 2616 2617 /* ------------ Visitor methods for various sorts of trees -------------*/ 2618 2619 public void visitClassDef(JCClassDecl tree) { 2620 JCTree prevTree = currentTree; 2621 try { 2622 currentTree = tree.sym.isLocal() ? tree : null; 2623 super.visitClassDef(tree); 2624 } finally { 2625 currentTree = prevTree; 2626 } 2627 } 2628 2629 @Override 2630 public void visitLambda(JCLambda tree) { 2631 JCTree prevTree = currentTree; 2632 try { 2633 currentTree = tree; 2634 super.visitLambda(tree); 2635 } finally { 2636 currentTree = prevTree; 2637 } 2638 } 2639 2640 @Override 2641 public void visitIdent(JCIdent tree) { 2642 if (tree.sym.kind == VAR) { 2643 checkEffectivelyFinal(tree, (VarSymbol)tree.sym); 2644 } 2645 } 2646 2647 public void visitAssign(JCAssign tree) { 2648 JCTree lhs = TreeInfo.skipParens(tree.lhs); 2649 if (!(lhs instanceof JCIdent)) { 2650 scan(lhs); 2651 } 2652 scan(tree.rhs); 2653 letInit(lhs); 2654 } 2655 2656 public void visitAssignop(JCAssignOp tree) { 2657 scan(tree.lhs); 2658 scan(tree.rhs); 2659 letInit(tree.lhs); 2660 } 2661 2662 public void visitUnary(JCUnary tree) { 2663 switch (tree.getTag()) { 2664 case PREINC: case POSTINC: 2665 case PREDEC: case POSTDEC: 2666 scan(tree.arg); 2667 letInit(tree.arg); 2668 break; 2669 default: 2670 scan(tree.arg); 2671 } 2672 } 2673 2674 public void visitTry(JCTry tree) { 2675 for (JCTree resource : tree.resources) { 2676 if (!resource.hasTag(VARDEF)) { 2677 Symbol var = TreeInfo.symbol(resource); 2678 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) { 2679 log.error(resource.pos(), "try.with.resources.expr.effectively.final.var", var); 2680 } 2681 } 2682 } 2683 super.visitTry(tree); 2684 } 2685 2686 public void visitModuleDef(JCModuleDecl tree) { 2687 // Do nothing for modules 2688 } 2689 2690 /************************************************************************** 2691 * main method 2692 *************************************************************************/ 2693 2694 /** Perform definite assignment/unassignment analysis on a tree. 2695 */ 2696 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 2697 analyzeTree(env, env.tree, make); 2698 } 2699 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 2700 try { 2701 attrEnv = env; 2702 Flow.this.make = make; 2703 pendingExits = new ListBuffer<>(); 2704 scan(tree); 2705 } finally { 2706 pendingExits = null; 2707 Flow.this.make = null; 2708 } 2709 } 2710 } 2711} 2712