duke@1: /*
duke@1: * Copyright 1999-2006 Sun Microsystems, Inc. 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
duke@1: * published by the Free Software Foundation. Sun designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
duke@1: * by Sun 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: *
duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or
duke@1: * have any 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:
duke@1: import com.sun.tools.javac.code.*;
duke@1: import com.sun.tools.javac.tree.*;
duke@1: import com.sun.tools.javac.util.*;
duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
duke@1:
duke@1: import com.sun.tools.javac.code.Symbol.*;
duke@1: import com.sun.tools.javac.tree.JCTree.*;
duke@1:
duke@1: import static com.sun.tools.javac.code.Flags.*;
duke@1: import static com.sun.tools.javac.code.Kinds.*;
duke@1: import static com.sun.tools.javac.code.TypeTags.*;
duke@1:
duke@1: /** This pass implements dataflow analysis for Java programs.
duke@1: * Liveness analysis checks that every statement is reachable.
duke@1: * Exception analysis ensures that every checked exception that is
duke@1: * thrown is declared or caught. Definite assignment analysis
duke@1: * ensures that each variable is assigned when used. Definite
duke@1: * unassignment analysis ensures that no final variable is assigned
duke@1: * more than once.
duke@1: *
duke@1: *
The second edition of 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: *
duke@1: *
There is no spec in JLS2 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
duke@1: * global variable "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: *
duke@1: *
This is NOT part of any API supported by Sun Microsystems. If
duke@1: * 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: */
duke@1: public class Flow extends TreeScanner {
duke@1: protected static final Context.Key flowKey =
duke@1: new Context.Key();
duke@1:
duke@1: private final Name.Table 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;
duke@1: private Lint lint;
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:
duke@1: protected Flow(Context context) {
duke@1: context.put(flowKey, this);
duke@1:
duke@1: names = Name.Table.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);
duke@1: }
duke@1:
duke@1: /** A flag that indicates whether the last statement could
duke@1: * complete normally.
duke@1: */
duke@1: private boolean alive;
duke@1:
duke@1: /** The set of definitely assigned variables.
duke@1: */
duke@1: Bits inits;
duke@1:
duke@1: /** The set of definitely unassigned variables.
duke@1: */
duke@1: Bits uninits;
duke@1:
duke@1: /** The set of variables that are definitely unassigned everywhere
duke@1: * in current try block. This variable is maintained lazily; it is
duke@1: * updated only when something gets removed from uninits,
duke@1: * typically by being assigned in reachable code. To obtain the
duke@1: * correct set of variables which are definitely unassigned
duke@1: * anywhere in current try block, intersect uninitsTry and
duke@1: * uninits.
duke@1: */
duke@1: Bits uninitsTry;
duke@1:
duke@1: /** When analyzing a condition, inits and uninits are null.
duke@1: * Instead we have:
duke@1: */
duke@1: Bits initsWhenTrue;
duke@1: Bits initsWhenFalse;
duke@1: Bits uninitsWhenTrue;
duke@1: Bits uninitsWhenFalse;
duke@1:
duke@1: /** A mapping from addresses to variable symbols.
duke@1: */
duke@1: VarSymbol[] vars;
duke@1:
duke@1: /** The current class being defined.
duke@1: */
duke@1: JCClassDecl classDef;
duke@1:
duke@1: /** The first variable sequence number in this class definition.
duke@1: */
duke@1: int firstadr;
duke@1:
duke@1: /** The next available variable sequence number.
duke@1: */
duke@1: int nextadr;
duke@1:
duke@1: /** The list of possibly thrown declarable exceptions.
duke@1: */
duke@1: List thrown;
duke@1:
duke@1: /** The list of exceptions that are either caught or declared to be
duke@1: * thrown.
duke@1: */
duke@1: List caught;
duke@1:
duke@1: /** Set when processing a loop body the second time for DU analysis. */
duke@1: boolean loopPassTwo = false;
duke@1:
duke@1: /*-------------------- Environments ----------------------*/
duke@1:
duke@1: /** A pending exit. These are the statements return, break, and
duke@1: * continue. In addition, exception-throwing expressions or
duke@1: * statements are put here when not known to be caught. This
duke@1: * will typically result in an error unless it is within a
duke@1: * try-finally whose finally block cannot complete normally.
duke@1: */
duke@1: static class PendingExit {
duke@1: JCTree tree;
duke@1: Bits inits;
duke@1: Bits uninits;
duke@1: Type thrown;
duke@1: PendingExit(JCTree tree, Bits inits, Bits uninits) {
duke@1: this.tree = tree;
duke@1: this.inits = inits.dup();
duke@1: this.uninits = uninits.dup();
duke@1: }
duke@1: PendingExit(JCTree tree, Type thrown) {
duke@1: this.tree = tree;
duke@1: this.thrown = thrown;
duke@1: }
duke@1: }
duke@1:
duke@1: /** The currently pending exits that go from current inner blocks
duke@1: * to an enclosing block, in source order.
duke@1: */
duke@1: ListBuffer pendingExits;
duke@1:
duke@1: /*-------------------- Exceptions ----------------------*/
duke@1:
duke@1: /** Complain that pending exceptions are not caught.
duke@1: */
duke@1: void errorUncaught() {
duke@1: for (PendingExit exit = pendingExits.next();
duke@1: exit != null;
duke@1: exit = pendingExits.next()) {
duke@1: boolean synthetic = classDef != null &&
duke@1: classDef.pos == exit.tree.pos;
duke@1: log.error(exit.tree.pos(),
duke@1: synthetic
duke@1: ? "unreported.exception.default.constructor"
duke@1: : "unreported.exception.need.to.catch.or.throw",
duke@1: exit.thrown);
duke@1: }
duke@1: }
duke@1:
duke@1: /** Record that exception is potentially thrown and check that it
duke@1: * is caught.
duke@1: */
duke@1: void markThrown(JCTree tree, Type exc) {
duke@1: if (!chk.isUnchecked(tree.pos(), exc)) {
duke@1: if (!chk.isHandled(exc, caught))
duke@1: pendingExits.append(new PendingExit(tree, exc));
duke@1: thrown = chk.incl(exc, thrown);
duke@1: }
duke@1: }
duke@1:
duke@1: /*-------------- Processing variables ----------------------*/
duke@1:
duke@1: /** Do we need to track init/uninit state of this symbol?
duke@1: * I.e. is symbol either a local or a blank final variable?
duke@1: */
duke@1: boolean trackable(VarSymbol sym) {
duke@1: return
duke@1: (sym.owner.kind == MTH ||
duke@1: ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
duke@1: classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)));
duke@1: }
duke@1:
duke@1: /** Initialize new trackable variable by setting its address field
duke@1: * to the next available sequence number and entering it under that
duke@1: * index into the vars array.
duke@1: */
duke@1: void newVar(VarSymbol sym) {
duke@1: if (nextadr == vars.length) {
duke@1: VarSymbol[] newvars = new VarSymbol[nextadr * 2];
duke@1: System.arraycopy(vars, 0, newvars, 0, nextadr);
duke@1: vars = newvars;
duke@1: }
duke@1: sym.adr = nextadr;
duke@1: vars[nextadr] = sym;
duke@1: inits.excl(nextadr);
duke@1: uninits.incl(nextadr);
duke@1: nextadr++;
duke@1: }
duke@1:
duke@1: /** Record an initialization of a trackable variable.
duke@1: */
duke@1: void letInit(DiagnosticPosition pos, VarSymbol sym) {
duke@1: if (sym.adr >= firstadr && trackable(sym)) {
duke@1: if ((sym.flags() & FINAL) != 0) {
duke@1: if ((sym.flags() & PARAMETER) != 0) {
duke@1: log.error(pos, "final.parameter.may.not.be.assigned",
duke@1: sym);
duke@1: } else if (!uninits.isMember(sym.adr)) {
duke@1: log.error(pos,
duke@1: loopPassTwo
duke@1: ? "var.might.be.assigned.in.loop"
duke@1: : "var.might.already.be.assigned",
duke@1: sym);
duke@1: } else if (!inits.isMember(sym.adr)) {
duke@1: // reachable assignment
duke@1: uninits.excl(sym.adr);
duke@1: uninitsTry.excl(sym.adr);
duke@1: } else {
duke@1: //log.rawWarning(pos, "unreachable assignment");//DEBUG
duke@1: uninits.excl(sym.adr);
duke@1: }
duke@1: }
duke@1: inits.incl(sym.adr);
duke@1: } else if ((sym.flags() & FINAL) != 0) {
duke@1: log.error(pos, "var.might.already.be.assigned", sym);
duke@1: }
duke@1: }
duke@1:
duke@1: /** If tree is either a simple name or of the form this.name or
duke@1: * C.this.name, and tree represents a trackable variable,
duke@1: * record an initialization of the variable.
duke@1: */
duke@1: void letInit(JCTree tree) {
duke@1: tree = TreeInfo.skipParens(tree);
duke@1: if (tree.getTag() == JCTree.IDENT || tree.getTag() == JCTree.SELECT) {
duke@1: Symbol sym = TreeInfo.symbol(tree);
duke@1: letInit(tree.pos(), (VarSymbol)sym);
duke@1: }
duke@1: }
duke@1:
duke@1: /** Check that trackable variable is initialized.
duke@1: */
duke@1: void checkInit(DiagnosticPosition pos, VarSymbol sym) {
duke@1: if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
duke@1: trackable(sym) &&
duke@1: !inits.isMember(sym.adr)) {
duke@1: log.error(pos, "var.might.not.have.been.initialized",
duke@1: sym);
duke@1: inits.incl(sym.adr);
duke@1: }
duke@1: }
duke@1:
duke@1: /*-------------------- Handling jumps ----------------------*/
duke@1:
duke@1: /** Record an outward transfer of control. */
duke@1: void recordExit(JCTree tree) {
duke@1: pendingExits.append(new PendingExit(tree, inits, uninits));
duke@1: markDead();
duke@1: }
duke@1:
duke@1: /** Resolve all breaks of this statement. */
duke@1: boolean resolveBreaks(JCTree tree,
duke@1: ListBuffer oldPendingExits) {
duke@1: boolean result = false;
duke@1: List exits = pendingExits.toList();
duke@1: pendingExits = oldPendingExits;
duke@1: for (; exits.nonEmpty(); exits = exits.tail) {
duke@1: PendingExit exit = exits.head;
duke@1: if (exit.tree.getTag() == JCTree.BREAK &&
duke@1: ((JCBreak) exit.tree).target == tree) {
duke@1: inits.andSet(exit.inits);
duke@1: uninits.andSet(exit.uninits);
duke@1: result = true;
duke@1: } else {
duke@1: pendingExits.append(exit);
duke@1: }
duke@1: }
duke@1: return result;
duke@1: }
duke@1:
duke@1: /** Resolve all continues of this statement. */
duke@1: boolean resolveContinues(JCTree tree) {
duke@1: boolean result = false;
duke@1: List exits = pendingExits.toList();
duke@1: pendingExits = new ListBuffer();
duke@1: for (; exits.nonEmpty(); exits = exits.tail) {
duke@1: PendingExit exit = exits.head;
duke@1: if (exit.tree.getTag() == JCTree.CONTINUE &&
duke@1: ((JCContinue) exit.tree).target == tree) {
duke@1: inits.andSet(exit.inits);
duke@1: uninits.andSet(exit.uninits);
duke@1: result = true;
duke@1: } else {
duke@1: pendingExits.append(exit);
duke@1: }
duke@1: }
duke@1: return result;
duke@1: }
duke@1:
duke@1: /** Record that statement is unreachable.
duke@1: */
duke@1: void markDead() {
duke@1: inits.inclRange(firstadr, nextadr);
duke@1: uninits.inclRange(firstadr, nextadr);
duke@1: alive = false;
duke@1: }
duke@1:
duke@1: /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets
duke@1: */
duke@1: void split() {
duke@1: initsWhenFalse = inits.dup();
duke@1: uninitsWhenFalse = uninits.dup();
duke@1: initsWhenTrue = inits;
duke@1: uninitsWhenTrue = uninits;
duke@1: inits = uninits = null;
duke@1: }
duke@1:
duke@1: /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets.
duke@1: */
duke@1: void merge() {
duke@1: inits = initsWhenFalse.andSet(initsWhenTrue);
duke@1: uninits = uninitsWhenFalse.andSet(uninitsWhenTrue);
duke@1: }
duke@1:
duke@1: /* ************************************************************************
duke@1: * Visitor methods for statements and definitions
duke@1: *************************************************************************/
duke@1:
duke@1: /** Analyze a definition.
duke@1: */
duke@1: void scanDef(JCTree tree) {
duke@1: scanStat(tree);
duke@1: if (tree != null && tree.getTag() == JCTree.BLOCK && !alive) {
duke@1: log.error(tree.pos(),
duke@1: "initializer.must.be.able.to.complete.normally");
duke@1: }
duke@1: }
duke@1:
duke@1: /** Analyze a statement. Check that statement is reachable.
duke@1: */
duke@1: void scanStat(JCTree tree) {
duke@1: if (!alive && tree != null) {
duke@1: log.error(tree.pos(), "unreachable.stmt");
duke@1: if (tree.getTag() != JCTree.SKIP) alive = true;
duke@1: }
duke@1: scan(tree);
duke@1: }
duke@1:
duke@1: /** Analyze list of statements.
duke@1: */
duke@1: void scanStats(List extends JCStatement> trees) {
duke@1: if (trees != null)
duke@1: for (List extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
duke@1: scanStat(l.head);
duke@1: }
duke@1:
duke@1: /** Analyze an expression. Make sure to set (un)inits rather than
duke@1: * (un)initsWhenTrue(WhenFalse) on exit.
duke@1: */
duke@1: void scanExpr(JCTree tree) {
duke@1: if (tree != null) {
duke@1: scan(tree);
duke@1: if (inits == null) merge();
duke@1: }
duke@1: }
duke@1:
duke@1: /** Analyze a list of expressions.
duke@1: */
duke@1: void scanExprs(List extends JCExpression> trees) {
duke@1: if (trees != null)
duke@1: for (List extends JCExpression> l = trees; l.nonEmpty(); l = l.tail)
duke@1: scanExpr(l.head);
duke@1: }
duke@1:
duke@1: /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
duke@1: * rather than (un)inits on exit.
duke@1: */
duke@1: void scanCond(JCTree tree) {
duke@1: if (tree.type.isFalse()) {
duke@1: if (inits == null) merge();
duke@1: initsWhenTrue = inits.dup();
duke@1: initsWhenTrue.inclRange(firstadr, nextadr);
duke@1: uninitsWhenTrue = uninits.dup();
duke@1: uninitsWhenTrue.inclRange(firstadr, nextadr);
duke@1: initsWhenFalse = inits;
duke@1: uninitsWhenFalse = uninits;
duke@1: } else if (tree.type.isTrue()) {
duke@1: if (inits == null) merge();
duke@1: initsWhenFalse = inits.dup();
duke@1: initsWhenFalse.inclRange(firstadr, nextadr);
duke@1: uninitsWhenFalse = uninits.dup();
duke@1: uninitsWhenFalse.inclRange(firstadr, nextadr);
duke@1: initsWhenTrue = inits;
duke@1: uninitsWhenTrue = uninits;
duke@1: } else {
duke@1: scan(tree);
duke@1: if (inits != null) split();
duke@1: }
duke@1: inits = uninits = null;
duke@1: }
duke@1:
duke@1: /* ------------ Visitor methods for various sorts of trees -------------*/
duke@1:
duke@1: public void visitClassDef(JCClassDecl tree) {
duke@1: if (tree.sym == null) return;
duke@1:
duke@1: JCClassDecl classDefPrev = classDef;
duke@1: List thrownPrev = thrown;
duke@1: List caughtPrev = caught;
duke@1: boolean alivePrev = alive;
duke@1: int firstadrPrev = firstadr;
duke@1: int nextadrPrev = nextadr;
duke@1: ListBuffer pendingExitsPrev = pendingExits;
duke@1: Lint lintPrev = lint;
duke@1:
duke@1: pendingExits = new ListBuffer();
duke@1: if (tree.name != names.empty) {
duke@1: caught = List.nil();
duke@1: firstadr = nextadr;
duke@1: }
duke@1: classDef = tree;
duke@1: thrown = List.nil();
duke@1: lint = lint.augment(tree.sym.attributes_field);
duke@1:
duke@1: try {
duke@1: // define all the static fields
duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
duke@1: if (l.head.getTag() == JCTree.VARDEF) {
duke@1: JCVariableDecl def = (JCVariableDecl)l.head;
duke@1: if ((def.mods.flags & STATIC) != 0) {
duke@1: VarSymbol sym = def.sym;
duke@1: if (trackable(sym))
duke@1: newVar(sym);
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: // process all the static initializers
duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
duke@1: if (l.head.getTag() != JCTree.METHODDEF &&
duke@1: (TreeInfo.flags(l.head) & STATIC) != 0) {
duke@1: scanDef(l.head);
duke@1: errorUncaught();
duke@1: }
duke@1: }
duke@1:
duke@1: // add intersection of all thrown clauses of initial constructors
duke@1: // to set of caught exceptions, unless class is anonymous.
duke@1: if (tree.name != names.empty) {
duke@1: boolean firstConstructor = true;
duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
duke@1: if (TreeInfo.isInitialConstructor(l.head)) {
duke@1: List mthrown =
duke@1: ((JCMethodDecl) l.head).sym.type.getThrownTypes();
duke@1: if (firstConstructor) {
duke@1: caught = mthrown;
duke@1: firstConstructor = false;
duke@1: } else {
duke@1: caught = chk.intersect(mthrown, caught);
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: // define all the instance fields
duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
duke@1: if (l.head.getTag() == JCTree.VARDEF) {
duke@1: JCVariableDecl def = (JCVariableDecl)l.head;
duke@1: if ((def.mods.flags & STATIC) == 0) {
duke@1: VarSymbol sym = def.sym;
duke@1: if (trackable(sym))
duke@1: newVar(sym);
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: // process all the instance initializers
duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
duke@1: if (l.head.getTag() != JCTree.METHODDEF &&
duke@1: (TreeInfo.flags(l.head) & STATIC) == 0) {
duke@1: scanDef(l.head);
duke@1: errorUncaught();
duke@1: }
duke@1: }
duke@1:
duke@1: // in an anonymous class, add the set of thrown exceptions to
duke@1: // the throws clause of the synthetic constructor and propagate
duke@1: // outwards.
duke@1: if (tree.name == names.empty) {
duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
duke@1: if (TreeInfo.isInitialConstructor(l.head)) {
duke@1: JCMethodDecl mdef = (JCMethodDecl)l.head;
duke@1: mdef.thrown = make.Types(thrown);
duke@1: mdef.sym.type.setThrown(thrown);
duke@1: }
duke@1: }
duke@1: thrownPrev = chk.union(thrown, thrownPrev);
duke@1: }
duke@1:
duke@1: // process all the methods
duke@1: for (List l = tree.defs; l.nonEmpty(); l = l.tail) {
duke@1: if (l.head.getTag() == JCTree.METHODDEF) {
duke@1: scan(l.head);
duke@1: errorUncaught();
duke@1: }
duke@1: }
duke@1:
duke@1: thrown = thrownPrev;
duke@1: } finally {
duke@1: pendingExits = pendingExitsPrev;
duke@1: alive = alivePrev;
duke@1: nextadr = nextadrPrev;
duke@1: firstadr = firstadrPrev;
duke@1: caught = caughtPrev;
duke@1: classDef = classDefPrev;
duke@1: lint = lintPrev;
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitMethodDef(JCMethodDecl tree) {
duke@1: if (tree.body == null) return;
duke@1:
duke@1: List caughtPrev = caught;
duke@1: List mthrown = tree.sym.type.getThrownTypes();
duke@1: Bits initsPrev = inits.dup();
duke@1: Bits uninitsPrev = uninits.dup();
duke@1: int nextadrPrev = nextadr;
duke@1: int firstadrPrev = firstadr;
duke@1: Lint lintPrev = lint;
duke@1:
duke@1: lint = lint.augment(tree.sym.attributes_field);
duke@1:
duke@1: assert pendingExits.isEmpty();
duke@1:
duke@1: try {
duke@1: boolean isInitialConstructor =
duke@1: TreeInfo.isInitialConstructor(tree);
duke@1:
duke@1: if (!isInitialConstructor)
duke@1: firstadr = nextadr;
duke@1: for (List l = tree.params; l.nonEmpty(); l = l.tail) {
duke@1: JCVariableDecl def = l.head;
duke@1: scan(def);
duke@1: inits.incl(def.sym.adr);
duke@1: uninits.excl(def.sym.adr);
duke@1: }
duke@1: if (isInitialConstructor)
duke@1: caught = chk.union(caught, mthrown);
duke@1: else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK)
duke@1: caught = mthrown;
duke@1: // else we are in an instance initializer block;
duke@1: // leave caught unchanged.
duke@1:
duke@1: alive = true;
duke@1: scanStat(tree.body);
duke@1:
duke@1: if (alive && tree.sym.type.getReturnType().tag != VOID)
duke@1: log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
duke@1:
duke@1: if (isInitialConstructor) {
duke@1: for (int i = firstadr; i < nextadr; i++)
duke@1: if (vars[i].owner == classDef.sym)
duke@1: checkInit(TreeInfo.diagEndPos(tree.body), vars[i]);
duke@1: }
duke@1: List exits = pendingExits.toList();
duke@1: pendingExits = new ListBuffer();
duke@1: while (exits.nonEmpty()) {
duke@1: PendingExit exit = exits.head;
duke@1: exits = exits.tail;
duke@1: if (exit.thrown == null) {
duke@1: assert exit.tree.getTag() == JCTree.RETURN;
duke@1: if (isInitialConstructor) {
duke@1: inits = exit.inits;
duke@1: for (int i = firstadr; i < nextadr; i++)
duke@1: checkInit(exit.tree.pos(), vars[i]);
duke@1: }
duke@1: } else {
duke@1: // uncaught throws will be reported later
duke@1: pendingExits.append(exit);
duke@1: }
duke@1: }
duke@1: } finally {
duke@1: inits = initsPrev;
duke@1: uninits = uninitsPrev;
duke@1: nextadr = nextadrPrev;
duke@1: firstadr = firstadrPrev;
duke@1: caught = caughtPrev;
duke@1: lint = lintPrev;
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitVarDef(JCVariableDecl tree) {
duke@1: boolean track = trackable(tree.sym);
duke@1: if (track && tree.sym.owner.kind == MTH) newVar(tree.sym);
duke@1: if (tree.init != null) {
duke@1: Lint lintPrev = lint;
duke@1: lint = lint.augment(tree.sym.attributes_field);
duke@1: try{
duke@1: scanExpr(tree.init);
duke@1: if (track) letInit(tree.pos(), tree.sym);
duke@1: } finally {
duke@1: lint = lintPrev;
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitBlock(JCBlock tree) {
duke@1: int nextadrPrev = nextadr;
duke@1: scanStats(tree.stats);
duke@1: nextadr = nextadrPrev;
duke@1: }
duke@1:
duke@1: public void visitDoLoop(JCDoWhileLoop tree) {
duke@1: ListBuffer prevPendingExits = pendingExits;
duke@1: boolean prevLoopPassTwo = loopPassTwo;
duke@1: pendingExits = new ListBuffer();
duke@1: do {
duke@1: Bits uninitsEntry = uninits.dup();
duke@1: scanStat(tree.body);
duke@1: alive |= resolveContinues(tree);
duke@1: scanCond(tree.cond);
duke@1: if (log.nerrors != 0 ||
duke@1: loopPassTwo ||
duke@1: uninitsEntry.diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
duke@1: break;
duke@1: inits = initsWhenTrue;
duke@1: uninits = uninitsEntry.andSet(uninitsWhenTrue);
duke@1: loopPassTwo = true;
duke@1: alive = true;
duke@1: } while (true);
duke@1: loopPassTwo = prevLoopPassTwo;
duke@1: inits = initsWhenFalse;
duke@1: uninits = uninitsWhenFalse;
duke@1: alive = alive && !tree.cond.type.isTrue();
duke@1: alive |= resolveBreaks(tree, prevPendingExits);
duke@1: }
duke@1:
duke@1: public void visitWhileLoop(JCWhileLoop tree) {
duke@1: ListBuffer prevPendingExits = pendingExits;
duke@1: boolean prevLoopPassTwo = loopPassTwo;
duke@1: Bits initsCond;
duke@1: Bits uninitsCond;
duke@1: pendingExits = new ListBuffer();
duke@1: do {
duke@1: Bits uninitsEntry = uninits.dup();
duke@1: scanCond(tree.cond);
duke@1: initsCond = initsWhenFalse;
duke@1: uninitsCond = uninitsWhenFalse;
duke@1: inits = initsWhenTrue;
duke@1: uninits = uninitsWhenTrue;
duke@1: alive = !tree.cond.type.isFalse();
duke@1: scanStat(tree.body);
duke@1: alive |= resolveContinues(tree);
duke@1: if (log.nerrors != 0 ||
duke@1: loopPassTwo ||
duke@1: uninitsEntry.diffSet(uninits).nextBit(firstadr) == -1)
duke@1: break;
duke@1: uninits = uninitsEntry.andSet(uninits);
duke@1: loopPassTwo = true;
duke@1: alive = true;
duke@1: } while (true);
duke@1: loopPassTwo = prevLoopPassTwo;
duke@1: inits = initsCond;
duke@1: uninits = uninitsCond;
duke@1: alive = resolveBreaks(tree, prevPendingExits) ||
duke@1: !tree.cond.type.isTrue();
duke@1: }
duke@1:
duke@1: public void visitForLoop(JCForLoop tree) {
duke@1: ListBuffer prevPendingExits = pendingExits;
duke@1: boolean prevLoopPassTwo = loopPassTwo;
duke@1: int nextadrPrev = nextadr;
duke@1: scanStats(tree.init);
duke@1: Bits initsCond;
duke@1: Bits uninitsCond;
duke@1: pendingExits = new ListBuffer();
duke@1: do {
duke@1: Bits uninitsEntry = uninits.dup();
duke@1: if (tree.cond != null) {
duke@1: scanCond(tree.cond);
duke@1: initsCond = initsWhenFalse;
duke@1: uninitsCond = uninitsWhenFalse;
duke@1: inits = initsWhenTrue;
duke@1: uninits = uninitsWhenTrue;
duke@1: alive = !tree.cond.type.isFalse();
duke@1: } else {
duke@1: initsCond = inits.dup();
duke@1: initsCond.inclRange(firstadr, nextadr);
duke@1: uninitsCond = uninits.dup();
duke@1: uninitsCond.inclRange(firstadr, nextadr);
duke@1: alive = true;
duke@1: }
duke@1: scanStat(tree.body);
duke@1: alive |= resolveContinues(tree);
duke@1: scan(tree.step);
duke@1: if (log.nerrors != 0 ||
duke@1: loopPassTwo ||
duke@1: uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
duke@1: break;
duke@1: uninits = uninitsEntry.andSet(uninits);
duke@1: loopPassTwo = true;
duke@1: alive = true;
duke@1: } while (true);
duke@1: loopPassTwo = prevLoopPassTwo;
duke@1: inits = initsCond;
duke@1: uninits = uninitsCond;
duke@1: alive = resolveBreaks(tree, prevPendingExits) ||
duke@1: tree.cond != null && !tree.cond.type.isTrue();
duke@1: nextadr = nextadrPrev;
duke@1: }
duke@1:
duke@1: public void visitForeachLoop(JCEnhancedForLoop tree) {
duke@1: visitVarDef(tree.var);
duke@1:
duke@1: ListBuffer prevPendingExits = pendingExits;
duke@1: boolean prevLoopPassTwo = loopPassTwo;
duke@1: int nextadrPrev = nextadr;
duke@1: scan(tree.expr);
duke@1: Bits initsStart = inits.dup();
duke@1: Bits uninitsStart = uninits.dup();
duke@1:
duke@1: letInit(tree.pos(), tree.var.sym);
duke@1: pendingExits = new ListBuffer();
duke@1: do {
duke@1: Bits uninitsEntry = uninits.dup();
duke@1: scanStat(tree.body);
duke@1: alive |= resolveContinues(tree);
duke@1: if (log.nerrors != 0 ||
duke@1: loopPassTwo ||
duke@1: uninitsEntry.diffSet(uninits).nextBit(firstadr) == -1)
duke@1: break;
duke@1: uninits = uninitsEntry.andSet(uninits);
duke@1: loopPassTwo = true;
duke@1: alive = true;
duke@1: } while (true);
duke@1: loopPassTwo = prevLoopPassTwo;
duke@1: inits = initsStart;
duke@1: uninits = uninitsStart.andSet(uninits);
duke@1: resolveBreaks(tree, prevPendingExits);
duke@1: alive = true;
duke@1: nextadr = nextadrPrev;
duke@1: }
duke@1:
duke@1: public void visitLabelled(JCLabeledStatement tree) {
duke@1: ListBuffer prevPendingExits = pendingExits;
duke@1: pendingExits = new ListBuffer();
duke@1: scanStat(tree.body);
duke@1: alive |= resolveBreaks(tree, prevPendingExits);
duke@1: }
duke@1:
duke@1: public void visitSwitch(JCSwitch tree) {
duke@1: ListBuffer prevPendingExits = pendingExits;
duke@1: pendingExits = new ListBuffer();
duke@1: int nextadrPrev = nextadr;
duke@1: scanExpr(tree.selector);
duke@1: Bits initsSwitch = inits;
duke@1: Bits uninitsSwitch = uninits.dup();
duke@1: boolean hasDefault = false;
duke@1: for (List l = tree.cases; l.nonEmpty(); l = l.tail) {
duke@1: alive = true;
duke@1: inits = initsSwitch.dup();
duke@1: uninits = uninits.andSet(uninitsSwitch);
duke@1: JCCase c = l.head;
duke@1: if (c.pat == null)
duke@1: hasDefault = true;
duke@1: else
duke@1: scanExpr(c.pat);
duke@1: scanStats(c.stats);
duke@1: addVars(c.stats, initsSwitch, uninitsSwitch);
duke@1: // Warn about fall-through if lint switch fallthrough enabled.
duke@1: if (!loopPassTwo &&
duke@1: alive &&
duke@1: lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
duke@1: c.stats.nonEmpty() && l.tail.nonEmpty())
duke@1: log.warning(l.tail.head.pos(),
duke@1: "possible.fall-through.into.case");
duke@1: }
duke@1: if (!hasDefault) {
duke@1: inits.andSet(initsSwitch);
duke@1: alive = true;
duke@1: }
duke@1: alive |= resolveBreaks(tree, prevPendingExits);
duke@1: nextadr = nextadrPrev;
duke@1: }
duke@1: // where
duke@1: /** Add any variables defined in stats to inits and uninits. */
duke@1: private static void addVars(List stats, Bits inits,
duke@1: Bits uninits) {
duke@1: for (;stats.nonEmpty(); stats = stats.tail) {
duke@1: JCTree stat = stats.head;
duke@1: if (stat.getTag() == JCTree.VARDEF) {
duke@1: int adr = ((JCVariableDecl) stat).sym.adr;
duke@1: inits.excl(adr);
duke@1: uninits.incl(adr);
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitTry(JCTry tree) {
duke@1: List caughtPrev = caught;
duke@1: List thrownPrev = thrown;
duke@1: thrown = List.nil();
duke@1: for (List l = tree.catchers; l.nonEmpty(); l = l.tail)
duke@1: caught = chk.incl(l.head.param.type, caught);
duke@1: Bits uninitsTryPrev = uninitsTry;
duke@1: ListBuffer prevPendingExits = pendingExits;
duke@1: pendingExits = new ListBuffer();
duke@1: Bits initsTry = inits.dup();
duke@1: uninitsTry = uninits.dup();
duke@1: scanStat(tree.body);
duke@1: List thrownInTry = thrown;
duke@1: thrown = thrownPrev;
duke@1: caught = caughtPrev;
duke@1: boolean aliveEnd = alive;
duke@1: uninitsTry.andSet(uninits);
duke@1: Bits initsEnd = inits;
duke@1: Bits uninitsEnd = uninits;
duke@1: int nextadrCatch = nextadr;
duke@1:
duke@1: List caughtInTry = List.nil();
duke@1: for (List l = tree.catchers; l.nonEmpty(); l = l.tail) {
duke@1: alive = true;
duke@1: JCVariableDecl param = l.head.param;
duke@1: Type exc = param.type;
duke@1: if (chk.subset(exc, caughtInTry)) {
duke@1: log.error(l.head.pos(),
duke@1: "except.already.caught", exc);
duke@1: } else if (!chk.isUnchecked(l.head.pos(), exc) &&
duke@1: exc.tsym != syms.throwableType.tsym &&
duke@1: exc.tsym != syms.exceptionType.tsym &&
duke@1: !chk.intersects(exc, thrownInTry)) {
duke@1: log.error(l.head.pos(),
duke@1: "except.never.thrown.in.try", exc);
duke@1: }
duke@1: caughtInTry = chk.incl(exc, caughtInTry);
duke@1: inits = initsTry.dup();
duke@1: uninits = uninitsTry.dup();
duke@1: scan(param);
duke@1: inits.incl(param.sym.adr);
duke@1: uninits.excl(param.sym.adr);
duke@1: scanStat(l.head.body);
duke@1: initsEnd.andSet(inits);
duke@1: uninitsEnd.andSet(uninits);
duke@1: nextadr = nextadrCatch;
duke@1: aliveEnd |= alive;
duke@1: }
duke@1: if (tree.finalizer != null) {
duke@1: List savedThrown = thrown;
duke@1: thrown = List.nil();
duke@1: inits = initsTry.dup();
duke@1: uninits = uninitsTry.dup();
duke@1: ListBuffer exits = pendingExits;
duke@1: pendingExits = prevPendingExits;
duke@1: alive = true;
duke@1: scanStat(tree.finalizer);
duke@1: if (!alive) {
duke@1: // discard exits and exceptions from try and finally
duke@1: thrown = chk.union(thrown, thrownPrev);
duke@1: if (!loopPassTwo &&
duke@1: lint.isEnabled(Lint.LintCategory.FINALLY)) {
duke@1: log.warning(TreeInfo.diagEndPos(tree.finalizer),
duke@1: "finally.cannot.complete");
duke@1: }
duke@1: } else {
duke@1: thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
duke@1: thrown = chk.union(thrown, savedThrown);
duke@1: uninits.andSet(uninitsEnd);
duke@1: // FIX: this doesn't preserve source order of exits in catch
duke@1: // versus finally!
duke@1: while (exits.nonEmpty()) {
duke@1: PendingExit exit = exits.next();
duke@1: if (exit.inits != null) {
duke@1: exit.inits.orSet(inits);
duke@1: exit.uninits.andSet(uninits);
duke@1: }
duke@1: pendingExits.append(exit);
duke@1: }
duke@1: inits.orSet(initsEnd);
duke@1: alive = aliveEnd;
duke@1: }
duke@1: } else {
duke@1: thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
duke@1: inits = initsEnd;
duke@1: uninits = uninitsEnd;
duke@1: alive = aliveEnd;
duke@1: ListBuffer exits = pendingExits;
duke@1: pendingExits = prevPendingExits;
duke@1: while (exits.nonEmpty()) pendingExits.append(exits.next());
duke@1: }
duke@1: uninitsTry.andSet(uninitsTryPrev).andSet(uninits);
duke@1: }
duke@1:
duke@1: public void visitConditional(JCConditional tree) {
duke@1: scanCond(tree.cond);
duke@1: Bits initsBeforeElse = initsWhenFalse;
duke@1: Bits uninitsBeforeElse = uninitsWhenFalse;
duke@1: inits = initsWhenTrue;
duke@1: uninits = uninitsWhenTrue;
duke@1: if (tree.truepart.type.tag == BOOLEAN &&
duke@1: tree.falsepart.type.tag == BOOLEAN) {
duke@1: // if b and c are boolean valued, then
duke@1: // v is (un)assigned after a?b:c when true iff
duke@1: // v is (un)assigned after b when true and
duke@1: // v is (un)assigned after c when true
duke@1: scanCond(tree.truepart);
duke@1: Bits initsAfterThenWhenTrue = initsWhenTrue.dup();
duke@1: Bits initsAfterThenWhenFalse = initsWhenFalse.dup();
duke@1: Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup();
duke@1: Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup();
duke@1: inits = initsBeforeElse;
duke@1: uninits = uninitsBeforeElse;
duke@1: scanCond(tree.falsepart);
duke@1: initsWhenTrue.andSet(initsAfterThenWhenTrue);
duke@1: initsWhenFalse.andSet(initsAfterThenWhenFalse);
duke@1: uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue);
duke@1: uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse);
duke@1: } else {
duke@1: scanExpr(tree.truepart);
duke@1: Bits initsAfterThen = inits.dup();
duke@1: Bits uninitsAfterThen = uninits.dup();
duke@1: inits = initsBeforeElse;
duke@1: uninits = uninitsBeforeElse;
duke@1: scanExpr(tree.falsepart);
duke@1: inits.andSet(initsAfterThen);
duke@1: uninits.andSet(uninitsAfterThen);
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitIf(JCIf tree) {
duke@1: scanCond(tree.cond);
duke@1: Bits initsBeforeElse = initsWhenFalse;
duke@1: Bits uninitsBeforeElse = uninitsWhenFalse;
duke@1: inits = initsWhenTrue;
duke@1: uninits = uninitsWhenTrue;
duke@1: scanStat(tree.thenpart);
duke@1: if (tree.elsepart != null) {
duke@1: boolean aliveAfterThen = alive;
duke@1: alive = true;
duke@1: Bits initsAfterThen = inits.dup();
duke@1: Bits uninitsAfterThen = uninits.dup();
duke@1: inits = initsBeforeElse;
duke@1: uninits = uninitsBeforeElse;
duke@1: scanStat(tree.elsepart);
duke@1: inits.andSet(initsAfterThen);
duke@1: uninits.andSet(uninitsAfterThen);
duke@1: alive = alive | aliveAfterThen;
duke@1: } else {
duke@1: inits.andSet(initsBeforeElse);
duke@1: uninits.andSet(uninitsBeforeElse);
duke@1: alive = true;
duke@1: }
duke@1: }
duke@1:
duke@1:
duke@1:
duke@1: public void visitBreak(JCBreak tree) {
duke@1: recordExit(tree);
duke@1: }
duke@1:
duke@1: public void visitContinue(JCContinue tree) {
duke@1: recordExit(tree);
duke@1: }
duke@1:
duke@1: public void visitReturn(JCReturn tree) {
duke@1: scanExpr(tree.expr);
duke@1: // if not initial constructor, should markDead instead of recordExit
duke@1: recordExit(tree);
duke@1: }
duke@1:
duke@1: public void visitThrow(JCThrow tree) {
duke@1: scanExpr(tree.expr);
duke@1: markThrown(tree, tree.expr.type);
duke@1: markDead();
duke@1: }
duke@1:
duke@1: public void visitApply(JCMethodInvocation tree) {
duke@1: scanExpr(tree.meth);
duke@1: scanExprs(tree.args);
duke@1: for (List l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
duke@1: markThrown(tree, l.head);
duke@1: }
duke@1:
duke@1: public void visitNewClass(JCNewClass tree) {
duke@1: scanExpr(tree.encl);
duke@1: scanExprs(tree.args);
duke@1: // scan(tree.def);
duke@1: for (List l = tree.constructor.type.getThrownTypes();
duke@1: l.nonEmpty();
duke@1: l = l.tail)
duke@1: markThrown(tree, l.head);
duke@1: scan(tree.def);
duke@1: }
duke@1:
duke@1: public void visitNewArray(JCNewArray tree) {
duke@1: scanExprs(tree.dims);
duke@1: scanExprs(tree.elems);
duke@1: }
duke@1:
duke@1: public void visitAssert(JCAssert tree) {
duke@1: Bits initsExit = inits.dup();
duke@1: Bits uninitsExit = uninits.dup();
duke@1: scanCond(tree.cond);
duke@1: uninitsExit.andSet(uninitsWhenTrue);
duke@1: if (tree.detail != null) {
duke@1: inits = initsWhenFalse;
duke@1: uninits = uninitsWhenFalse;
duke@1: scanExpr(tree.detail);
duke@1: }
duke@1: inits = initsExit;
duke@1: uninits = uninitsExit;
duke@1: }
duke@1:
duke@1: public void visitAssign(JCAssign tree) {
duke@1: JCTree lhs = TreeInfo.skipParens(tree.lhs);
duke@1: if (!(lhs instanceof JCIdent)) scanExpr(lhs);
duke@1: scanExpr(tree.rhs);
duke@1: letInit(lhs);
duke@1: }
duke@1:
duke@1: public void visitAssignop(JCAssignOp tree) {
duke@1: scanExpr(tree.lhs);
duke@1: scanExpr(tree.rhs);
duke@1: letInit(tree.lhs);
duke@1: }
duke@1:
duke@1: public void visitUnary(JCUnary tree) {
duke@1: switch (tree.getTag()) {
duke@1: case JCTree.NOT:
duke@1: scanCond(tree.arg);
duke@1: Bits t = initsWhenFalse;
duke@1: initsWhenFalse = initsWhenTrue;
duke@1: initsWhenTrue = t;
duke@1: t = uninitsWhenFalse;
duke@1: uninitsWhenFalse = uninitsWhenTrue;
duke@1: uninitsWhenTrue = t;
duke@1: break;
duke@1: case JCTree.PREINC: case JCTree.POSTINC:
duke@1: case JCTree.PREDEC: case JCTree.POSTDEC:
duke@1: scanExpr(tree.arg);
duke@1: letInit(tree.arg);
duke@1: break;
duke@1: default:
duke@1: scanExpr(tree.arg);
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitBinary(JCBinary tree) {
duke@1: switch (tree.getTag()) {
duke@1: case JCTree.AND:
duke@1: scanCond(tree.lhs);
duke@1: Bits initsWhenFalseLeft = initsWhenFalse;
duke@1: Bits uninitsWhenFalseLeft = uninitsWhenFalse;
duke@1: inits = initsWhenTrue;
duke@1: uninits = uninitsWhenTrue;
duke@1: scanCond(tree.rhs);
duke@1: initsWhenFalse.andSet(initsWhenFalseLeft);
duke@1: uninitsWhenFalse.andSet(uninitsWhenFalseLeft);
duke@1: break;
duke@1: case JCTree.OR:
duke@1: scanCond(tree.lhs);
duke@1: Bits initsWhenTrueLeft = initsWhenTrue;
duke@1: Bits uninitsWhenTrueLeft = uninitsWhenTrue;
duke@1: inits = initsWhenFalse;
duke@1: uninits = uninitsWhenFalse;
duke@1: scanCond(tree.rhs);
duke@1: initsWhenTrue.andSet(initsWhenTrueLeft);
duke@1: uninitsWhenTrue.andSet(uninitsWhenTrueLeft);
duke@1: break;
duke@1: default:
duke@1: scanExpr(tree.lhs);
duke@1: scanExpr(tree.rhs);
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitIdent(JCIdent tree) {
duke@1: if (tree.sym.kind == VAR)
duke@1: checkInit(tree.pos(), (VarSymbol)tree.sym);
duke@1: }
duke@1:
duke@1: public void visitTypeCast(JCTypeCast tree) {
duke@1: super.visitTypeCast(tree);
duke@1: if (!tree.type.isErroneous()
duke@1: && lint.isEnabled(Lint.LintCategory.CAST)
duke@1: && types.isSameType(tree.expr.type, tree.clazz.type)) {
duke@1: log.warning(tree.pos(), "redundant.cast", tree.expr.type);
duke@1: }
duke@1: }
duke@1:
duke@1: public void visitTopLevel(JCCompilationUnit tree) {
duke@1: // Do nothing for TopLevel since each class is visited individually
duke@1: }
duke@1:
duke@1: /**************************************************************************
duke@1: * main method
duke@1: *************************************************************************/
duke@1:
duke@1: /** Perform definite assignment/unassignment analysis on a tree.
duke@1: */
duke@1: public void analyzeTree(JCTree tree, TreeMaker make) {
duke@1: try {
duke@1: this.make = make;
duke@1: inits = new Bits();
duke@1: uninits = new Bits();
duke@1: uninitsTry = new Bits();
duke@1: initsWhenTrue = initsWhenFalse =
duke@1: uninitsWhenTrue = uninitsWhenFalse = null;
duke@1: if (vars == null)
duke@1: vars = new VarSymbol[32];
duke@1: else
duke@1: for (int i=0; i();
duke@1: alive = true;
duke@1: this.thrown = this.caught = null;
duke@1: this.classDef = null;
duke@1: scan(tree);
duke@1: } finally {
duke@1: // note that recursive invocations of this method fail hard
duke@1: inits = uninits = uninitsTry = null;
duke@1: initsWhenTrue = initsWhenFalse =
duke@1: uninitsWhenTrue = uninitsWhenFalse = null;
duke@1: if (vars != null) for (int i=0; i