duke@1: /* jjg@815: * Copyright (c) 1999, 2011, 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; darcy@609: import java.util.Map; darcy@609: import java.util.LinkedHashMap; mcimadamore@550: duke@1: import com.sun.tools.javac.code.*; duke@1: import com.sun.tools.javac.tree.*; duke@1: import com.sun.tools.javac.util.*; duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; duke@1: duke@1: import com.sun.tools.javac.code.Symbol.*; duke@1: import com.sun.tools.javac.tree.JCTree.*; duke@1: duke@1: import static com.sun.tools.javac.code.Flags.*; 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: * 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: */ duke@1: public class Flow extends TreeScanner { 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@617: private Env attrEnv; duke@1: private Lint lint; mcimadamore@935: private final boolean allowImprovedRethrowAnalysis; mcimadamore@935: private final boolean allowImprovedCatchAnalysis; 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); 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@550: Source source = Source.instance(context); mcimadamore@935: allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis(); mcimadamore@935: allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis(); 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: mcimadamore@735: HashMap> preciseRethrowTypes; mcimadamore@550: 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: darcy@609: /** The list of unreferenced automatic resources. darcy@609: */ mcimadamore@905: Scope unrefdResources; darcy@609: 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()) { mcimadamore@878: if (classDef != null && mcimadamore@878: classDef.pos == exit.tree.pos) { mcimadamore@878: log.error(exit.tree.pos(), mcimadamore@878: "unreported.exception.default.constructor", mcimadamore@878: exit.thrown); mcimadamore@878: } else if (exit.tree.getTag() == JCTree.VARDEF && mcimadamore@878: ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { mcimadamore@878: log.error(exit.tree.pos(), mcimadamore@878: "unreported.exception.implicit.close", mcimadamore@878: exit.thrown, mcimadamore@878: ((JCVariableDecl)exit.tree).sym.name); mcimadamore@878: } else { mcimadamore@878: log.error(exit.tree.pos(), mcimadamore@878: "unreported.exception.need.to.catch.or.throw", mcimadamore@878: exit.thrown); mcimadamore@878: } 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)); mcimadamore@735: 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) { darcy@969: if ((sym.flags() & UNION) != 0) { //multi-catch parameter mcimadamore@550: log.error(pos, "multicatch.parameter.may.not.be.assigned", mcimadamore@550: sym); mcimadamore@550: } mcimadamore@550: else { mcimadamore@550: log.error(pos, "final.parameter.may.not.be.assigned", duke@1: sym); mcimadamore@550: } 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); mcimadamore@676: if (sym.kind == VAR) { mcimadamore@676: letInit(tree.pos(), (VarSymbol)sym); mcimadamore@676: } 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: */ mcimadamore@676: void split(boolean setToNull) { duke@1: initsWhenFalse = inits.dup(); duke@1: uninitsWhenFalse = uninits.dup(); duke@1: initsWhenTrue = inits; duke@1: uninitsWhenTrue = uninits; mcimadamore@676: if (setToNull) mcimadamore@676: 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 trees) { duke@1: if (trees != null) duke@1: for (List 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 trees) { duke@1: if (trees != null) duke@1: for (List 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); mcimadamore@676: if (inits != null) mcimadamore@676: split(tree.type != syms.unknownType); duke@1: } mcimadamore@676: if (tree.type != syms.unknownType) mcimadamore@676: 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. dlsmith@880: // Changing the throws clause on the fly is okay here because dlsmith@880: // the anonymous constructor can't be invoked anywhere else, dlsmith@880: // and its type hasn't been cached. 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); dlsmith@880: mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, 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: jjg@816: Assert.check(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) { jjg@816: Assert.check(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(); mcimadamore@906: int prevErrors = log.nerrors; duke@1: do { duke@1: Bits uninitsEntry = uninits.dup(); mcimadamore@906: uninitsEntry.excludeFrom(nextadr); duke@1: scanStat(tree.body); duke@1: alive |= resolveContinues(tree); duke@1: scanCond(tree.cond); mcimadamore@906: if (log.nerrors != prevErrors || duke@1: loopPassTwo || mcimadamore@906: uninitsEntry.dup().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(); mcimadamore@906: int prevErrors = log.nerrors; duke@1: do { duke@1: Bits uninitsEntry = uninits.dup(); mcimadamore@906: uninitsEntry.excludeFrom(nextadr); 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); mcimadamore@906: if (log.nerrors != prevErrors || duke@1: loopPassTwo || mcimadamore@906: 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.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(); mcimadamore@906: int prevErrors = log.nerrors; duke@1: do { duke@1: Bits uninitsEntry = uninits.dup(); mcimadamore@906: uninitsEntry.excludeFrom(nextadr); 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); mcimadamore@906: if (log.nerrors != prevErrors || 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(); mcimadamore@906: int prevErrors = log.nerrors; duke@1: do { duke@1: Bits uninitsEntry = uninits.dup(); mcimadamore@906: uninitsEntry.excludeFrom(nextadr); duke@1: scanStat(tree.body); duke@1: alive |= resolveContinues(tree); mcimadamore@906: if (log.nerrors != prevErrors || duke@1: loopPassTwo || mcimadamore@906: 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 = 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()) jjg@612: log.warning(Lint.LintCategory.FALLTHROUGH, jjg@612: 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(); mcimadamore@550: for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { mcimadamore@550: List subClauses = TreeInfo.isMultiCatch(l.head) ? darcy@969: ((JCTypeUnion)l.head.param.vartype).alternatives : mcimadamore@550: List.of(l.head.param.vartype); mcimadamore@550: for (JCExpression ct : subClauses) { mcimadamore@550: caught = chk.incl(ct.type, caught); mcimadamore@550: } mcimadamore@550: } mcimadamore@905: ListBuffer resourceVarDecls = ListBuffer.lb(); 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(); darcy@609: for (JCTree resource : tree.resources) { darcy@609: if (resource instanceof JCVariableDecl) { darcy@609: JCVariableDecl vdecl = (JCVariableDecl) resource; darcy@609: visitVarDef(vdecl); mcimadamore@905: unrefdResources.enter(vdecl.sym); mcimadamore@905: resourceVarDecls.append(vdecl); darcy@609: } else if (resource instanceof JCExpression) { darcy@609: scanExpr((JCExpression) resource); darcy@609: } else { darcy@609: throw new AssertionError(tree); // parser error darcy@609: } darcy@609: } darcy@609: for (JCTree resource : tree.resources) { darcy@609: List closeableSupertypes = resource.type.isCompound() ? darcy@609: types.interfaces(resource.type).prepend(types.supertype(resource.type)) : darcy@609: List.of(resource.type); darcy@609: for (Type sup : closeableSupertypes) { darcy@609: if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { mcimadamore@676: Symbol closeMethod = rs.resolveQualifiedMethod(tree, mcimadamore@617: attrEnv, mcimadamore@617: sup, mcimadamore@617: names.close, mcimadamore@617: List.nil(), mcimadamore@617: List.nil()); mcimadamore@617: if (closeMethod.kind == MTH) { mcimadamore@617: for (Type t : ((MethodSymbol)closeMethod).getThrownTypes()) { mcimadamore@878: markThrown(resource, t); mcimadamore@617: } darcy@609: } darcy@609: } darcy@609: } darcy@609: } duke@1: scanStat(tree.body); mcimadamore@935: List thrownInTry = allowImprovedCatchAnalysis ? mcimadamore@935: chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) : mcimadamore@935: 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: mcimadamore@905: if (!resourceVarDecls.isEmpty() && mcimadamore@743: lint.isEnabled(Lint.LintCategory.TRY)) { mcimadamore@905: for (JCVariableDecl resVar : resourceVarDecls) { mcimadamore@905: if (unrefdResources.includes(resVar.sym)) { mcimadamore@905: log.warning(Lint.LintCategory.TRY, resVar.pos(), mcimadamore@905: "try.resource.not.referenced", resVar.sym); mcimadamore@905: unrefdResources.remove(resVar.sym); mcimadamore@905: } darcy@609: } darcy@609: } darcy@609: 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; mcimadamore@550: List subClauses = TreeInfo.isMultiCatch(l.head) ? darcy@969: ((JCTypeUnion)l.head.param.vartype).alternatives : mcimadamore@550: List.of(l.head.param.vartype); mcimadamore@550: List ctypes = List.nil(); mcimadamore@550: List rethrownTypes = chk.diff(thrownInTry, caughtInTry); mcimadamore@550: for (JCExpression ct : subClauses) { mcimadamore@550: Type exc = ct.type; mcimadamore@676: if (exc != syms.unknownType) { mcimadamore@676: ctypes = ctypes.append(exc); mcimadamore@676: if (types.isSameType(exc, syms.objectType)) mcimadamore@676: continue; mcimadamore@935: checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry); mcimadamore@676: caughtInTry = chk.incl(exc, caughtInTry); mcimadamore@550: } duke@1: } 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); mcimadamore@735: preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); duke@1: scanStat(l.head.body); duke@1: initsEnd.andSet(inits); duke@1: uninitsEnd.andSet(uninits); duke@1: nextadr = nextadrCatch; mcimadamore@735: preciseRethrowTypes.remove(param.sym); 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)) { jjg@612: log.warning(Lint.LintCategory.FINALLY, jjg@612: TreeInfo.diagEndPos(tree.finalizer), jjg@612: "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: mcimadamore@935: void checkCaughtType(DiagnosticPosition pos, Type exc, List thrownInTry, List caughtInTry) { mcimadamore@935: if (chk.subset(exc, caughtInTry)) { mcimadamore@935: log.error(pos, "except.already.caught", exc); mcimadamore@935: } else if (!chk.isUnchecked(pos, exc) && mcimadamore@935: exc.tsym != syms.throwableType.tsym && mcimadamore@935: exc.tsym != syms.exceptionType.tsym && mcimadamore@935: !chk.intersects(exc, thrownInTry)) { mcimadamore@935: log.error(pos, "except.never.thrown.in.try", exc); mcimadamore@935: } else if (allowImprovedCatchAnalysis) { mcimadamore@935: List catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); mcimadamore@935: // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an mcimadamore@935: // unchecked exception, the result list would not be empty, as the augmented mcimadamore@935: // thrown set includes { RuntimeException, Error }; if 'exc' was a checked mcimadamore@935: // exception, that would have been covered in the branch above mcimadamore@935: if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty()) { mcimadamore@935: String key = catchableThrownTypes.length() == 1 ? mcimadamore@935: "unreachable.catch" : mcimadamore@935: "unreachable.catch.1"; mcimadamore@935: log.warning(pos, key, catchableThrownTypes); mcimadamore@935: } mcimadamore@935: } mcimadamore@935: } mcimadamore@935: 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); mcimadamore@550: Symbol sym = TreeInfo.symbol(tree.expr); mcimadamore@550: if (sym != null && mcimadamore@550: sym.kind == VAR && mcimadamore@735: (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && mcimadamore@735: preciseRethrowTypes.get(sym) != null && mcimadamore@935: allowImprovedRethrowAnalysis) { mcimadamore@735: for (Type t : preciseRethrowTypes.get(sym)) { mcimadamore@550: markThrown(tree, t); mcimadamore@550: } mcimadamore@550: } mcimadamore@550: else { mcimadamore@550: markThrown(tree, tree.expr.type); mcimadamore@550: } 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); mcimadamore@186: for (List l = tree.constructorType.getThrownTypes(); duke@1: l.nonEmpty(); mcimadamore@186: l = l.tail) { duke@1: markThrown(tree, l.head); mcimadamore@186: } mcimadamore@186: List caughtPrev = caught; mcimadamore@186: try { mcimadamore@186: // If the new class expression defines an anonymous class, mcimadamore@186: // analysis of the anonymous constructor may encounter thrown mcimadamore@186: // types which are unsubstituted type variables. mcimadamore@186: // However, since the constructor's actual thrown types have mcimadamore@186: // already been marked as thrown, it is safe to simply include mcimadamore@186: // each of the constructor's formal thrown types in the set of mcimadamore@186: // 'caught/declared to be thrown' types, for the duration of mcimadamore@186: // the class def analysis. mcimadamore@186: if (tree.def != null) mcimadamore@186: for (List l = tree.constructor.type.getThrownTypes(); mcimadamore@186: l.nonEmpty(); mcimadamore@186: l = l.tail) { mcimadamore@186: caught = chk.incl(l.head, caught); mcimadamore@186: } mcimadamore@186: scan(tree.def); mcimadamore@186: } mcimadamore@186: finally { mcimadamore@186: caught = caughtPrev; mcimadamore@186: } 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) { darcy@609: if (tree.sym.kind == VAR) { duke@1: checkInit(tree.pos(), (VarSymbol)tree.sym); darcy@609: referenced(tree.sym); darcy@609: } darcy@609: } darcy@609: darcy@609: void referenced(Symbol sym) { mcimadamore@905: unrefdResources.remove(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) jjg@308: && types.isSameType(tree.expr.type, tree.clazz.type) mcimadamore@742: && !is292targetTypeCast(tree)) { jjg@612: log.warning(Lint.LintCategory.CAST, jjg@612: tree.pos(), "redundant.cast", tree.expr.type); duke@1: } duke@1: } mcimadamore@742: //where mcimadamore@742: private boolean is292targetTypeCast(JCTypeCast tree) { mcimadamore@742: boolean is292targetTypeCast = false; mcimadamore@820: JCExpression expr = TreeInfo.skipParens(tree.expr); mcimadamore@820: if (expr.getTag() == JCTree.APPLY) { mcimadamore@820: JCMethodInvocation apply = (JCMethodInvocation)expr; mcimadamore@742: Symbol sym = TreeInfo.symbol(apply.meth); mcimadamore@742: is292targetTypeCast = sym != null && mcimadamore@742: sym.kind == MTH && mcimadamore@742: (sym.flags() & POLYMORPHIC_SIGNATURE) != 0; mcimadamore@742: } mcimadamore@742: return is292targetTypeCast; mcimadamore@742: } 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: */ mcimadamore@617: public void analyzeTree(Env env, TreeMaker make) { duke@1: try { mcimadamore@617: attrEnv = env; mcimadamore@617: JCTree tree = env.tree; 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(); mcimadamore@735: preciseRethrowTypes = new HashMap>(); duke@1: alive = true; duke@1: this.thrown = this.caught = null; duke@1: this.classDef = null; mcimadamore@905: unrefdResources = new Scope(env.enclClass.sym); 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