duke@1: /* mcimadamore@1237: * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: //todo: one might eliminate uninits.andSets when monotonic duke@1: duke@1: package com.sun.tools.javac.comp; duke@1: mcimadamore@550: import java.util.HashMap; mcimadamore@550: duke@1: import com.sun.tools.javac.code.*; duke@1: import com.sun.tools.javac.tree.*; duke@1: import com.sun.tools.javac.util.*; duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; duke@1: duke@1: import com.sun.tools.javac.code.Symbol.*; duke@1: import com.sun.tools.javac.tree.JCTree.*; duke@1: duke@1: import static com.sun.tools.javac.code.Flags.*; jjg@1127: import static com.sun.tools.javac.code.Flags.BLOCK; duke@1: import static com.sun.tools.javac.code.Kinds.*; jjg@1374: import static com.sun.tools.javac.code.TypeTag.BOOLEAN; jjg@1374: import static com.sun.tools.javac.code.TypeTag.VOID; jjg@1127: import static com.sun.tools.javac.tree.JCTree.Tag.*; duke@1: mcimadamore@1297: /** This pass implements dataflow analysis for Java programs though mcimadamore@1297: * different AST visitor steps. Liveness analysis (see AliveAlanyzer) checks that mcimadamore@1297: * every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that mcimadamore@1297: * every checked exception that is thrown is declared or caught. Definite assignment analysis mcimadamore@1297: * (see AssignAnalyzer) ensures that each variable is assigned when used. Definite mcimadamore@1297: * unassignment analysis (see AssignAnalyzer) in ensures that no final variable mcimadamore@1297: * is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer) mcimadamore@1348: * determines that local variables accessed within the scope of an inner class/lambda mcimadamore@1348: * are either final or effectively-final. duke@1: * jjh@972: *

The JLS has a number of problems in the duke@1: * specification of these flow analysis problems. This implementation duke@1: * attempts to address those issues. duke@1: * duke@1: *

First, there is no accommodation for a finally clause that cannot duke@1: * complete normally. For liveness analysis, an intervening finally duke@1: * clause can cause a break, continue, or return not to reach its duke@1: * target. For exception analysis, an intervening finally clause can duke@1: * cause any exception to be "caught". For DA/DU analysis, the finally duke@1: * clause can prevent a transfer of control from propagating DA/DU duke@1: * state to the target. In addition, code in the finally clause can duke@1: * affect the DA/DU status of variables. duke@1: * duke@1: *

For try statements, we introduce the idea of a variable being duke@1: * definitely unassigned "everywhere" in a block. A variable V is duke@1: * "unassigned everywhere" in a block iff it is unassigned at the duke@1: * beginning of the block and there is no reachable assignment to V duke@1: * in the block. An assignment V=e is reachable iff V is not DA duke@1: * after e. Then we can say that V is DU at the beginning of the duke@1: * catch block iff V is DU everywhere in the try block. Similarly, V duke@1: * is DU at the beginning of the finally block iff V is DU everywhere duke@1: * in the try block and in every catch block. Specifically, the duke@1: * following bullet is added to 16.2.2 duke@1: *

duke@1:  *      V is unassigned everywhere in a block if it is
duke@1:  *      unassigned before the block and there is no reachable
duke@1:  *      assignment to V within the block.
duke@1:  *  
duke@1: *

In 16.2.15, the third bullet (and all of its sub-bullets) for all duke@1: * try blocks is changed to duke@1: *

duke@1:  *      V is definitely unassigned before a catch block iff V is
duke@1:  *      definitely unassigned everywhere in the try block.
duke@1:  *  
duke@1: *

The last bullet (and all of its sub-bullets) for try blocks that duke@1: * have a finally block is changed to duke@1: *

duke@1:  *      V is definitely unassigned before the finally block iff
duke@1:  *      V is definitely unassigned everywhere in the try block
duke@1:  *      and everywhere in each catch block of the try statement.
duke@1:  *  
duke@1: *

In addition, duke@1: *

duke@1:  *      V is definitely assigned at the end of a constructor iff
duke@1:  *      V is definitely assigned after the block that is the body
duke@1:  *      of the constructor and V is definitely assigned at every
duke@1:  *      return that can return from the constructor.
duke@1:  *  
duke@1: *

In addition, each continue statement with the loop as its target duke@1: * is treated as a jump to the end of the loop body, and "intervening" duke@1: * finally clauses are treated as follows: V is DA "due to the duke@1: * continue" iff V is DA before the continue statement or V is DA at duke@1: * the end of any intervening finally block. V is DU "due to the duke@1: * continue" iff any intervening finally cannot complete normally or V duke@1: * is DU at the end of every intervening finally block. This "due to duke@1: * the continue" concept is then used in the spec for the loops. duke@1: * duke@1: *

Similarly, break statements must consider intervening finally duke@1: * blocks. For liveness analysis, a break statement for which any duke@1: * intervening finally cannot complete normally is not considered to duke@1: * cause the target statement to be able to complete normally. Then duke@1: * we say V is DA "due to the break" iff V is DA before the break or duke@1: * V is DA at the end of any intervening finally block. V is DU "due duke@1: * to the break" iff any intervening finally cannot complete normally duke@1: * or V is DU at the break and at the end of every intervening duke@1: * finally block. (I suspect this latter condition can be duke@1: * simplified.) This "due to the break" is then used in the spec for duke@1: * all statements that can be "broken". duke@1: * duke@1: *

The return statement is treated similarly. V is DA "due to a duke@1: * return statement" iff V is DA before the return statement or V is duke@1: * DA at the end of any intervening finally block. Note that we duke@1: * don't have to worry about the return expression because this duke@1: * concept is only used for construcrors. duke@1: * jjh@972: *

There is no spec in the JLS for when a variable is definitely duke@1: * assigned at the end of a constructor, which is needed for final duke@1: * fields (8.3.1.2). We implement the rule that V is DA at the end duke@1: * of the constructor iff it is DA and the end of the body of the duke@1: * constructor and V is DA "due to" every return of the constructor. duke@1: * duke@1: *

Intervening finally blocks similarly affect exception analysis. An duke@1: * intervening finally that cannot complete normally allows us to ignore duke@1: * an otherwise uncaught exception. duke@1: * duke@1: *

To implement the semantics of intervening finally clauses, all duke@1: * nonlocal transfers (break, continue, return, throw, method call that duke@1: * can throw a checked exception, and a constructor invocation that can duke@1: * thrown a checked exception) are recorded in a queue, and removed duke@1: * from the queue when we complete processing the target of the duke@1: * nonlocal transfer. This allows us to modify the queue in accordance duke@1: * with the above rules when we encounter a finally clause. The only duke@1: * exception to this [no pun intended] is that checked exceptions that duke@1: * are known to be caught or declared to be caught in the enclosing duke@1: * method are not recorded in the queue, but instead are recorded in a jjg@1358: * global variable "{@code Set thrown}" that records the type of all duke@1: * exceptions that can be thrown. duke@1: * duke@1: *

Other minor issues the treatment of members of other classes duke@1: * (always considered DA except that within an anonymous class duke@1: * constructor, where DA status from the enclosing scope is duke@1: * preserved), treatment of the case expression (V is DA before the duke@1: * case expression iff V is DA after the switch expression), duke@1: * treatment of variables declared in a switch block (the implied duke@1: * DA/DU status after the switch expression is DU and not DA for duke@1: * variables defined in a switch block), the treatment of boolean ?: duke@1: * expressions (The JLS rules only handle b and c non-boolean; the duke@1: * new rule is that if b and c are boolean valued, then V is duke@1: * (un)assigned after a?b:c when true/false iff V is (un)assigned duke@1: * after b when true/false and V is (un)assigned after c when duke@1: * true/false). duke@1: * duke@1: *

There is the remaining question of what syntactic forms constitute a duke@1: * reference to a variable. It is conventional to allow this.x on the duke@1: * left-hand-side to initialize a final instance field named x, yet duke@1: * this.x isn't considered a "use" when appearing on a right-hand-side duke@1: * in most implementations. Should parentheses affect what is duke@1: * considered a variable reference? The simplest rule would be to duke@1: * allow unqualified forms only, parentheses optional, and phase out duke@1: * support for assigning to a final field via this.x. duke@1: * jjg@581: *

This is NOT part of any supported API. jjg@581: * If you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ mcimadamore@1237: public class Flow { duke@1: protected static final Context.Key flowKey = duke@1: new Context.Key(); duke@1: jjg@113: private final Names names; duke@1: private final Log log; duke@1: private final Symtab syms; duke@1: private final Types types; duke@1: private final Check chk; duke@1: private TreeMaker make; mcimadamore@617: private final Resolve rs; mcimadamore@1297: private final JCDiagnostic.Factory diags; mcimadamore@617: private Env attrEnv; duke@1: private Lint lint; mcimadamore@935: private final boolean allowImprovedRethrowAnalysis; mcimadamore@935: private final boolean allowImprovedCatchAnalysis; mcimadamore@1297: private final boolean allowEffectivelyFinalInInnerClasses; duke@1: duke@1: public static Flow instance(Context context) { duke@1: Flow instance = context.get(flowKey); duke@1: if (instance == null) duke@1: instance = new Flow(context); duke@1: return instance; duke@1: } duke@1: mcimadamore@1237: public void analyzeTree(Env env, TreeMaker make) { mcimadamore@1297: new AliveAnalyzer().analyzeTree(env, make); mcimadamore@1297: new AssignAnalyzer().analyzeTree(env, make); mcimadamore@1237: new FlowAnalyzer().analyzeTree(env, make); mcimadamore@1297: new CaptureAnalyzer().analyzeTree(env, make); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1348: public void analyzeLambda(Env env, JCLambda that, TreeMaker make, boolean speculative) { jjg@1406: Log.DiagnosticHandler diagHandler = null; mcimadamore@1348: //we need to disable diagnostics temporarily; the problem is that if mcimadamore@1348: //a lambda expression contains e.g. an unreachable statement, an error mcimadamore@1348: //message will be reported and will cause compilation to skip the flow analyis mcimadamore@1348: //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis mcimadamore@1348: //related errors, which will allow for more errors to be detected mcimadamore@1348: if (!speculative) { jjg@1406: diagHandler = new Log.DiscardDiagnosticHandler(log); mcimadamore@1348: } mcimadamore@1348: try { mcimadamore@1348: new AliveAnalyzer().analyzeTree(env, that, make); mcimadamore@1348: new FlowAnalyzer().analyzeTree(env, that, make); mcimadamore@1348: } finally { mcimadamore@1348: if (!speculative) { jjg@1406: log.popDiagnosticHandler(diagHandler); mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: mcimadamore@1297: /** mcimadamore@1297: * Definite assignment scan mode mcimadamore@1297: */ mcimadamore@1297: enum FlowKind { mcimadamore@1297: /** mcimadamore@1297: * This is the normal DA/DU analysis mode mcimadamore@1297: */ mcimadamore@1297: NORMAL("var.might.already.be.assigned", false), mcimadamore@1297: /** mcimadamore@1297: * This is the speculative DA/DU analysis mode used to speculatively mcimadamore@1297: * derive assertions within loop bodies mcimadamore@1297: */ mcimadamore@1297: SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true); mcimadamore@1297: vromero@1442: final String errKey; vromero@1442: final boolean isFinal; mcimadamore@1297: mcimadamore@1297: FlowKind(String errKey, boolean isFinal) { mcimadamore@1297: this.errKey = errKey; mcimadamore@1297: this.isFinal = isFinal; mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: boolean isFinal() { mcimadamore@1297: return isFinal; mcimadamore@1297: } mcimadamore@1237: } mcimadamore@1237: duke@1: protected Flow(Context context) { duke@1: context.put(flowKey, this); jjg@113: names = Names.instance(context); duke@1: log = Log.instance(context); duke@1: syms = Symtab.instance(context); duke@1: types = Types.instance(context); duke@1: chk = Check.instance(context); duke@1: lint = Lint.instance(context); mcimadamore@617: rs = Resolve.instance(context); mcimadamore@1297: diags = JCDiagnostic.Factory.instance(context); mcimadamore@550: Source source = Source.instance(context); mcimadamore@935: allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis(); mcimadamore@935: allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis(); mcimadamore@1415: allowEffectivelyFinalInInnerClasses = source.allowEffectivelyFinalInInnerClasses(); duke@1: } duke@1: mcimadamore@1237: /** mcimadamore@1237: * Base visitor class for all visitors implementing dataflow analysis logic. mcimadamore@1237: * This class define the shared logic for handling jumps (break/continue statements). duke@1: */ mcimadamore@1237: static abstract class BaseAnalyzer

extends TreeScanner { duke@1: mcimadamore@1237: enum JumpKind { mcimadamore@1237: BREAK(JCTree.Tag.BREAK) { mcimadamore@1237: @Override mcimadamore@1237: JCTree getTarget(JCTree tree) { mcimadamore@1237: return ((JCBreak)tree).target; mcimadamore@1237: } mcimadamore@1237: }, mcimadamore@1237: CONTINUE(JCTree.Tag.CONTINUE) { mcimadamore@1237: @Override mcimadamore@1237: JCTree getTarget(JCTree tree) { mcimadamore@1237: return ((JCContinue)tree).target; mcimadamore@1237: } mcimadamore@1237: }; duke@1: vromero@1442: final JCTree.Tag treeTag; duke@1: mcimadamore@1237: private JumpKind(Tag treeTag) { mcimadamore@1237: this.treeTag = treeTag; mcimadamore@1237: } mcimadamore@550: mcimadamore@1237: abstract JCTree getTarget(JCTree tree); mcimadamore@1237: } duke@1: mcimadamore@1237: /** The currently pending exits that go from current inner blocks mcimadamore@1237: * to an enclosing block, in source order. mcimadamore@1237: */ mcimadamore@1237: ListBuffer

pendingExits; duke@1: mcimadamore@1237: /** A pending exit. These are the statements return, break, and mcimadamore@1237: * continue. In addition, exception-throwing expressions or mcimadamore@1237: * statements are put here when not known to be caught. This mcimadamore@1237: * will typically result in an error unless it is within a mcimadamore@1237: * try-finally whose finally block cannot complete normally. mcimadamore@1237: */ mcimadamore@1297: static class PendingExit { mcimadamore@1237: JCTree tree; duke@1: mcimadamore@1237: PendingExit(JCTree tree) { mcimadamore@1237: this.tree = tree; mcimadamore@1237: } duke@1: mcimadamore@1297: void resolveJump() { mcimadamore@1297: //do nothing mcimadamore@1297: } mcimadamore@1237: } duke@1: mcimadamore@1237: abstract void markDead(); duke@1: mcimadamore@1237: /** Record an outward transfer of control. */ mcimadamore@1237: void recordExit(JCTree tree, P pe) { mcimadamore@1237: pendingExits.append(pe); mcimadamore@1237: markDead(); mcimadamore@1237: } duke@1: mcimadamore@1237: /** Resolve all jumps of this statement. */ mcimadamore@1237: private boolean resolveJump(JCTree tree, mcimadamore@1237: ListBuffer

oldPendingExits, mcimadamore@1237: JumpKind jk) { mcimadamore@1237: boolean resolved = false; mcimadamore@1237: List

exits = pendingExits.toList(); mcimadamore@1237: pendingExits = oldPendingExits; mcimadamore@1237: for (; exits.nonEmpty(); exits = exits.tail) { mcimadamore@1237: P exit = exits.head; mcimadamore@1237: if (exit.tree.hasTag(jk.treeTag) && mcimadamore@1237: jk.getTarget(exit.tree) == tree) { mcimadamore@1237: exit.resolveJump(); mcimadamore@1237: resolved = true; mcimadamore@1237: } else { mcimadamore@1237: pendingExits.append(exit); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: return resolved; mcimadamore@1237: } duke@1: mcimadamore@1237: /** Resolve all breaks of this statement. */ mcimadamore@1237: boolean resolveContinues(JCTree tree) { mcimadamore@1237: return resolveJump(tree, new ListBuffer

(), JumpKind.CONTINUE); mcimadamore@1237: } darcy@609: mcimadamore@1237: /** Resolve all continues of this statement. */ mcimadamore@1237: boolean resolveBreaks(JCTree tree, ListBuffer

oldPendingExits) { mcimadamore@1237: return resolveJump(tree, oldPendingExits, JumpKind.BREAK); duke@1: } duke@1: } duke@1: mcimadamore@1237: /** mcimadamore@1297: * This pass implements the first step of the dataflow analysis, namely mcimadamore@1297: * the liveness analysis check. This checks that every statement is reachable. mcimadamore@1297: * The output of this analysis pass are used by other analyzers. This analyzer mcimadamore@1297: * sets the 'finallyCanCompleteNormally' field in the JCTry class. mcimadamore@1297: */ mcimadamore@1297: class AliveAnalyzer extends BaseAnalyzer { mcimadamore@1297: mcimadamore@1297: /** A flag that indicates whether the last statement could mcimadamore@1297: * complete normally. mcimadamore@1297: */ mcimadamore@1297: private boolean alive; mcimadamore@1297: mcimadamore@1297: @Override mcimadamore@1297: void markDead() { mcimadamore@1297: alive = false; mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /************************************************************************* mcimadamore@1297: * Visitor methods for statements and definitions mcimadamore@1297: *************************************************************************/ mcimadamore@1297: mcimadamore@1297: /** Analyze a definition. mcimadamore@1297: */ mcimadamore@1297: void scanDef(JCTree tree) { mcimadamore@1297: scanStat(tree); mcimadamore@1297: if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { mcimadamore@1297: log.error(tree.pos(), mcimadamore@1297: "initializer.must.be.able.to.complete.normally"); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /** Analyze a statement. Check that statement is reachable. mcimadamore@1297: */ mcimadamore@1297: void scanStat(JCTree tree) { mcimadamore@1297: if (!alive && tree != null) { mcimadamore@1297: log.error(tree.pos(), "unreachable.stmt"); mcimadamore@1297: if (!tree.hasTag(SKIP)) alive = true; mcimadamore@1297: } mcimadamore@1297: scan(tree); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /** Analyze list of statements. mcimadamore@1297: */ mcimadamore@1297: void scanStats(List trees) { mcimadamore@1297: if (trees != null) mcimadamore@1297: for (List l = trees; l.nonEmpty(); l = l.tail) mcimadamore@1297: scanStat(l.head); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /* ------------ Visitor methods for various sorts of trees -------------*/ mcimadamore@1297: mcimadamore@1297: public void visitClassDef(JCClassDecl tree) { mcimadamore@1297: if (tree.sym == null) return; mcimadamore@1297: boolean alivePrev = alive; mcimadamore@1297: ListBuffer pendingExitsPrev = pendingExits; mcimadamore@1297: Lint lintPrev = lint; mcimadamore@1297: mcimadamore@1297: pendingExits = new ListBuffer(); jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1297: mcimadamore@1297: try { mcimadamore@1297: // process all the static initializers mcimadamore@1297: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1297: if (!l.head.hasTag(METHODDEF) && mcimadamore@1297: (TreeInfo.flags(l.head) & STATIC) != 0) { mcimadamore@1297: scanDef(l.head); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: // process all the instance initializers mcimadamore@1297: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1297: if (!l.head.hasTag(METHODDEF) && mcimadamore@1297: (TreeInfo.flags(l.head) & STATIC) == 0) { mcimadamore@1297: scanDef(l.head); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: // process all the methods mcimadamore@1297: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1297: if (l.head.hasTag(METHODDEF)) { mcimadamore@1297: scan(l.head); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } finally { mcimadamore@1297: pendingExits = pendingExitsPrev; mcimadamore@1297: alive = alivePrev; mcimadamore@1297: lint = lintPrev; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitMethodDef(JCMethodDecl tree) { mcimadamore@1297: if (tree.body == null) return; mcimadamore@1297: Lint lintPrev = lint; mcimadamore@1297: jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1297: mcimadamore@1297: Assert.check(pendingExits.isEmpty()); mcimadamore@1297: mcimadamore@1297: try { mcimadamore@1297: alive = true; mcimadamore@1297: scanStat(tree.body); mcimadamore@1297: jjg@1374: if (alive && !tree.sym.type.getReturnType().hasTag(VOID)) mcimadamore@1297: log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt"); mcimadamore@1297: mcimadamore@1297: List exits = pendingExits.toList(); mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: while (exits.nonEmpty()) { mcimadamore@1297: PendingExit exit = exits.head; mcimadamore@1297: exits = exits.tail; mcimadamore@1297: Assert.check(exit.tree.hasTag(RETURN)); mcimadamore@1297: } mcimadamore@1297: } finally { mcimadamore@1297: lint = lintPrev; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitVarDef(JCVariableDecl tree) { mcimadamore@1297: if (tree.init != null) { mcimadamore@1297: Lint lintPrev = lint; jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1297: try{ mcimadamore@1297: scan(tree.init); mcimadamore@1297: } finally { mcimadamore@1297: lint = lintPrev; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitBlock(JCBlock tree) { mcimadamore@1297: scanStats(tree.stats); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitDoLoop(JCDoWhileLoop tree) { mcimadamore@1297: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: scanStat(tree.body); mcimadamore@1297: alive |= resolveContinues(tree); mcimadamore@1297: scan(tree.cond); mcimadamore@1297: alive = alive && !tree.cond.type.isTrue(); mcimadamore@1297: alive |= resolveBreaks(tree, prevPendingExits); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitWhileLoop(JCWhileLoop tree) { mcimadamore@1297: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: scan(tree.cond); mcimadamore@1297: alive = !tree.cond.type.isFalse(); mcimadamore@1297: scanStat(tree.body); mcimadamore@1297: alive |= resolveContinues(tree); mcimadamore@1297: alive = resolveBreaks(tree, prevPendingExits) || mcimadamore@1297: !tree.cond.type.isTrue(); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitForLoop(JCForLoop tree) { mcimadamore@1297: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: scanStats(tree.init); mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: if (tree.cond != null) { mcimadamore@1297: scan(tree.cond); mcimadamore@1297: alive = !tree.cond.type.isFalse(); mcimadamore@1297: } else { mcimadamore@1297: alive = true; mcimadamore@1297: } mcimadamore@1297: scanStat(tree.body); mcimadamore@1297: alive |= resolveContinues(tree); mcimadamore@1297: scan(tree.step); mcimadamore@1297: alive = resolveBreaks(tree, prevPendingExits) || mcimadamore@1297: tree.cond != null && !tree.cond.type.isTrue(); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitForeachLoop(JCEnhancedForLoop tree) { mcimadamore@1297: visitVarDef(tree.var); mcimadamore@1297: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: scan(tree.expr); mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: scanStat(tree.body); mcimadamore@1297: alive |= resolveContinues(tree); mcimadamore@1297: resolveBreaks(tree, prevPendingExits); mcimadamore@1297: alive = true; mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitLabelled(JCLabeledStatement tree) { mcimadamore@1297: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: scanStat(tree.body); mcimadamore@1297: alive |= resolveBreaks(tree, prevPendingExits); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitSwitch(JCSwitch tree) { mcimadamore@1297: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: scan(tree.selector); mcimadamore@1297: boolean hasDefault = false; mcimadamore@1297: for (List l = tree.cases; l.nonEmpty(); l = l.tail) { mcimadamore@1297: alive = true; mcimadamore@1297: JCCase c = l.head; mcimadamore@1297: if (c.pat == null) mcimadamore@1297: hasDefault = true; mcimadamore@1297: else mcimadamore@1297: scan(c.pat); mcimadamore@1297: scanStats(c.stats); mcimadamore@1297: // Warn about fall-through if lint switch fallthrough enabled. mcimadamore@1297: if (alive && mcimadamore@1297: lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && mcimadamore@1297: c.stats.nonEmpty() && l.tail.nonEmpty()) mcimadamore@1297: log.warning(Lint.LintCategory.FALLTHROUGH, mcimadamore@1297: l.tail.head.pos(), mcimadamore@1297: "possible.fall-through.into.case"); mcimadamore@1297: } mcimadamore@1297: if (!hasDefault) { mcimadamore@1297: alive = true; mcimadamore@1297: } mcimadamore@1297: alive |= resolveBreaks(tree, prevPendingExits); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitTry(JCTry tree) { mcimadamore@1297: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: for (JCTree resource : tree.resources) { mcimadamore@1297: if (resource instanceof JCVariableDecl) { mcimadamore@1297: JCVariableDecl vdecl = (JCVariableDecl) resource; mcimadamore@1297: visitVarDef(vdecl); mcimadamore@1297: } else if (resource instanceof JCExpression) { mcimadamore@1297: scan((JCExpression) resource); mcimadamore@1297: } else { mcimadamore@1297: throw new AssertionError(tree); // parser error mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: scanStat(tree.body); mcimadamore@1297: boolean aliveEnd = alive; mcimadamore@1297: mcimadamore@1297: for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { mcimadamore@1297: alive = true; mcimadamore@1297: JCVariableDecl param = l.head.param; mcimadamore@1297: scan(param); mcimadamore@1297: scanStat(l.head.body); mcimadamore@1297: aliveEnd |= alive; mcimadamore@1297: } mcimadamore@1297: if (tree.finalizer != null) { mcimadamore@1297: ListBuffer exits = pendingExits; mcimadamore@1297: pendingExits = prevPendingExits; mcimadamore@1297: alive = true; mcimadamore@1297: scanStat(tree.finalizer); mcimadamore@1297: tree.finallyCanCompleteNormally = alive; mcimadamore@1297: if (!alive) { mcimadamore@1297: if (lint.isEnabled(Lint.LintCategory.FINALLY)) { mcimadamore@1297: log.warning(Lint.LintCategory.FINALLY, mcimadamore@1297: TreeInfo.diagEndPos(tree.finalizer), mcimadamore@1297: "finally.cannot.complete"); mcimadamore@1297: } mcimadamore@1297: } else { mcimadamore@1297: while (exits.nonEmpty()) { mcimadamore@1297: pendingExits.append(exits.next()); mcimadamore@1297: } mcimadamore@1297: alive = aliveEnd; mcimadamore@1297: } mcimadamore@1297: } else { mcimadamore@1297: alive = aliveEnd; mcimadamore@1297: ListBuffer exits = pendingExits; mcimadamore@1297: pendingExits = prevPendingExits; mcimadamore@1297: while (exits.nonEmpty()) pendingExits.append(exits.next()); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: @Override mcimadamore@1297: public void visitIf(JCIf tree) { mcimadamore@1297: scan(tree.cond); mcimadamore@1297: scanStat(tree.thenpart); mcimadamore@1297: if (tree.elsepart != null) { mcimadamore@1297: boolean aliveAfterThen = alive; mcimadamore@1297: alive = true; mcimadamore@1297: scanStat(tree.elsepart); mcimadamore@1297: alive = alive | aliveAfterThen; mcimadamore@1297: } else { mcimadamore@1297: alive = true; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitBreak(JCBreak tree) { mcimadamore@1297: recordExit(tree, new PendingExit(tree)); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitContinue(JCContinue tree) { mcimadamore@1297: recordExit(tree, new PendingExit(tree)); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitReturn(JCReturn tree) { mcimadamore@1297: scan(tree.expr); mcimadamore@1297: recordExit(tree, new PendingExit(tree)); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitThrow(JCThrow tree) { mcimadamore@1297: scan(tree.expr); mcimadamore@1297: markDead(); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitApply(JCMethodInvocation tree) { mcimadamore@1297: scan(tree.meth); mcimadamore@1297: scan(tree.args); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitNewClass(JCNewClass tree) { mcimadamore@1297: scan(tree.encl); mcimadamore@1297: scan(tree.args); mcimadamore@1297: if (tree.def != null) { mcimadamore@1297: scan(tree.def); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1348: @Override mcimadamore@1348: public void visitLambda(JCLambda tree) { mcimadamore@1348: if (tree.type != null && mcimadamore@1348: tree.type.isErroneous()) { mcimadamore@1348: return; mcimadamore@1348: } mcimadamore@1348: mcimadamore@1348: ListBuffer prevPending = pendingExits; mcimadamore@1348: boolean prevAlive = alive; mcimadamore@1348: try { mcimadamore@1348: pendingExits = ListBuffer.lb(); mcimadamore@1348: alive = true; mcimadamore@1348: scanStat(tree.body); mcimadamore@1348: tree.canCompleteNormally = alive; mcimadamore@1348: } mcimadamore@1348: finally { mcimadamore@1348: pendingExits = prevPending; mcimadamore@1348: alive = prevAlive; mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: mcimadamore@1297: public void visitTopLevel(JCCompilationUnit tree) { mcimadamore@1297: // Do nothing for TopLevel since each class is visited individually mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /************************************************************************** mcimadamore@1297: * main method mcimadamore@1297: *************************************************************************/ mcimadamore@1297: mcimadamore@1297: /** Perform definite assignment/unassignment analysis on a tree. mcimadamore@1297: */ mcimadamore@1297: public void analyzeTree(Env env, TreeMaker make) { mcimadamore@1348: analyzeTree(env, env.tree, make); mcimadamore@1348: } mcimadamore@1348: public void analyzeTree(Env env, JCTree tree, TreeMaker make) { mcimadamore@1297: try { mcimadamore@1297: attrEnv = env; mcimadamore@1297: Flow.this.make = make; mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: alive = true; mcimadamore@1297: scan(env.tree); mcimadamore@1297: } finally { mcimadamore@1297: pendingExits = null; mcimadamore@1297: Flow.this.make = null; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /** mcimadamore@1297: * This pass implements the second step of the dataflow analysis, namely mcimadamore@1297: * the exception analysis. This is to ensure that every checked exception that is mcimadamore@1297: * thrown is declared or caught. The analyzer uses some info that has been set by mcimadamore@1297: * the liveliness analyzer. duke@1: */ mcimadamore@1237: class FlowAnalyzer extends BaseAnalyzer { duke@1: mcimadamore@1237: /** A flag that indicates whether the last statement could mcimadamore@1237: * complete normally. mcimadamore@1237: */ mcimadamore@1237: HashMap> preciseRethrowTypes; mcimadamore@1237: mcimadamore@1237: /** The current class being defined. mcimadamore@1237: */ mcimadamore@1237: JCClassDecl classDef; mcimadamore@1237: mcimadamore@1237: /** The list of possibly thrown declarable exceptions. mcimadamore@1237: */ mcimadamore@1237: List thrown; mcimadamore@1237: mcimadamore@1237: /** The list of exceptions that are either caught or declared to be mcimadamore@1237: * thrown. mcimadamore@1237: */ mcimadamore@1237: List caught; mcimadamore@1237: mcimadamore@1237: class FlowPendingExit extends BaseAnalyzer.PendingExit { mcimadamore@1237: mcimadamore@1237: Type thrown; mcimadamore@1237: mcimadamore@1237: FlowPendingExit(JCTree tree, Type thrown) { mcimadamore@1237: super(tree); mcimadamore@1237: this.thrown = thrown; mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: @Override mcimadamore@1237: void markDead() { mcimadamore@1297: //do nothing mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: /*-------------------- Exceptions ----------------------*/ mcimadamore@1237: mcimadamore@1237: /** Complain that pending exceptions are not caught. mcimadamore@1237: */ mcimadamore@1237: void errorUncaught() { mcimadamore@1237: for (FlowPendingExit exit = pendingExits.next(); mcimadamore@1237: exit != null; mcimadamore@1237: exit = pendingExits.next()) { mcimadamore@1237: if (classDef != null && mcimadamore@1237: classDef.pos == exit.tree.pos) { mcimadamore@1237: log.error(exit.tree.pos(), mcimadamore@1237: "unreported.exception.default.constructor", mcimadamore@1237: exit.thrown); mcimadamore@1237: } else if (exit.tree.hasTag(VARDEF) && mcimadamore@1237: ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { mcimadamore@1237: log.error(exit.tree.pos(), mcimadamore@1237: "unreported.exception.implicit.close", mcimadamore@1237: exit.thrown, mcimadamore@1237: ((JCVariableDecl)exit.tree).sym.name); mcimadamore@1237: } else { mcimadamore@1237: log.error(exit.tree.pos(), mcimadamore@1237: "unreported.exception.need.to.catch.or.throw", mcimadamore@1237: exit.thrown); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: /** Record that exception is potentially thrown and check that it mcimadamore@1237: * is caught. mcimadamore@1237: */ mcimadamore@1237: void markThrown(JCTree tree, Type exc) { mcimadamore@1237: if (!chk.isUnchecked(tree.pos(), exc)) { mcimadamore@1237: if (!chk.isHandled(exc, caught)) mcimadamore@1237: pendingExits.append(new FlowPendingExit(tree, exc)); mcimadamore@1237: thrown = chk.incl(exc, thrown); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: /************************************************************************* mcimadamore@1237: * Visitor methods for statements and definitions mcimadamore@1237: *************************************************************************/ mcimadamore@1237: mcimadamore@1237: /* ------------ Visitor methods for various sorts of trees -------------*/ mcimadamore@1237: mcimadamore@1237: public void visitClassDef(JCClassDecl tree) { mcimadamore@1237: if (tree.sym == null) return; mcimadamore@1237: mcimadamore@1237: JCClassDecl classDefPrev = classDef; mcimadamore@1237: List thrownPrev = thrown; mcimadamore@1237: List caughtPrev = caught; mcimadamore@1237: ListBuffer pendingExitsPrev = pendingExits; mcimadamore@1237: Lint lintPrev = lint; mcimadamore@1237: mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: if (tree.name != names.empty) { mcimadamore@1237: caught = List.nil(); mcimadamore@1237: } mcimadamore@1237: classDef = tree; mcimadamore@1237: thrown = List.nil(); jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1237: mcimadamore@1237: try { mcimadamore@1237: // process all the static initializers mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (!l.head.hasTag(METHODDEF) && mcimadamore@1237: (TreeInfo.flags(l.head) & STATIC) != 0) { mcimadamore@1297: scan(l.head); mcimadamore@1237: errorUncaught(); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: // add intersection of all thrown clauses of initial constructors mcimadamore@1237: // to set of caught exceptions, unless class is anonymous. mcimadamore@1237: if (tree.name != names.empty) { mcimadamore@1237: boolean firstConstructor = true; mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (TreeInfo.isInitialConstructor(l.head)) { mcimadamore@1237: List mthrown = mcimadamore@1237: ((JCMethodDecl) l.head).sym.type.getThrownTypes(); mcimadamore@1237: if (firstConstructor) { mcimadamore@1237: caught = mthrown; mcimadamore@1237: firstConstructor = false; mcimadamore@1237: } else { mcimadamore@1237: caught = chk.intersect(mthrown, caught); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: // process all the instance initializers mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (!l.head.hasTag(METHODDEF) && mcimadamore@1237: (TreeInfo.flags(l.head) & STATIC) == 0) { mcimadamore@1297: scan(l.head); mcimadamore@1237: errorUncaught(); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: // in an anonymous class, add the set of thrown exceptions to mcimadamore@1237: // the throws clause of the synthetic constructor and propagate mcimadamore@1237: // outwards. mcimadamore@1237: // Changing the throws clause on the fly is okay here because mcimadamore@1237: // the anonymous constructor can't be invoked anywhere else, mcimadamore@1237: // and its type hasn't been cached. mcimadamore@1237: if (tree.name == names.empty) { mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (TreeInfo.isInitialConstructor(l.head)) { mcimadamore@1237: JCMethodDecl mdef = (JCMethodDecl)l.head; mcimadamore@1237: mdef.thrown = make.Types(thrown); mcimadamore@1237: mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: thrownPrev = chk.union(thrown, thrownPrev); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: // process all the methods mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (l.head.hasTag(METHODDEF)) { mcimadamore@1237: scan(l.head); mcimadamore@1237: errorUncaught(); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: thrown = thrownPrev; mcimadamore@1237: } finally { mcimadamore@1237: pendingExits = pendingExitsPrev; mcimadamore@1237: caught = caughtPrev; mcimadamore@1237: classDef = classDefPrev; mcimadamore@1237: lint = lintPrev; mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitMethodDef(JCMethodDecl tree) { mcimadamore@1237: if (tree.body == null) return; mcimadamore@1237: mcimadamore@1237: List caughtPrev = caught; mcimadamore@1237: List mthrown = tree.sym.type.getThrownTypes(); mcimadamore@1237: Lint lintPrev = lint; mcimadamore@1237: jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1237: mcimadamore@1237: Assert.check(pendingExits.isEmpty()); mcimadamore@1237: mcimadamore@1237: try { mcimadamore@1237: for (List l = tree.params; l.nonEmpty(); l = l.tail) { mcimadamore@1237: JCVariableDecl def = l.head; mcimadamore@1237: scan(def); mcimadamore@1237: } mcimadamore@1237: if (TreeInfo.isInitialConstructor(tree)) mcimadamore@1237: caught = chk.union(caught, mthrown); mcimadamore@1237: else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) mcimadamore@1237: caught = mthrown; mcimadamore@1237: // else we are in an instance initializer block; mcimadamore@1237: // leave caught unchanged. mcimadamore@1237: mcimadamore@1297: scan(tree.body); mcimadamore@1237: mcimadamore@1237: List exits = pendingExits.toList(); mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: while (exits.nonEmpty()) { mcimadamore@1237: FlowPendingExit exit = exits.head; mcimadamore@1237: exits = exits.tail; mcimadamore@1237: if (exit.thrown == null) { mcimadamore@1237: Assert.check(exit.tree.hasTag(RETURN)); mcimadamore@1237: } else { mcimadamore@1237: // uncaught throws will be reported later mcimadamore@1237: pendingExits.append(exit); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } finally { mcimadamore@1237: caught = caughtPrev; mcimadamore@1237: lint = lintPrev; mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitVarDef(JCVariableDecl tree) { mcimadamore@1237: if (tree.init != null) { mcimadamore@1237: Lint lintPrev = lint; jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1237: try{ mcimadamore@1237: scan(tree.init); mcimadamore@1237: } finally { mcimadamore@1237: lint = lintPrev; mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitBlock(JCBlock tree) { mcimadamore@1297: scan(tree.stats); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitDoLoop(JCDoWhileLoop tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1297: scan(tree.body); mcimadamore@1297: resolveContinues(tree); mcimadamore@1237: scan(tree.cond); mcimadamore@1297: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitWhileLoop(JCWhileLoop tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: scan(tree.cond); mcimadamore@1297: scan(tree.body); mcimadamore@1297: resolveContinues(tree); mcimadamore@1297: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitForLoop(JCForLoop tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: scan(tree.init); mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: if (tree.cond != null) { mcimadamore@1237: scan(tree.cond); mcimadamore@1237: } mcimadamore@1297: scan(tree.body); mcimadamore@1297: resolveContinues(tree); mcimadamore@1237: scan(tree.step); mcimadamore@1297: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitForeachLoop(JCEnhancedForLoop tree) { mcimadamore@1237: visitVarDef(tree.var); mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: scan(tree.expr); mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1297: scan(tree.body); mcimadamore@1297: resolveContinues(tree); mcimadamore@1237: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitLabelled(JCLabeledStatement tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1297: scan(tree.body); mcimadamore@1297: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitSwitch(JCSwitch tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: scan(tree.selector); mcimadamore@1237: for (List l = tree.cases; l.nonEmpty(); l = l.tail) { mcimadamore@1237: JCCase c = l.head; mcimadamore@1297: if (c.pat != null) { mcimadamore@1237: scan(c.pat); mcimadamore@1297: } mcimadamore@1297: scan(c.stats); mcimadamore@1237: } mcimadamore@1297: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitTry(JCTry tree) { mcimadamore@1237: List caughtPrev = caught; mcimadamore@1237: List thrownPrev = thrown; mcimadamore@1237: thrown = List.nil(); mcimadamore@1237: for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { mcimadamore@1237: List subClauses = TreeInfo.isMultiCatch(l.head) ? mcimadamore@1237: ((JCTypeUnion)l.head.param.vartype).alternatives : mcimadamore@1237: List.of(l.head.param.vartype); mcimadamore@1237: for (JCExpression ct : subClauses) { mcimadamore@1237: caught = chk.incl(ct.type, caught); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: for (JCTree resource : tree.resources) { mcimadamore@1237: if (resource instanceof JCVariableDecl) { mcimadamore@1237: JCVariableDecl vdecl = (JCVariableDecl) resource; mcimadamore@1237: visitVarDef(vdecl); mcimadamore@1237: } else if (resource instanceof JCExpression) { mcimadamore@1237: scan((JCExpression) resource); mcimadamore@1237: } else { mcimadamore@1237: throw new AssertionError(tree); // parser error mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: for (JCTree resource : tree.resources) { mcimadamore@1237: List closeableSupertypes = resource.type.isCompound() ? mcimadamore@1237: types.interfaces(resource.type).prepend(types.supertype(resource.type)) : mcimadamore@1237: List.of(resource.type); mcimadamore@1237: for (Type sup : closeableSupertypes) { mcimadamore@1237: if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { mcimadamore@1237: Symbol closeMethod = rs.resolveQualifiedMethod(tree, mcimadamore@1237: attrEnv, mcimadamore@1237: sup, mcimadamore@1237: names.close, mcimadamore@1237: List.nil(), mcimadamore@1237: List.nil()); mcimadamore@1237: if (closeMethod.kind == MTH) { mcimadamore@1237: for (Type t : ((MethodSymbol)closeMethod).getThrownTypes()) { mcimadamore@1237: markThrown(resource, t); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1297: scan(tree.body); mcimadamore@1237: List thrownInTry = allowImprovedCatchAnalysis ? mcimadamore@1237: chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) : mcimadamore@1237: thrown; mcimadamore@1237: thrown = thrownPrev; mcimadamore@1237: caught = caughtPrev; mcimadamore@1237: mcimadamore@1237: List caughtInTry = List.nil(); mcimadamore@1237: for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { mcimadamore@1237: JCVariableDecl param = l.head.param; mcimadamore@1237: List subClauses = TreeInfo.isMultiCatch(l.head) ? mcimadamore@1237: ((JCTypeUnion)l.head.param.vartype).alternatives : mcimadamore@1237: List.of(l.head.param.vartype); mcimadamore@1237: List ctypes = List.nil(); mcimadamore@1237: List rethrownTypes = chk.diff(thrownInTry, caughtInTry); mcimadamore@1237: for (JCExpression ct : subClauses) { mcimadamore@1237: Type exc = ct.type; mcimadamore@1237: if (exc != syms.unknownType) { mcimadamore@1237: ctypes = ctypes.append(exc); mcimadamore@1237: if (types.isSameType(exc, syms.objectType)) mcimadamore@1237: continue; mcimadamore@1237: checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry); mcimadamore@1237: caughtInTry = chk.incl(exc, caughtInTry); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: scan(param); mcimadamore@1237: preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); mcimadamore@1297: scan(l.head.body); mcimadamore@1237: preciseRethrowTypes.remove(param.sym); mcimadamore@1237: } mcimadamore@1237: if (tree.finalizer != null) { mcimadamore@1237: List savedThrown = thrown; mcimadamore@1237: thrown = List.nil(); mcimadamore@1237: ListBuffer exits = pendingExits; mcimadamore@1237: pendingExits = prevPendingExits; mcimadamore@1297: scan(tree.finalizer); mcimadamore@1297: if (!tree.finallyCanCompleteNormally) { mcimadamore@1237: // discard exits and exceptions from try and finally mcimadamore@1237: thrown = chk.union(thrown, thrownPrev); mcimadamore@1237: } else { mcimadamore@1237: thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); mcimadamore@1237: thrown = chk.union(thrown, savedThrown); mcimadamore@1237: // FIX: this doesn't preserve source order of exits in catch mcimadamore@1237: // versus finally! mcimadamore@1237: while (exits.nonEmpty()) { mcimadamore@1237: pendingExits.append(exits.next()); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } else { mcimadamore@1237: thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); mcimadamore@1237: ListBuffer exits = pendingExits; mcimadamore@1237: pendingExits = prevPendingExits; mcimadamore@1237: while (exits.nonEmpty()) pendingExits.append(exits.next()); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: @Override mcimadamore@1237: public void visitIf(JCIf tree) { mcimadamore@1237: scan(tree.cond); mcimadamore@1297: scan(tree.thenpart); mcimadamore@1237: if (tree.elsepart != null) { mcimadamore@1297: scan(tree.elsepart); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: void checkCaughtType(DiagnosticPosition pos, Type exc, List thrownInTry, List caughtInTry) { mcimadamore@1237: if (chk.subset(exc, caughtInTry)) { mcimadamore@1237: log.error(pos, "except.already.caught", exc); mcimadamore@1237: } else if (!chk.isUnchecked(pos, exc) && mcimadamore@1237: !isExceptionOrThrowable(exc) && mcimadamore@1237: !chk.intersects(exc, thrownInTry)) { mcimadamore@1237: log.error(pos, "except.never.thrown.in.try", exc); mcimadamore@1237: } else if (allowImprovedCatchAnalysis) { mcimadamore@1237: List catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); mcimadamore@1237: // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an mcimadamore@1237: // unchecked exception, the result list would not be empty, as the augmented mcimadamore@1237: // thrown set includes { RuntimeException, Error }; if 'exc' was a checked mcimadamore@1237: // exception, that would have been covered in the branch above mcimadamore@1237: if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() && mcimadamore@1237: !isExceptionOrThrowable(exc)) { mcimadamore@1237: String key = catchableThrownTypes.length() == 1 ? mcimadamore@1237: "unreachable.catch" : mcimadamore@1237: "unreachable.catch.1"; mcimadamore@1237: log.warning(pos, key, catchableThrownTypes); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: //where mcimadamore@1237: private boolean isExceptionOrThrowable(Type exc) { mcimadamore@1237: return exc.tsym == syms.throwableType.tsym || mcimadamore@1237: exc.tsym == syms.exceptionType.tsym; mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitBreak(JCBreak tree) { mcimadamore@1237: recordExit(tree, new FlowPendingExit(tree, null)); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitContinue(JCContinue tree) { mcimadamore@1237: recordExit(tree, new FlowPendingExit(tree, null)); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitReturn(JCReturn tree) { mcimadamore@1237: scan(tree.expr); mcimadamore@1237: recordExit(tree, new FlowPendingExit(tree, null)); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitThrow(JCThrow tree) { mcimadamore@1237: scan(tree.expr); mcimadamore@1237: Symbol sym = TreeInfo.symbol(tree.expr); mcimadamore@1237: if (sym != null && mcimadamore@1237: sym.kind == VAR && mcimadamore@1237: (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && mcimadamore@1237: preciseRethrowTypes.get(sym) != null && mcimadamore@1237: allowImprovedRethrowAnalysis) { mcimadamore@1237: for (Type t : preciseRethrowTypes.get(sym)) { mcimadamore@1237: markThrown(tree, t); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: else { mcimadamore@1237: markThrown(tree, tree.expr.type); mcimadamore@1237: } mcimadamore@1237: markDead(); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitApply(JCMethodInvocation tree) { mcimadamore@1237: scan(tree.meth); mcimadamore@1237: scan(tree.args); mcimadamore@1237: for (List l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) mcimadamore@1237: markThrown(tree, l.head); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitNewClass(JCNewClass tree) { mcimadamore@1237: scan(tree.encl); mcimadamore@1237: scan(tree.args); mcimadamore@1237: // scan(tree.def); mcimadamore@1237: for (List l = tree.constructorType.getThrownTypes(); mcimadamore@1237: l.nonEmpty(); mcimadamore@1237: l = l.tail) { mcimadamore@1237: markThrown(tree, l.head); mcimadamore@1237: } mcimadamore@1237: List caughtPrev = caught; mcimadamore@1237: try { mcimadamore@1237: // If the new class expression defines an anonymous class, mcimadamore@1237: // analysis of the anonymous constructor may encounter thrown mcimadamore@1237: // types which are unsubstituted type variables. mcimadamore@1237: // However, since the constructor's actual thrown types have mcimadamore@1237: // already been marked as thrown, it is safe to simply include mcimadamore@1237: // each of the constructor's formal thrown types in the set of mcimadamore@1237: // 'caught/declared to be thrown' types, for the duration of mcimadamore@1237: // the class def analysis. mcimadamore@1237: if (tree.def != null) mcimadamore@1237: for (List l = tree.constructor.type.getThrownTypes(); mcimadamore@1237: l.nonEmpty(); mcimadamore@1237: l = l.tail) { mcimadamore@1237: caught = chk.incl(l.head, caught); mcimadamore@1237: } mcimadamore@1237: scan(tree.def); mcimadamore@1237: } mcimadamore@1237: finally { mcimadamore@1237: caught = caughtPrev; mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1348: @Override mcimadamore@1348: public void visitLambda(JCLambda tree) { mcimadamore@1348: if (tree.type != null && mcimadamore@1348: tree.type.isErroneous()) { mcimadamore@1348: return; mcimadamore@1348: } mcimadamore@1348: List prevCaught = caught; mcimadamore@1348: List prevThrown = thrown; mcimadamore@1348: ListBuffer prevPending = pendingExits; mcimadamore@1348: try { mcimadamore@1348: pendingExits = ListBuffer.lb(); mcimadamore@1348: caught = List.of(syms.throwableType); //inhibit exception checking mcimadamore@1348: thrown = List.nil(); mcimadamore@1348: scan(tree.body); mcimadamore@1348: tree.inferredThrownTypes = thrown; mcimadamore@1348: } mcimadamore@1348: finally { mcimadamore@1348: pendingExits = prevPending; mcimadamore@1348: caught = prevCaught; mcimadamore@1348: thrown = prevThrown; mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: mcimadamore@1237: public void visitTopLevel(JCCompilationUnit tree) { mcimadamore@1237: // Do nothing for TopLevel since each class is visited individually mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: /************************************************************************** mcimadamore@1237: * main method mcimadamore@1237: *************************************************************************/ mcimadamore@1237: mcimadamore@1237: /** Perform definite assignment/unassignment analysis on a tree. mcimadamore@1237: */ mcimadamore@1237: public void analyzeTree(Env env, TreeMaker make) { mcimadamore@1297: analyzeTree(env, env.tree, make); mcimadamore@1297: } mcimadamore@1297: public void analyzeTree(Env env, JCTree tree, TreeMaker make) { mcimadamore@1237: try { mcimadamore@1237: attrEnv = env; mcimadamore@1237: Flow.this.make = make; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: preciseRethrowTypes = new HashMap>(); mcimadamore@1237: this.thrown = this.caught = null; mcimadamore@1237: this.classDef = null; mcimadamore@1237: scan(tree); mcimadamore@1237: } finally { mcimadamore@1237: pendingExits = null; mcimadamore@1237: Flow.this.make = null; mcimadamore@1237: this.thrown = this.caught = null; mcimadamore@1237: this.classDef = null; mcimadamore@878: } duke@1: } duke@1: } duke@1: mcimadamore@1237: /** mcimadamore@1237: * This pass implements (i) definite assignment analysis, which ensures that mcimadamore@1237: * each variable is assigned when used and (ii) definite unassignment analysis, mcimadamore@1237: * which ensures that no final variable is assigned more than once. This visitor mcimadamore@1297: * depends on the results of the liveliness analyzer. This pass is also used to mark mcimadamore@1297: * effectively-final local variables/parameters. duke@1: */ mcimadamore@1237: class AssignAnalyzer extends BaseAnalyzer { mcimadamore@1237: mcimadamore@1237: /** The set of definitely assigned variables. mcimadamore@1237: */ mcimadamore@1237: Bits inits; mcimadamore@1237: mcimadamore@1237: /** The set of definitely unassigned variables. mcimadamore@1237: */ mcimadamore@1237: Bits uninits; mcimadamore@1237: mcimadamore@1237: /** The set of variables that are definitely unassigned everywhere mcimadamore@1237: * in current try block. This variable is maintained lazily; it is mcimadamore@1237: * updated only when something gets removed from uninits, mcimadamore@1237: * typically by being assigned in reachable code. To obtain the mcimadamore@1237: * correct set of variables which are definitely unassigned mcimadamore@1237: * anywhere in current try block, intersect uninitsTry and mcimadamore@1237: * uninits. mcimadamore@1237: */ mcimadamore@1237: Bits uninitsTry; mcimadamore@1237: mcimadamore@1237: /** When analyzing a condition, inits and uninits are null. mcimadamore@1237: * Instead we have: mcimadamore@1237: */ mcimadamore@1237: Bits initsWhenTrue; mcimadamore@1237: Bits initsWhenFalse; mcimadamore@1237: Bits uninitsWhenTrue; mcimadamore@1237: Bits uninitsWhenFalse; mcimadamore@1237: mcimadamore@1237: /** A mapping from addresses to variable symbols. mcimadamore@1237: */ mcimadamore@1237: VarSymbol[] vars; mcimadamore@1237: mcimadamore@1237: /** The current class being defined. mcimadamore@1237: */ mcimadamore@1237: JCClassDecl classDef; mcimadamore@1237: mcimadamore@1237: /** The first variable sequence number in this class definition. mcimadamore@1237: */ mcimadamore@1237: int firstadr; mcimadamore@1237: mcimadamore@1237: /** The next available variable sequence number. mcimadamore@1237: */ mcimadamore@1237: int nextadr; mcimadamore@1237: mcimadamore@1348: /** The first variable sequence number in a block that can return. mcimadamore@1348: */ mcimadamore@1348: int returnadr; mcimadamore@1348: mcimadamore@1237: /** The list of unreferenced automatic resources. mcimadamore@1237: */ mcimadamore@1237: Scope unrefdResources; mcimadamore@1237: mcimadamore@1237: /** Set when processing a loop body the second time for DU analysis. */ mcimadamore@1297: FlowKind flowKind = FlowKind.NORMAL; mcimadamore@1297: mcimadamore@1297: /** The starting position of the analysed tree */ mcimadamore@1297: int startPos; mcimadamore@1237: mcimadamore@1237: class AssignPendingExit extends BaseAnalyzer.PendingExit { mcimadamore@1237: mcimadamore@1237: Bits exit_inits; mcimadamore@1237: Bits exit_uninits; mcimadamore@1237: mcimadamore@1237: AssignPendingExit(JCTree tree, Bits inits, Bits uninits) { mcimadamore@1237: super(tree); mcimadamore@1237: this.exit_inits = inits.dup(); mcimadamore@1237: this.exit_uninits = uninits.dup(); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: void resolveJump() { mcimadamore@1237: inits.andSet(exit_inits); mcimadamore@1237: uninits.andSet(exit_uninits); mcimadamore@1237: } duke@1: } duke@1: mcimadamore@1237: @Override mcimadamore@1237: void markDead() { mcimadamore@1348: inits.inclRange(returnadr, nextadr); mcimadamore@1348: uninits.inclRange(returnadr, nextadr); mcimadamore@1237: } duke@1: mcimadamore@1237: /*-------------- Processing variables ----------------------*/ duke@1: mcimadamore@1237: /** Do we need to track init/uninit state of this symbol? mcimadamore@1237: * I.e. is symbol either a local or a blank final variable? mcimadamore@1237: */ mcimadamore@1237: boolean trackable(VarSymbol sym) { mcimadamore@1237: return mcimadamore@1297: sym.pos >= startPos && mcimadamore@1297: ((sym.owner.kind == MTH || mcimadamore@1237: ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && mcimadamore@1297: classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)))); duke@1: } duke@1: mcimadamore@1237: /** Initialize new trackable variable by setting its address field mcimadamore@1237: * to the next available sequence number and entering it under that mcimadamore@1237: * index into the vars array. mcimadamore@1237: */ mcimadamore@1237: void newVar(VarSymbol sym) { jjg@1339: vars = ArrayUtils.ensureCapacity(vars, nextadr); mcimadamore@1297: if ((sym.flags() & FINAL) == 0) { mcimadamore@1297: sym.flags_field |= EFFECTIVELY_FINAL; mcimadamore@1297: } mcimadamore@1237: sym.adr = nextadr; mcimadamore@1237: vars[nextadr] = sym; mcimadamore@1237: inits.excl(nextadr); mcimadamore@1237: uninits.incl(nextadr); mcimadamore@1237: nextadr++; mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: /** Record an initialization of a trackable variable. mcimadamore@1237: */ mcimadamore@1237: void letInit(DiagnosticPosition pos, VarSymbol sym) { mcimadamore@1237: if (sym.adr >= firstadr && trackable(sym)) { mcimadamore@1297: if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { mcimadamore@1297: if (!uninits.isMember(sym.adr)) { mcimadamore@1297: //assignment targeting an effectively final variable mcimadamore@1297: //makes the variable lose its status of effectively final mcimadamore@1297: //if the variable is _not_ definitively unassigned mcimadamore@1297: sym.flags_field &= ~EFFECTIVELY_FINAL; mcimadamore@1297: } else { mcimadamore@1297: uninit(sym); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: else if ((sym.flags() & FINAL) != 0) { mcimadamore@1237: if ((sym.flags() & PARAMETER) != 0) { mcimadamore@1237: if ((sym.flags() & UNION) != 0) { //multi-catch parameter mcimadamore@1237: log.error(pos, "multicatch.parameter.may.not.be.assigned", mcimadamore@1237: sym); mcimadamore@1237: } mcimadamore@1237: else { mcimadamore@1237: log.error(pos, "final.parameter.may.not.be.assigned", mcimadamore@550: sym); mcimadamore@1237: } mcimadamore@1237: } else if (!uninits.isMember(sym.adr)) { mcimadamore@1297: log.error(pos, flowKind.errKey, sym); mcimadamore@1237: } else { mcimadamore@1297: uninit(sym); mcimadamore@550: } mcimadamore@1237: } mcimadamore@1237: inits.incl(sym.adr); mcimadamore@1237: } else if ((sym.flags() & FINAL) != 0) { mcimadamore@1237: log.error(pos, "var.might.already.be.assigned", sym); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1297: //where mcimadamore@1297: void uninit(VarSymbol sym) { mcimadamore@1297: if (!inits.isMember(sym.adr)) { mcimadamore@1297: // reachable assignment mcimadamore@1297: uninits.excl(sym.adr); mcimadamore@1297: uninitsTry.excl(sym.adr); mcimadamore@1297: } else { mcimadamore@1297: //log.rawWarning(pos, "unreachable assignment");//DEBUG mcimadamore@1297: uninits.excl(sym.adr); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1237: mcimadamore@1237: /** If tree is either a simple name or of the form this.name or mcimadamore@1237: * C.this.name, and tree represents a trackable variable, mcimadamore@1237: * record an initialization of the variable. mcimadamore@1237: */ mcimadamore@1237: void letInit(JCTree tree) { mcimadamore@1237: tree = TreeInfo.skipParens(tree); mcimadamore@1237: if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { mcimadamore@1237: Symbol sym = TreeInfo.symbol(tree); mcimadamore@1237: if (sym.kind == VAR) { mcimadamore@1237: letInit(tree.pos(), (VarSymbol)sym); duke@1: } duke@1: } duke@1: } duke@1: mcimadamore@1237: /** Check that trackable variable is initialized. mcimadamore@1237: */ mcimadamore@1237: void checkInit(DiagnosticPosition pos, VarSymbol sym) { mcimadamore@1237: if ((sym.adr >= firstadr || sym.owner.kind != TYP) && mcimadamore@1237: trackable(sym) && mcimadamore@1237: !inits.isMember(sym.adr)) { mcimadamore@1237: log.error(pos, "var.might.not.have.been.initialized", mcimadamore@1237: sym); mcimadamore@1237: inits.incl(sym.adr); mcimadamore@676: } duke@1: } duke@1: mcimadamore@1237: /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets mcimadamore@1237: */ mcimadamore@1237: void split(boolean setToNull) { mcimadamore@1237: initsWhenFalse = inits.dup(); mcimadamore@1237: uninitsWhenFalse = uninits.dup(); mcimadamore@1237: initsWhenTrue = inits; mcimadamore@1237: uninitsWhenTrue = uninits; mcimadamore@1237: if (setToNull) mcimadamore@1237: inits = uninits = null; duke@1: } duke@1: mcimadamore@1237: /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. mcimadamore@1237: */ mcimadamore@1237: void merge() { mcimadamore@1237: inits = initsWhenFalse.andSet(initsWhenTrue); mcimadamore@1237: uninits = uninitsWhenFalse.andSet(uninitsWhenTrue); mcimadamore@1237: } duke@1: mcimadamore@1237: /* ************************************************************************ mcimadamore@1237: * Visitor methods for statements and definitions mcimadamore@1237: *************************************************************************/ duke@1: mcimadamore@1237: /** Analyze an expression. Make sure to set (un)inits rather than mcimadamore@1237: * (un)initsWhenTrue(WhenFalse) on exit. mcimadamore@1237: */ mcimadamore@1237: void scanExpr(JCTree tree) { mcimadamore@1237: if (tree != null) { mcimadamore@1237: scan(tree); mcimadamore@1237: if (inits == null) merge(); duke@1: } duke@1: } duke@1: mcimadamore@1237: /** Analyze a list of expressions. mcimadamore@1237: */ mcimadamore@1237: void scanExprs(List trees) { mcimadamore@1237: if (trees != null) mcimadamore@1237: for (List l = trees; l.nonEmpty(); l = l.tail) mcimadamore@1237: scanExpr(l.head); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) mcimadamore@1237: * rather than (un)inits on exit. mcimadamore@1237: */ mcimadamore@1237: void scanCond(JCTree tree) { mcimadamore@1237: if (tree.type.isFalse()) { mcimadamore@1237: if (inits == null) merge(); mcimadamore@1237: initsWhenTrue = inits.dup(); mcimadamore@1237: initsWhenTrue.inclRange(firstadr, nextadr); mcimadamore@1237: uninitsWhenTrue = uninits.dup(); mcimadamore@1237: uninitsWhenTrue.inclRange(firstadr, nextadr); mcimadamore@1237: initsWhenFalse = inits; mcimadamore@1237: uninitsWhenFalse = uninits; mcimadamore@1237: } else if (tree.type.isTrue()) { mcimadamore@1237: if (inits == null) merge(); mcimadamore@1237: initsWhenFalse = inits.dup(); mcimadamore@1237: initsWhenFalse.inclRange(firstadr, nextadr); mcimadamore@1237: uninitsWhenFalse = uninits.dup(); mcimadamore@1237: uninitsWhenFalse.inclRange(firstadr, nextadr); mcimadamore@1237: initsWhenTrue = inits; mcimadamore@1237: uninitsWhenTrue = uninits; duke@1: } else { mcimadamore@1237: scan(tree); mcimadamore@1237: if (inits != null) mcimadamore@1237: split(tree.type != syms.unknownType); duke@1: } mcimadamore@1237: if (tree.type != syms.unknownType) mcimadamore@1237: inits = uninits = null; duke@1: } duke@1: mcimadamore@1237: /* ------------ Visitor methods for various sorts of trees -------------*/ duke@1: mcimadamore@1237: public void visitClassDef(JCClassDecl tree) { mcimadamore@1237: if (tree.sym == null) return; duke@1: mcimadamore@1237: JCClassDecl classDefPrev = classDef; mcimadamore@1237: int firstadrPrev = firstadr; mcimadamore@1237: int nextadrPrev = nextadr; mcimadamore@1237: ListBuffer pendingExitsPrev = pendingExits; mcimadamore@1237: Lint lintPrev = lint; duke@1: mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: if (tree.name != names.empty) { mcimadamore@1237: firstadr = nextadr; mcimadamore@1237: } mcimadamore@1237: classDef = tree; jfranck@1313: lint = lint.augment(tree.sym.annotations); duke@1: mcimadamore@1237: try { mcimadamore@1237: // define all the static fields duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (l.head.hasTag(VARDEF)) { mcimadamore@1237: JCVariableDecl def = (JCVariableDecl)l.head; mcimadamore@1237: if ((def.mods.flags & STATIC) != 0) { mcimadamore@1237: VarSymbol sym = def.sym; mcimadamore@1237: if (trackable(sym)) mcimadamore@1237: newVar(sym); duke@1: } duke@1: } duke@1: } duke@1: mcimadamore@1237: // process all the static initializers mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (!l.head.hasTag(METHODDEF) && mcimadamore@1237: (TreeInfo.flags(l.head) & STATIC) != 0) { mcimadamore@1237: scan(l.head); duke@1: } duke@1: } duke@1: mcimadamore@1237: // define all the instance fields duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (l.head.hasTag(VARDEF)) { mcimadamore@1237: JCVariableDecl def = (JCVariableDecl)l.head; mcimadamore@1237: if ((def.mods.flags & STATIC) == 0) { mcimadamore@1237: VarSymbol sym = def.sym; mcimadamore@1237: if (trackable(sym)) mcimadamore@1237: newVar(sym); mcimadamore@1237: } duke@1: } duke@1: } mcimadamore@1237: mcimadamore@1237: // process all the instance initializers mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (!l.head.hasTag(METHODDEF) && mcimadamore@1237: (TreeInfo.flags(l.head) & STATIC) == 0) { mcimadamore@1237: scan(l.head); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: // process all the methods mcimadamore@1237: for (List l = tree.defs; l.nonEmpty(); l = l.tail) { mcimadamore@1237: if (l.head.hasTag(METHODDEF)) { mcimadamore@1237: scan(l.head); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } finally { mcimadamore@1237: pendingExits = pendingExitsPrev; mcimadamore@1237: nextadr = nextadrPrev; mcimadamore@1237: firstadr = firstadrPrev; mcimadamore@1237: classDef = classDefPrev; mcimadamore@1237: lint = lintPrev; duke@1: } mcimadamore@1237: } duke@1: mcimadamore@1237: public void visitMethodDef(JCMethodDecl tree) { mcimadamore@1237: if (tree.body == null) return; mcimadamore@1237: mcimadamore@1237: Bits initsPrev = inits.dup(); mcimadamore@1237: Bits uninitsPrev = uninits.dup(); mcimadamore@1237: int nextadrPrev = nextadr; mcimadamore@1237: int firstadrPrev = firstadr; mcimadamore@1348: int returnadrPrev = returnadr; mcimadamore@1237: Lint lintPrev = lint; mcimadamore@1237: jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1237: mcimadamore@1237: Assert.check(pendingExits.isEmpty()); mcimadamore@1237: mcimadamore@1237: try { mcimadamore@1237: boolean isInitialConstructor = mcimadamore@1237: TreeInfo.isInitialConstructor(tree); mcimadamore@1237: mcimadamore@1237: if (!isInitialConstructor) mcimadamore@1237: firstadr = nextadr; mcimadamore@1237: for (List l = tree.params; l.nonEmpty(); l = l.tail) { mcimadamore@1237: JCVariableDecl def = l.head; mcimadamore@1237: scan(def); mcimadamore@1237: inits.incl(def.sym.adr); mcimadamore@1237: uninits.excl(def.sym.adr); duke@1: } mcimadamore@1237: // else we are in an instance initializer block; mcimadamore@1237: // leave caught unchanged. mcimadamore@1237: scan(tree.body); duke@1: mcimadamore@1237: if (isInitialConstructor) { mcimadamore@1237: for (int i = firstadr; i < nextadr; i++) mcimadamore@1237: if (vars[i].owner == classDef.sym) mcimadamore@1237: checkInit(TreeInfo.diagEndPos(tree.body), vars[i]); mcimadamore@1237: } mcimadamore@1237: List exits = pendingExits.toList(); mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: while (exits.nonEmpty()) { mcimadamore@1237: AssignPendingExit exit = exits.head; mcimadamore@1237: exits = exits.tail; mcimadamore@1237: Assert.check(exit.tree.hasTag(RETURN), exit.tree); duke@1: if (isInitialConstructor) { mcimadamore@1237: inits = exit.exit_inits; duke@1: for (int i = firstadr; i < nextadr; i++) duke@1: checkInit(exit.tree.pos(), vars[i]); duke@1: } duke@1: } duke@1: } finally { mcimadamore@1237: inits = initsPrev; mcimadamore@1237: uninits = uninitsPrev; mcimadamore@1237: nextadr = nextadrPrev; mcimadamore@1237: firstadr = firstadrPrev; mcimadamore@1348: returnadr = returnadrPrev; duke@1: lint = lintPrev; duke@1: } duke@1: } duke@1: mcimadamore@1237: public void visitVarDef(JCVariableDecl tree) { mcimadamore@1237: boolean track = trackable(tree.sym); mcimadamore@1237: if (track && tree.sym.owner.kind == MTH) newVar(tree.sym); mcimadamore@1237: if (tree.init != null) { mcimadamore@1237: Lint lintPrev = lint; jfranck@1313: lint = lint.augment(tree.sym.annotations); mcimadamore@1237: try{ mcimadamore@1237: scanExpr(tree.init); mcimadamore@1237: if (track) letInit(tree.pos(), tree.sym); mcimadamore@1237: } finally { mcimadamore@1237: lint = lintPrev; mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } duke@1: mcimadamore@1237: public void visitBlock(JCBlock tree) { mcimadamore@1237: int nextadrPrev = nextadr; mcimadamore@1237: scan(tree.stats); mcimadamore@1237: nextadr = nextadrPrev; mcimadamore@1237: } duke@1: mcimadamore@1237: public void visitDoLoop(JCDoWhileLoop tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: FlowKind prevFlowKind = flowKind; mcimadamore@1297: flowKind = FlowKind.NORMAL; mcimadamore@1297: Bits initsSkip = null; mcimadamore@1297: Bits uninitsSkip = null; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: int prevErrors = log.nerrors; mcimadamore@1237: do { mcimadamore@1237: Bits uninitsEntry = uninits.dup(); mcimadamore@1237: uninitsEntry.excludeFrom(nextadr); mcimadamore@1297: scan(tree.body); mcimadamore@1297: resolveContinues(tree); mcimadamore@1237: scanCond(tree.cond); mcimadamore@1297: if (!flowKind.isFinal()) { mcimadamore@1297: initsSkip = initsWhenFalse; mcimadamore@1297: uninitsSkip = uninitsWhenFalse; mcimadamore@1297: } mcimadamore@1237: if (log.nerrors != prevErrors || mcimadamore@1297: flowKind.isFinal() || mcimadamore@1237: uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) mcimadamore@1237: break; mcimadamore@1237: inits = initsWhenTrue; mcimadamore@1237: uninits = uninitsEntry.andSet(uninitsWhenTrue); mcimadamore@1297: flowKind = FlowKind.SPECULATIVE_LOOP; mcimadamore@1237: } while (true); mcimadamore@1297: flowKind = prevFlowKind; mcimadamore@1297: inits = initsSkip; mcimadamore@1297: uninits = uninitsSkip; mcimadamore@1237: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } duke@1: mcimadamore@1237: public void visitWhileLoop(JCWhileLoop tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: FlowKind prevFlowKind = flowKind; mcimadamore@1297: flowKind = FlowKind.NORMAL; mcimadamore@1297: Bits initsSkip = null; mcimadamore@1297: Bits uninitsSkip = null; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: int prevErrors = log.nerrors; mcimadamore@1297: Bits uninitsEntry = uninits.dup(); mcimadamore@1297: uninitsEntry.excludeFrom(nextadr); mcimadamore@1237: do { duke@1: scanCond(tree.cond); mcimadamore@1297: if (!flowKind.isFinal()) { mcimadamore@1297: initsSkip = initsWhenFalse; mcimadamore@1297: uninitsSkip = uninitsWhenFalse; mcimadamore@1297: } duke@1: inits = initsWhenTrue; duke@1: uninits = uninitsWhenTrue; mcimadamore@1237: scan(tree.body); mcimadamore@1237: resolveContinues(tree); mcimadamore@1237: if (log.nerrors != prevErrors || mcimadamore@1297: flowKind.isFinal() || mcimadamore@1237: uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) mcimadamore@1237: break; mcimadamore@1237: uninits = uninitsEntry.andSet(uninits); mcimadamore@1297: flowKind = FlowKind.SPECULATIVE_LOOP; mcimadamore@1237: } while (true); mcimadamore@1297: flowKind = prevFlowKind; mcimadamore@1297: //a variable is DA/DU after the while statement, if it's DA/DU assuming the mcimadamore@1297: //branch is not taken AND if it's DA/DU before any break statement mcimadamore@1297: inits = initsSkip; mcimadamore@1297: uninits = uninitsSkip; mcimadamore@1237: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitForLoop(JCForLoop tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: FlowKind prevFlowKind = flowKind; mcimadamore@1297: flowKind = FlowKind.NORMAL; mcimadamore@1237: int nextadrPrev = nextadr; mcimadamore@1237: scan(tree.init); mcimadamore@1297: Bits initsSkip = null; mcimadamore@1297: Bits uninitsSkip = null; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: int prevErrors = log.nerrors; mcimadamore@1237: do { mcimadamore@1237: Bits uninitsEntry = uninits.dup(); mcimadamore@1237: uninitsEntry.excludeFrom(nextadr); mcimadamore@1237: if (tree.cond != null) { mcimadamore@1237: scanCond(tree.cond); mcimadamore@1297: if (!flowKind.isFinal()) { mcimadamore@1297: initsSkip = initsWhenFalse; mcimadamore@1297: uninitsSkip = uninitsWhenFalse; mcimadamore@1297: } mcimadamore@1237: inits = initsWhenTrue; mcimadamore@1237: uninits = uninitsWhenTrue; mcimadamore@1297: } else if (!flowKind.isFinal()) { mcimadamore@1297: initsSkip = inits.dup(); mcimadamore@1297: initsSkip.inclRange(firstadr, nextadr); mcimadamore@1297: uninitsSkip = uninits.dup(); mcimadamore@1297: uninitsSkip.inclRange(firstadr, nextadr); mcimadamore@1237: } mcimadamore@1237: scan(tree.body); mcimadamore@1237: resolveContinues(tree); mcimadamore@1237: scan(tree.step); mcimadamore@1237: if (log.nerrors != prevErrors || mcimadamore@1297: flowKind.isFinal() || mcimadamore@1237: uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) mcimadamore@1237: break; mcimadamore@1237: uninits = uninitsEntry.andSet(uninits); mcimadamore@1297: flowKind = FlowKind.SPECULATIVE_LOOP; mcimadamore@1237: } while (true); mcimadamore@1297: flowKind = prevFlowKind; mcimadamore@1297: //a variable is DA/DU after a for loop, if it's DA/DU assuming the mcimadamore@1297: //branch is not taken AND if it's DA/DU before any break statement mcimadamore@1297: inits = initsSkip; mcimadamore@1297: uninits = uninitsSkip; mcimadamore@1237: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: nextadr = nextadrPrev; mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitForeachLoop(JCEnhancedForLoop tree) { mcimadamore@1237: visitVarDef(tree.var); mcimadamore@1237: mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1297: FlowKind prevFlowKind = flowKind; mcimadamore@1297: flowKind = FlowKind.NORMAL; mcimadamore@1237: int nextadrPrev = nextadr; mcimadamore@1237: scan(tree.expr); mcimadamore@1237: Bits initsStart = inits.dup(); mcimadamore@1237: Bits uninitsStart = uninits.dup(); mcimadamore@1237: mcimadamore@1237: letInit(tree.pos(), tree.var.sym); mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: int prevErrors = log.nerrors; mcimadamore@1237: do { mcimadamore@1237: Bits uninitsEntry = uninits.dup(); mcimadamore@1237: uninitsEntry.excludeFrom(nextadr); mcimadamore@1237: scan(tree.body); mcimadamore@1237: resolveContinues(tree); mcimadamore@1237: if (log.nerrors != prevErrors || mcimadamore@1297: flowKind.isFinal() || mcimadamore@1237: uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) mcimadamore@1237: break; mcimadamore@1237: uninits = uninitsEntry.andSet(uninits); mcimadamore@1297: flowKind = FlowKind.SPECULATIVE_LOOP; mcimadamore@1237: } while (true); mcimadamore@1297: flowKind = prevFlowKind; mcimadamore@1237: inits = initsStart; mcimadamore@1237: uninits = uninitsStart.andSet(uninits); mcimadamore@1237: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: nextadr = nextadrPrev; mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitLabelled(JCLabeledStatement tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: scan(tree.body); mcimadamore@1237: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitSwitch(JCSwitch tree) { mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: int nextadrPrev = nextadr; mcimadamore@1237: scanExpr(tree.selector); mcimadamore@1237: Bits initsSwitch = inits; mcimadamore@1237: Bits uninitsSwitch = uninits.dup(); mcimadamore@1237: boolean hasDefault = false; mcimadamore@1237: for (List l = tree.cases; l.nonEmpty(); l = l.tail) { mcimadamore@1237: inits = initsSwitch.dup(); mcimadamore@1237: uninits = uninits.andSet(uninitsSwitch); mcimadamore@1237: JCCase c = l.head; mcimadamore@1237: if (c.pat == null) mcimadamore@1237: hasDefault = true; mcimadamore@1237: else mcimadamore@1237: scanExpr(c.pat); mcimadamore@1237: scan(c.stats); mcimadamore@1237: addVars(c.stats, initsSwitch, uninitsSwitch); mcimadamore@1237: // Warn about fall-through if lint switch fallthrough enabled. mcimadamore@1237: } mcimadamore@1237: if (!hasDefault) { mcimadamore@1237: inits.andSet(initsSwitch); mcimadamore@1237: } mcimadamore@1237: resolveBreaks(tree, prevPendingExits); mcimadamore@1237: nextadr = nextadrPrev; mcimadamore@1237: } mcimadamore@1237: // where mcimadamore@1237: /** Add any variables defined in stats to inits and uninits. */ mcimadamore@1237: private void addVars(List stats, Bits inits, mcimadamore@1237: Bits uninits) { mcimadamore@1237: for (;stats.nonEmpty(); stats = stats.tail) { mcimadamore@1237: JCTree stat = stats.head; mcimadamore@1237: if (stat.hasTag(VARDEF)) { mcimadamore@1237: int adr = ((JCVariableDecl) stat).sym.adr; mcimadamore@1237: inits.excl(adr); mcimadamore@1237: uninits.incl(adr); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitTry(JCTry tree) { mcimadamore@1237: ListBuffer resourceVarDecls = ListBuffer.lb(); mcimadamore@1237: Bits uninitsTryPrev = uninitsTry; mcimadamore@1237: ListBuffer prevPendingExits = pendingExits; mcimadamore@1237: pendingExits = new ListBuffer(); mcimadamore@1237: Bits initsTry = inits.dup(); mcimadamore@1237: uninitsTry = uninits.dup(); mcimadamore@1237: for (JCTree resource : tree.resources) { mcimadamore@1237: if (resource instanceof JCVariableDecl) { mcimadamore@1237: JCVariableDecl vdecl = (JCVariableDecl) resource; mcimadamore@1237: visitVarDef(vdecl); mcimadamore@1237: unrefdResources.enter(vdecl.sym); mcimadamore@1237: resourceVarDecls.append(vdecl); mcimadamore@1237: } else if (resource instanceof JCExpression) { mcimadamore@1237: scanExpr((JCExpression) resource); mcimadamore@1237: } else { mcimadamore@1237: throw new AssertionError(tree); // parser error mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: scan(tree.body); mcimadamore@1237: uninitsTry.andSet(uninits); mcimadamore@1237: Bits initsEnd = inits; mcimadamore@1237: Bits uninitsEnd = uninits; mcimadamore@1237: int nextadrCatch = nextadr; mcimadamore@1237: mcimadamore@1237: if (!resourceVarDecls.isEmpty() && mcimadamore@1237: lint.isEnabled(Lint.LintCategory.TRY)) { mcimadamore@1237: for (JCVariableDecl resVar : resourceVarDecls) { mcimadamore@1237: if (unrefdResources.includes(resVar.sym)) { mcimadamore@1237: log.warning(Lint.LintCategory.TRY, resVar.pos(), mcimadamore@1237: "try.resource.not.referenced", resVar.sym); mcimadamore@1237: unrefdResources.remove(resVar.sym); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { mcimadamore@1237: JCVariableDecl param = l.head.param; mcimadamore@1237: inits = initsTry.dup(); mcimadamore@1237: uninits = uninitsTry.dup(); mcimadamore@1237: scan(param); mcimadamore@1237: inits.incl(param.sym.adr); mcimadamore@1237: uninits.excl(param.sym.adr); mcimadamore@1237: scan(l.head.body); mcimadamore@1237: initsEnd.andSet(inits); mcimadamore@1237: uninitsEnd.andSet(uninits); mcimadamore@1237: nextadr = nextadrCatch; mcimadamore@1237: } mcimadamore@1237: if (tree.finalizer != null) { mcimadamore@1237: inits = initsTry.dup(); mcimadamore@1237: uninits = uninitsTry.dup(); mcimadamore@1237: ListBuffer exits = pendingExits; mcimadamore@1237: pendingExits = prevPendingExits; mcimadamore@1237: scan(tree.finalizer); mcimadamore@1237: if (!tree.finallyCanCompleteNormally) { mcimadamore@1237: // discard exits and exceptions from try and finally mcimadamore@1237: } else { mcimadamore@1237: uninits.andSet(uninitsEnd); mcimadamore@1237: // FIX: this doesn't preserve source order of exits in catch mcimadamore@1237: // versus finally! mcimadamore@1237: while (exits.nonEmpty()) { mcimadamore@1237: AssignPendingExit exit = exits.next(); mcimadamore@1237: if (exit.exit_inits != null) { mcimadamore@1237: exit.exit_inits.orSet(inits); mcimadamore@1237: exit.exit_uninits.andSet(uninits); mcimadamore@1237: } mcimadamore@1237: pendingExits.append(exit); mcimadamore@1237: } mcimadamore@1237: inits.orSet(initsEnd); mcimadamore@1237: } duke@1: } else { mcimadamore@1237: inits = initsEnd; mcimadamore@1237: uninits = uninitsEnd; mcimadamore@1237: ListBuffer exits = pendingExits; mcimadamore@1237: pendingExits = prevPendingExits; mcimadamore@1237: while (exits.nonEmpty()) pendingExits.append(exits.next()); duke@1: } mcimadamore@1237: uninitsTry.andSet(uninitsTryPrev).andSet(uninits); mcimadamore@1237: } duke@1: mcimadamore@1237: public void visitConditional(JCConditional tree) { mcimadamore@1237: scanCond(tree.cond); mcimadamore@1237: Bits initsBeforeElse = initsWhenFalse; mcimadamore@1237: Bits uninitsBeforeElse = uninitsWhenFalse; mcimadamore@1237: inits = initsWhenTrue; mcimadamore@1237: uninits = uninitsWhenTrue; jjg@1374: if (tree.truepart.type.hasTag(BOOLEAN) && jjg@1374: tree.falsepart.type.hasTag(BOOLEAN)) { mcimadamore@1237: // if b and c are boolean valued, then mcimadamore@1237: // v is (un)assigned after a?b:c when true iff mcimadamore@1237: // v is (un)assigned after b when true and mcimadamore@1237: // v is (un)assigned after c when true mcimadamore@1237: scanCond(tree.truepart); mcimadamore@1237: Bits initsAfterThenWhenTrue = initsWhenTrue.dup(); mcimadamore@1237: Bits initsAfterThenWhenFalse = initsWhenFalse.dup(); mcimadamore@1237: Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup(); mcimadamore@1237: Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup(); mcimadamore@1237: inits = initsBeforeElse; mcimadamore@1237: uninits = uninitsBeforeElse; mcimadamore@1237: scanCond(tree.falsepart); mcimadamore@1237: initsWhenTrue.andSet(initsAfterThenWhenTrue); mcimadamore@1237: initsWhenFalse.andSet(initsAfterThenWhenFalse); mcimadamore@1237: uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); mcimadamore@1237: uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); mcimadamore@1237: } else { mcimadamore@1237: scanExpr(tree.truepart); mcimadamore@1237: Bits initsAfterThen = inits.dup(); mcimadamore@1237: Bits uninitsAfterThen = uninits.dup(); mcimadamore@1237: inits = initsBeforeElse; mcimadamore@1237: uninits = uninitsBeforeElse; mcimadamore@1237: scanExpr(tree.falsepart); mcimadamore@1237: inits.andSet(initsAfterThen); mcimadamore@1237: uninits.andSet(uninitsAfterThen); duke@1: } duke@1: } duke@1: mcimadamore@1237: public void visitIf(JCIf tree) { mcimadamore@1237: scanCond(tree.cond); mcimadamore@1237: Bits initsBeforeElse = initsWhenFalse; mcimadamore@1237: Bits uninitsBeforeElse = uninitsWhenFalse; mcimadamore@1237: inits = initsWhenTrue; mcimadamore@1237: uninits = uninitsWhenTrue; mcimadamore@1237: scan(tree.thenpart); mcimadamore@1237: if (tree.elsepart != null) { mcimadamore@1237: Bits initsAfterThen = inits.dup(); mcimadamore@1237: Bits uninitsAfterThen = uninits.dup(); mcimadamore@1237: inits = initsBeforeElse; mcimadamore@1237: uninits = uninitsBeforeElse; mcimadamore@1237: scan(tree.elsepart); mcimadamore@1237: inits.andSet(initsAfterThen); mcimadamore@1237: uninits.andSet(uninitsAfterThen); darcy@609: } else { mcimadamore@1237: inits.andSet(initsBeforeElse); mcimadamore@1237: uninits.andSet(uninitsBeforeElse); darcy@609: } darcy@609: } darcy@609: mcimadamore@1237: public void visitBreak(JCBreak tree) { mcimadamore@1237: recordExit(tree, new AssignPendingExit(tree, inits, uninits)); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitContinue(JCContinue tree) { mcimadamore@1237: recordExit(tree, new AssignPendingExit(tree, inits, uninits)); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitReturn(JCReturn tree) { mcimadamore@1237: scanExpr(tree.expr); mcimadamore@1237: recordExit(tree, new AssignPendingExit(tree, inits, uninits)); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitThrow(JCThrow tree) { mcimadamore@1237: scanExpr(tree.expr); mcimadamore@1237: markDead(); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitApply(JCMethodInvocation tree) { mcimadamore@1237: scanExpr(tree.meth); mcimadamore@1237: scanExprs(tree.args); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitNewClass(JCNewClass tree) { mcimadamore@1237: scanExpr(tree.encl); mcimadamore@1237: scanExprs(tree.args); mcimadamore@1237: scan(tree.def); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1348: @Override mcimadamore@1348: public void visitLambda(JCLambda tree) { mcimadamore@1348: Bits prevUninits = uninits; mcimadamore@1348: Bits prevInits = inits; mcimadamore@1348: int returnadrPrev = returnadr; mcimadamore@1348: ListBuffer prevPending = pendingExits; mcimadamore@1348: try { mcimadamore@1348: returnadr = nextadr; mcimadamore@1348: pendingExits = new ListBuffer(); mcimadamore@1348: for (List l = tree.params; l.nonEmpty(); l = l.tail) { mcimadamore@1348: JCVariableDecl def = l.head; mcimadamore@1348: scan(def); mcimadamore@1348: inits.incl(def.sym.adr); mcimadamore@1348: uninits.excl(def.sym.adr); mcimadamore@1348: } mcimadamore@1348: if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { mcimadamore@1348: scanExpr(tree.body); mcimadamore@1348: } else { mcimadamore@1348: scan(tree.body); mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: finally { mcimadamore@1348: returnadr = returnadrPrev; mcimadamore@1348: uninits = prevUninits; mcimadamore@1348: inits = prevInits; mcimadamore@1348: pendingExits = prevPending; mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: mcimadamore@1237: public void visitNewArray(JCNewArray tree) { mcimadamore@1237: scanExprs(tree.dims); mcimadamore@1237: scanExprs(tree.elems); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitAssert(JCAssert tree) { mcimadamore@1237: Bits initsExit = inits.dup(); mcimadamore@1237: Bits uninitsExit = uninits.dup(); mcimadamore@1237: scanCond(tree.cond); mcimadamore@1237: uninitsExit.andSet(uninitsWhenTrue); mcimadamore@1237: if (tree.detail != null) { mcimadamore@1237: inits = initsWhenFalse; mcimadamore@1237: uninits = uninitsWhenFalse; mcimadamore@1237: scanExpr(tree.detail); duke@1: } mcimadamore@1237: inits = initsExit; mcimadamore@1237: uninits = uninitsExit; duke@1: } mcimadamore@1237: mcimadamore@1237: public void visitAssign(JCAssign tree) { mcimadamore@1237: JCTree lhs = TreeInfo.skipParens(tree.lhs); mcimadamore@1297: if (!(lhs instanceof JCIdent)) { mcimadamore@1297: scanExpr(lhs); mcimadamore@1297: } mcimadamore@1237: scanExpr(tree.rhs); mcimadamore@1237: letInit(lhs); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitAssignop(JCAssignOp tree) { mcimadamore@1237: scanExpr(tree.lhs); mcimadamore@1237: scanExpr(tree.rhs); mcimadamore@1237: letInit(tree.lhs); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitUnary(JCUnary tree) { mcimadamore@1237: switch (tree.getTag()) { mcimadamore@1237: case NOT: mcimadamore@1237: scanCond(tree.arg); mcimadamore@1237: Bits t = initsWhenFalse; mcimadamore@1237: initsWhenFalse = initsWhenTrue; mcimadamore@1237: initsWhenTrue = t; mcimadamore@1237: t = uninitsWhenFalse; mcimadamore@1237: uninitsWhenFalse = uninitsWhenTrue; mcimadamore@1237: uninitsWhenTrue = t; mcimadamore@1237: break; mcimadamore@1237: case PREINC: case POSTINC: mcimadamore@1237: case PREDEC: case POSTDEC: mcimadamore@1237: scanExpr(tree.arg); mcimadamore@1237: letInit(tree.arg); mcimadamore@1237: break; mcimadamore@1237: default: mcimadamore@1237: scanExpr(tree.arg); duke@1: } duke@1: } duke@1: mcimadamore@1237: public void visitBinary(JCBinary tree) { mcimadamore@1237: switch (tree.getTag()) { mcimadamore@1237: case AND: mcimadamore@1237: scanCond(tree.lhs); mcimadamore@1237: Bits initsWhenFalseLeft = initsWhenFalse; mcimadamore@1237: Bits uninitsWhenFalseLeft = uninitsWhenFalse; mcimadamore@1237: inits = initsWhenTrue; mcimadamore@1237: uninits = uninitsWhenTrue; mcimadamore@1237: scanCond(tree.rhs); mcimadamore@1237: initsWhenFalse.andSet(initsWhenFalseLeft); mcimadamore@1237: uninitsWhenFalse.andSet(uninitsWhenFalseLeft); mcimadamore@1237: break; mcimadamore@1237: case OR: mcimadamore@1237: scanCond(tree.lhs); mcimadamore@1237: Bits initsWhenTrueLeft = initsWhenTrue; mcimadamore@1237: Bits uninitsWhenTrueLeft = uninitsWhenTrue; mcimadamore@1237: inits = initsWhenFalse; mcimadamore@1237: uninits = uninitsWhenFalse; mcimadamore@1237: scanCond(tree.rhs); mcimadamore@1237: initsWhenTrue.andSet(initsWhenTrueLeft); mcimadamore@1237: uninitsWhenTrue.andSet(uninitsWhenTrueLeft); mcimadamore@1237: break; mcimadamore@1237: default: mcimadamore@1237: scanExpr(tree.lhs); mcimadamore@1237: scanExpr(tree.rhs); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitIdent(JCIdent tree) { mcimadamore@1237: if (tree.sym.kind == VAR) { mcimadamore@1237: checkInit(tree.pos(), (VarSymbol)tree.sym); mcimadamore@1237: referenced(tree.sym); mcimadamore@1237: } mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: void referenced(Symbol sym) { mcimadamore@1237: unrefdResources.remove(sym); mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: public void visitTopLevel(JCCompilationUnit tree) { mcimadamore@1237: // Do nothing for TopLevel since each class is visited individually mcimadamore@1237: } mcimadamore@1237: mcimadamore@1237: /************************************************************************** mcimadamore@1237: * main method mcimadamore@1237: *************************************************************************/ mcimadamore@1237: mcimadamore@1237: /** Perform definite assignment/unassignment analysis on a tree. mcimadamore@1237: */ mcimadamore@1237: public void analyzeTree(Env env, TreeMaker make) { mcimadamore@1297: analyzeTree(env, env.tree, make); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void analyzeTree(Env env, JCTree tree, TreeMaker make) { mcimadamore@1237: try { mcimadamore@1237: attrEnv = env; mcimadamore@1237: Flow.this.make = make; mcimadamore@1297: startPos = tree.pos().getStartPosition(); mcimadamore@1237: inits = new Bits(); mcimadamore@1237: uninits = new Bits(); mcimadamore@1237: uninitsTry = new Bits(); mcimadamore@1237: initsWhenTrue = initsWhenFalse = mcimadamore@1237: uninitsWhenTrue = uninitsWhenFalse = null; mcimadamore@1237: if (vars == null) mcimadamore@1237: vars = new VarSymbol[32]; mcimadamore@1237: else mcimadamore@1237: for (int i=0; i(); mcimadamore@1237: this.classDef = null; mcimadamore@1237: unrefdResources = new Scope(env.enclClass.sym); mcimadamore@1237: scan(tree); mcimadamore@1237: } finally { mcimadamore@1237: // note that recursive invocations of this method fail hard mcimadamore@1297: startPos = -1; mcimadamore@1237: inits = uninits = uninitsTry = null; mcimadamore@1237: initsWhenTrue = initsWhenFalse = mcimadamore@1237: uninitsWhenTrue = uninitsWhenFalse = null; mcimadamore@1237: if (vars != null) for (int i=0; i { mcimadamore@1297: mcimadamore@1297: JCTree currentTree; //local class or lambda mcimadamore@1297: mcimadamore@1297: @Override mcimadamore@1297: void markDead() { mcimadamore@1297: //do nothing mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: @SuppressWarnings("fallthrough") mcimadamore@1297: void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) { mcimadamore@1297: if (currentTree != null && mcimadamore@1297: sym.owner.kind == MTH && mcimadamore@1297: sym.pos < currentTree.getStartPosition()) { mcimadamore@1297: switch (currentTree.getTag()) { mcimadamore@1297: case CLASSDEF: mcimadamore@1297: if (!allowEffectivelyFinalInInnerClasses) { mcimadamore@1297: if ((sym.flags() & FINAL) == 0) { mcimadamore@1297: reportInnerClsNeedsFinalError(pos, sym); mcimadamore@1297: } mcimadamore@1297: break; mcimadamore@1297: } mcimadamore@1297: case LAMBDA: mcimadamore@1297: if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { mcimadamore@1297: reportEffectivelyFinalError(pos, sym); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: @SuppressWarnings("fallthrough") mcimadamore@1297: void letInit(JCTree tree) { mcimadamore@1297: tree = TreeInfo.skipParens(tree); mcimadamore@1297: if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { mcimadamore@1297: Symbol sym = TreeInfo.symbol(tree); mcimadamore@1297: if (currentTree != null && mcimadamore@1297: sym.kind == VAR && mcimadamore@1297: sym.owner.kind == MTH && mcimadamore@1297: ((VarSymbol)sym).pos < currentTree.getStartPosition()) { mcimadamore@1297: switch (currentTree.getTag()) { mcimadamore@1297: case CLASSDEF: mcimadamore@1297: if (!allowEffectivelyFinalInInnerClasses) { mcimadamore@1297: reportInnerClsNeedsFinalError(tree, sym); mcimadamore@1297: break; mcimadamore@1297: } mcimadamore@1297: case LAMBDA: mcimadamore@1297: reportEffectivelyFinalError(tree, sym); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { mcimadamore@1297: String subKey = currentTree.hasTag(LAMBDA) ? mcimadamore@1297: "lambda" : "inner.cls"; mcimadamore@1297: log.error(pos, "cant.ref.non.effectively.final.var", sym, diags.fragment(subKey)); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: void reportInnerClsNeedsFinalError(DiagnosticPosition pos, Symbol sym) { mcimadamore@1297: log.error(pos, mcimadamore@1297: "local.var.accessed.from.icls.needs.final", mcimadamore@1297: sym); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /************************************************************************* mcimadamore@1297: * Visitor methods for statements and definitions mcimadamore@1297: *************************************************************************/ mcimadamore@1297: mcimadamore@1297: /* ------------ Visitor methods for various sorts of trees -------------*/ mcimadamore@1297: mcimadamore@1297: public void visitClassDef(JCClassDecl tree) { mcimadamore@1297: JCTree prevTree = currentTree; mcimadamore@1297: try { mcimadamore@1297: currentTree = tree.sym.isLocal() ? tree : null; mcimadamore@1297: super.visitClassDef(tree); mcimadamore@1297: } finally { mcimadamore@1297: currentTree = prevTree; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: @Override mcimadamore@1297: public void visitLambda(JCLambda tree) { mcimadamore@1297: JCTree prevTree = currentTree; mcimadamore@1297: try { mcimadamore@1297: currentTree = tree; mcimadamore@1297: super.visitLambda(tree); mcimadamore@1297: } finally { mcimadamore@1297: currentTree = prevTree; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: @Override mcimadamore@1297: public void visitIdent(JCIdent tree) { mcimadamore@1297: if (tree.sym.kind == VAR) { mcimadamore@1297: checkEffectivelyFinal(tree, (VarSymbol)tree.sym); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitAssign(JCAssign tree) { mcimadamore@1297: JCTree lhs = TreeInfo.skipParens(tree.lhs); mcimadamore@1297: if (!(lhs instanceof JCIdent)) { mcimadamore@1297: scan(lhs); mcimadamore@1297: } mcimadamore@1297: scan(tree.rhs); mcimadamore@1297: letInit(lhs); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitAssignop(JCAssignOp tree) { mcimadamore@1297: scan(tree.lhs); mcimadamore@1297: scan(tree.rhs); mcimadamore@1297: letInit(tree.lhs); mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitUnary(JCUnary tree) { mcimadamore@1297: switch (tree.getTag()) { mcimadamore@1297: case PREINC: case POSTINC: mcimadamore@1297: case PREDEC: case POSTDEC: mcimadamore@1297: scan(tree.arg); mcimadamore@1297: letInit(tree.arg); mcimadamore@1297: break; mcimadamore@1297: default: mcimadamore@1297: scan(tree.arg); mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: public void visitTopLevel(JCCompilationUnit tree) { mcimadamore@1297: // Do nothing for TopLevel since each class is visited individually mcimadamore@1297: } mcimadamore@1297: mcimadamore@1297: /************************************************************************** mcimadamore@1297: * main method mcimadamore@1297: *************************************************************************/ mcimadamore@1297: mcimadamore@1297: /** Perform definite assignment/unassignment analysis on a tree. mcimadamore@1297: */ mcimadamore@1297: public void analyzeTree(Env env, TreeMaker make) { mcimadamore@1297: analyzeTree(env, env.tree, make); mcimadamore@1297: } mcimadamore@1297: public void analyzeTree(Env env, JCTree tree, TreeMaker make) { mcimadamore@1297: try { mcimadamore@1297: attrEnv = env; mcimadamore@1297: Flow.this.make = make; mcimadamore@1297: pendingExits = new ListBuffer(); mcimadamore@1297: scan(tree); mcimadamore@1297: } finally { mcimadamore@1297: pendingExits = null; mcimadamore@1297: Flow.this.make = null; mcimadamore@1297: } mcimadamore@1297: } mcimadamore@1297: } duke@1: }