duke@1: /*
jjg@1521: * Copyright (c) 1999, 2013, 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.*;
jjg@1521: import com.sun.tools.javac.comp.Resolve;
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 extends JCStatement> trees) {
mcimadamore@1297: if (trees != null)
mcimadamore@1297: for (List extends JCStatement> 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 extends JCExpression> trees) {
mcimadamore@1237: if (trees != null)
mcimadamore@1237: for (List extends JCExpression> 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:
jjg@1521: public void visitAnnotatedType(JCAnnotatedType tree) {
jjg@1521: // annotations don't get scanned
jjg@1521: tree.underlyingType.accept(this);
jjg@1521: }
jjg@1521:
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: }