0N/A/*
1425N/A * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
553N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
553N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
553N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
553N/A * or visit www.oracle.com if you need additional information or have any
553N/A * questions.
0N/A */
0N/A
0N/A//todo: one might eliminate uninits.andSets when monotonic
0N/A
0N/Apackage com.sun.tools.javac.comp;
0N/A
549N/Aimport java.util.HashMap;
608N/Aimport java.util.Map;
608N/Aimport java.util.LinkedHashMap;
549N/A
0N/Aimport com.sun.tools.javac.code.*;
0N/Aimport com.sun.tools.javac.tree.*;
0N/Aimport com.sun.tools.javac.util.*;
0N/Aimport com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
0N/A
0N/Aimport com.sun.tools.javac.code.Symbol.*;
0N/Aimport com.sun.tools.javac.tree.JCTree.*;
0N/A
0N/Aimport static com.sun.tools.javac.code.Flags.*;
0N/Aimport static com.sun.tools.javac.code.Kinds.*;
0N/Aimport static com.sun.tools.javac.code.TypeTags.*;
0N/A
0N/A/** This pass implements dataflow analysis for Java programs.
0N/A * Liveness analysis checks that every statement is reachable.
0N/A * Exception analysis ensures that every checked exception that is
0N/A * thrown is declared or caught. Definite assignment analysis
0N/A * ensures that each variable is assigned when used. Definite
0N/A * unassignment analysis ensures that no final variable is assigned
0N/A * more than once.
0N/A *
971N/A * <p>The JLS has a number of problems in the
0N/A * specification of these flow analysis problems. This implementation
0N/A * attempts to address those issues.
0N/A *
0N/A * <p>First, there is no accommodation for a finally clause that cannot
0N/A * complete normally. For liveness analysis, an intervening finally
0N/A * clause can cause a break, continue, or return not to reach its
0N/A * target. For exception analysis, an intervening finally clause can
0N/A * cause any exception to be "caught". For DA/DU analysis, the finally
0N/A * clause can prevent a transfer of control from propagating DA/DU
0N/A * state to the target. In addition, code in the finally clause can
0N/A * affect the DA/DU status of variables.
0N/A *
0N/A * <p>For try statements, we introduce the idea of a variable being
0N/A * definitely unassigned "everywhere" in a block. A variable V is
0N/A * "unassigned everywhere" in a block iff it is unassigned at the
0N/A * beginning of the block and there is no reachable assignment to V
0N/A * in the block. An assignment V=e is reachable iff V is not DA
0N/A * after e. Then we can say that V is DU at the beginning of the
0N/A * catch block iff V is DU everywhere in the try block. Similarly, V
0N/A * is DU at the beginning of the finally block iff V is DU everywhere
0N/A * in the try block and in every catch block. Specifically, the
0N/A * following bullet is added to 16.2.2
0N/A * <pre>
0N/A * V is <em>unassigned everywhere</em> in a block if it is
0N/A * unassigned before the block and there is no reachable
0N/A * assignment to V within the block.
0N/A * </pre>
0N/A * <p>In 16.2.15, the third bullet (and all of its sub-bullets) for all
0N/A * try blocks is changed to
0N/A * <pre>
0N/A * V is definitely unassigned before a catch block iff V is
0N/A * definitely unassigned everywhere in the try block.
0N/A * </pre>
0N/A * <p>The last bullet (and all of its sub-bullets) for try blocks that
0N/A * have a finally block is changed to
0N/A * <pre>
0N/A * V is definitely unassigned before the finally block iff
0N/A * V is definitely unassigned everywhere in the try block
0N/A * and everywhere in each catch block of the try statement.
0N/A * </pre>
0N/A * <p>In addition,
0N/A * <pre>
0N/A * V is definitely assigned at the end of a constructor iff
0N/A * V is definitely assigned after the block that is the body
0N/A * of the constructor and V is definitely assigned at every
0N/A * return that can return from the constructor.
0N/A * </pre>
0N/A * <p>In addition, each continue statement with the loop as its target
0N/A * is treated as a jump to the end of the loop body, and "intervening"
0N/A * finally clauses are treated as follows: V is DA "due to the
0N/A * continue" iff V is DA before the continue statement or V is DA at
0N/A * the end of any intervening finally block. V is DU "due to the
0N/A * continue" iff any intervening finally cannot complete normally or V
0N/A * is DU at the end of every intervening finally block. This "due to
0N/A * the continue" concept is then used in the spec for the loops.
0N/A *
0N/A * <p>Similarly, break statements must consider intervening finally
0N/A * blocks. For liveness analysis, a break statement for which any
0N/A * intervening finally cannot complete normally is not considered to
0N/A * cause the target statement to be able to complete normally. Then
0N/A * we say V is DA "due to the break" iff V is DA before the break or
0N/A * V is DA at the end of any intervening finally block. V is DU "due
0N/A * to the break" iff any intervening finally cannot complete normally
0N/A * or V is DU at the break and at the end of every intervening
0N/A * finally block. (I suspect this latter condition can be
0N/A * simplified.) This "due to the break" is then used in the spec for
0N/A * all statements that can be "broken".
0N/A *
0N/A * <p>The return statement is treated similarly. V is DA "due to a
0N/A * return statement" iff V is DA before the return statement or V is
0N/A * DA at the end of any intervening finally block. Note that we
0N/A * don't have to worry about the return expression because this
0N/A * concept is only used for construcrors.
0N/A *
971N/A * <p>There is no spec in the JLS for when a variable is definitely
0N/A * assigned at the end of a constructor, which is needed for final
0N/A * fields (8.3.1.2). We implement the rule that V is DA at the end
0N/A * of the constructor iff it is DA and the end of the body of the
0N/A * constructor and V is DA "due to" every return of the constructor.
0N/A *
0N/A * <p>Intervening finally blocks similarly affect exception analysis. An
0N/A * intervening finally that cannot complete normally allows us to ignore
0N/A * an otherwise uncaught exception.
0N/A *
0N/A * <p>To implement the semantics of intervening finally clauses, all
0N/A * nonlocal transfers (break, continue, return, throw, method call that
0N/A * can throw a checked exception, and a constructor invocation that can
0N/A * thrown a checked exception) are recorded in a queue, and removed
0N/A * from the queue when we complete processing the target of the
0N/A * nonlocal transfer. This allows us to modify the queue in accordance
0N/A * with the above rules when we encounter a finally clause. The only
0N/A * exception to this [no pun intended] is that checked exceptions that
0N/A * are known to be caught or declared to be caught in the enclosing
0N/A * method are not recorded in the queue, but instead are recorded in a
0N/A * global variable "Set<Type> thrown" that records the type of all
0N/A * exceptions that can be thrown.
0N/A *
0N/A * <p>Other minor issues the treatment of members of other classes
0N/A * (always considered DA except that within an anonymous class
0N/A * constructor, where DA status from the enclosing scope is
0N/A * preserved), treatment of the case expression (V is DA before the
0N/A * case expression iff V is DA after the switch expression),
0N/A * treatment of variables declared in a switch block (the implied
0N/A * DA/DU status after the switch expression is DU and not DA for
0N/A * variables defined in a switch block), the treatment of boolean ?:
0N/A * expressions (The JLS rules only handle b and c non-boolean; the
0N/A * new rule is that if b and c are boolean valued, then V is
0N/A * (un)assigned after a?b:c when true/false iff V is (un)assigned
0N/A * after b when true/false and V is (un)assigned after c when
0N/A * true/false).
0N/A *
0N/A * <p>There is the remaining question of what syntactic forms constitute a
0N/A * reference to a variable. It is conventional to allow this.x on the
0N/A * left-hand-side to initialize a final instance field named x, yet
0N/A * this.x isn't considered a "use" when appearing on a right-hand-side
0N/A * in most implementations. Should parentheses affect what is
0N/A * considered a variable reference? The simplest rule would be to
0N/A * allow unqualified forms only, parentheses optional, and phase out
0N/A * support for assigning to a final field via this.x.
0N/A *
580N/A * <p><b>This is NOT part of any supported API.
580N/A * If you write code that depends on this, you do so at your own risk.
0N/A * This code and its internal interfaces are subject to change or
0N/A * deletion without notice.</b>
0N/A */
0N/Apublic class Flow extends TreeScanner {
0N/A protected static final Context.Key<Flow> flowKey =
0N/A new Context.Key<Flow>();
0N/A
112N/A private final Names names;
0N/A private final Log log;
0N/A private final Symtab syms;
0N/A private final Types types;
0N/A private final Check chk;
0N/A private TreeMaker make;
616N/A private final Resolve rs;
616N/A private Env<AttrContext> attrEnv;
0N/A private Lint lint;
934N/A private final boolean allowImprovedRethrowAnalysis;
934N/A private final boolean allowImprovedCatchAnalysis;
0N/A
0N/A public static Flow instance(Context context) {
0N/A Flow instance = context.get(flowKey);
0N/A if (instance == null)
0N/A instance = new Flow(context);
0N/A return instance;
0N/A }
0N/A
0N/A protected Flow(Context context) {
0N/A context.put(flowKey, this);
112N/A names = Names.instance(context);
0N/A log = Log.instance(context);
0N/A syms = Symtab.instance(context);
0N/A types = Types.instance(context);
0N/A chk = Check.instance(context);
0N/A lint = Lint.instance(context);
616N/A rs = Resolve.instance(context);
549N/A Source source = Source.instance(context);
934N/A allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis();
934N/A allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis();
0N/A }
0N/A
0N/A /** A flag that indicates whether the last statement could
0N/A * complete normally.
0N/A */
0N/A private boolean alive;
0N/A
0N/A /** The set of definitely assigned variables.
0N/A */
0N/A Bits inits;
0N/A
0N/A /** The set of definitely unassigned variables.
0N/A */
0N/A Bits uninits;
0N/A
734N/A HashMap<Symbol, List<Type>> preciseRethrowTypes;
549N/A
0N/A /** The set of variables that are definitely unassigned everywhere
0N/A * in current try block. This variable is maintained lazily; it is
0N/A * updated only when something gets removed from uninits,
0N/A * typically by being assigned in reachable code. To obtain the
0N/A * correct set of variables which are definitely unassigned
0N/A * anywhere in current try block, intersect uninitsTry and
0N/A * uninits.
0N/A */
0N/A Bits uninitsTry;
0N/A
0N/A /** When analyzing a condition, inits and uninits are null.
0N/A * Instead we have:
0N/A */
0N/A Bits initsWhenTrue;
0N/A Bits initsWhenFalse;
0N/A Bits uninitsWhenTrue;
0N/A Bits uninitsWhenFalse;
0N/A
0N/A /** A mapping from addresses to variable symbols.
0N/A */
0N/A VarSymbol[] vars;
0N/A
0N/A /** The current class being defined.
0N/A */
0N/A JCClassDecl classDef;
0N/A
0N/A /** The first variable sequence number in this class definition.
0N/A */
0N/A int firstadr;
0N/A
0N/A /** The next available variable sequence number.
0N/A */
0N/A int nextadr;
0N/A
0N/A /** The list of possibly thrown declarable exceptions.
0N/A */
0N/A List<Type> thrown;
0N/A
0N/A /** The list of exceptions that are either caught or declared to be
0N/A * thrown.
0N/A */
0N/A List<Type> caught;
0N/A
608N/A /** The list of unreferenced automatic resources.
608N/A */
904N/A Scope unrefdResources;
608N/A
0N/A /** Set when processing a loop body the second time for DU analysis. */
0N/A boolean loopPassTwo = false;
0N/A
0N/A /*-------------------- Environments ----------------------*/
0N/A
0N/A /** A pending exit. These are the statements return, break, and
0N/A * continue. In addition, exception-throwing expressions or
0N/A * statements are put here when not known to be caught. This
0N/A * will typically result in an error unless it is within a
0N/A * try-finally whose finally block cannot complete normally.
0N/A */
0N/A static class PendingExit {
0N/A JCTree tree;
0N/A Bits inits;
0N/A Bits uninits;
0N/A Type thrown;
0N/A PendingExit(JCTree tree, Bits inits, Bits uninits) {
0N/A this.tree = tree;
0N/A this.inits = inits.dup();
0N/A this.uninits = uninits.dup();
0N/A }
0N/A PendingExit(JCTree tree, Type thrown) {
0N/A this.tree = tree;
0N/A this.thrown = thrown;
0N/A }
0N/A }
0N/A
0N/A /** The currently pending exits that go from current inner blocks
0N/A * to an enclosing block, in source order.
0N/A */
0N/A ListBuffer<PendingExit> pendingExits;
0N/A
0N/A /*-------------------- Exceptions ----------------------*/
0N/A
0N/A /** Complain that pending exceptions are not caught.
0N/A */
0N/A void errorUncaught() {
0N/A for (PendingExit exit = pendingExits.next();
0N/A exit != null;
0N/A exit = pendingExits.next()) {
877N/A if (classDef != null &&
877N/A classDef.pos == exit.tree.pos) {
877N/A log.error(exit.tree.pos(),
877N/A "unreported.exception.default.constructor",
877N/A exit.thrown);
877N/A } else if (exit.tree.getTag() == JCTree.VARDEF &&
877N/A ((JCVariableDecl)exit.tree).sym.isResourceVariable()) {
877N/A log.error(exit.tree.pos(),
877N/A "unreported.exception.implicit.close",
877N/A exit.thrown,
877N/A ((JCVariableDecl)exit.tree).sym.name);
877N/A } else {
877N/A log.error(exit.tree.pos(),
877N/A "unreported.exception.need.to.catch.or.throw",
877N/A exit.thrown);
877N/A }
0N/A }
0N/A }
0N/A
0N/A /** Record that exception is potentially thrown and check that it
0N/A * is caught.
0N/A */
0N/A void markThrown(JCTree tree, Type exc) {
0N/A if (!chk.isUnchecked(tree.pos(), exc)) {
1425N/A if (!chk.isHandled(exc, caught)) {
0N/A pendingExits.append(new PendingExit(tree, exc));
1425N/A }
1425N/A thrown = chk.incl(exc, thrown);
0N/A }
0N/A }
0N/A
0N/A /*-------------- Processing variables ----------------------*/
0N/A
0N/A /** Do we need to track init/uninit state of this symbol?
0N/A * I.e. is symbol either a local or a blank final variable?
0N/A */
0N/A boolean trackable(VarSymbol sym) {
0N/A return
0N/A (sym.owner.kind == MTH ||
0N/A ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
0N/A classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)));
0N/A }
0N/A
0N/A /** Initialize new trackable variable by setting its address field
0N/A * to the next available sequence number and entering it under that
0N/A * index into the vars array.
0N/A */
0N/A void newVar(VarSymbol sym) {
0N/A if (nextadr == vars.length) {
0N/A VarSymbol[] newvars = new VarSymbol[nextadr * 2];
0N/A System.arraycopy(vars, 0, newvars, 0, nextadr);
0N/A vars = newvars;
0N/A }
0N/A sym.adr = nextadr;
0N/A vars[nextadr] = sym;
0N/A inits.excl(nextadr);
0N/A uninits.incl(nextadr);
0N/A nextadr++;
0N/A }
0N/A
0N/A /** Record an initialization of a trackable variable.
0N/A */
0N/A void letInit(DiagnosticPosition pos, VarSymbol sym) {
0N/A if (sym.adr >= firstadr && trackable(sym)) {
0N/A if ((sym.flags() & FINAL) != 0) {
0N/A if ((sym.flags() & PARAMETER) != 0) {
968N/A if ((sym.flags() & UNION) != 0) { //multi-catch parameter
549N/A log.error(pos, "multicatch.parameter.may.not.be.assigned",
549N/A sym);
549N/A }
549N/A else {
549N/A log.error(pos, "final.parameter.may.not.be.assigned",
0N/A sym);
549N/A }
0N/A } else if (!uninits.isMember(sym.adr)) {
0N/A log.error(pos,
0N/A loopPassTwo
0N/A ? "var.might.be.assigned.in.loop"
0N/A : "var.might.already.be.assigned",
0N/A sym);
0N/A } else if (!inits.isMember(sym.adr)) {
0N/A // reachable assignment
0N/A uninits.excl(sym.adr);
0N/A uninitsTry.excl(sym.adr);
0N/A } else {
0N/A //log.rawWarning(pos, "unreachable assignment");//DEBUG
0N/A uninits.excl(sym.adr);
0N/A }
0N/A }
0N/A inits.incl(sym.adr);
0N/A } else if ((sym.flags() & FINAL) != 0) {
0N/A log.error(pos, "var.might.already.be.assigned", sym);
0N/A }
0N/A }
0N/A
0N/A /** If tree is either a simple name or of the form this.name or
0N/A * C.this.name, and tree represents a trackable variable,
0N/A * record an initialization of the variable.
0N/A */
0N/A void letInit(JCTree tree) {
0N/A tree = TreeInfo.skipParens(tree);
0N/A if (tree.getTag() == JCTree.IDENT || tree.getTag() == JCTree.SELECT) {
0N/A Symbol sym = TreeInfo.symbol(tree);
675N/A if (sym.kind == VAR) {
675N/A letInit(tree.pos(), (VarSymbol)sym);
675N/A }
0N/A }
0N/A }
0N/A
0N/A /** Check that trackable variable is initialized.
0N/A */
0N/A void checkInit(DiagnosticPosition pos, VarSymbol sym) {
0N/A if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
0N/A trackable(sym) &&
0N/A !inits.isMember(sym.adr)) {
0N/A log.error(pos, "var.might.not.have.been.initialized",
0N/A sym);
0N/A inits.incl(sym.adr);
0N/A }
0N/A }
0N/A
0N/A /*-------------------- Handling jumps ----------------------*/
0N/A
0N/A /** Record an outward transfer of control. */
0N/A void recordExit(JCTree tree) {
0N/A pendingExits.append(new PendingExit(tree, inits, uninits));
0N/A markDead();
0N/A }
0N/A
0N/A /** Resolve all breaks of this statement. */
0N/A boolean resolveBreaks(JCTree tree,
0N/A ListBuffer<PendingExit> oldPendingExits) {
0N/A boolean result = false;
0N/A List<PendingExit> exits = pendingExits.toList();
0N/A pendingExits = oldPendingExits;
0N/A for (; exits.nonEmpty(); exits = exits.tail) {
0N/A PendingExit exit = exits.head;
0N/A if (exit.tree.getTag() == JCTree.BREAK &&
0N/A ((JCBreak) exit.tree).target == tree) {
0N/A inits.andSet(exit.inits);
0N/A uninits.andSet(exit.uninits);
0N/A result = true;
0N/A } else {
0N/A pendingExits.append(exit);
0N/A }
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A /** Resolve all continues of this statement. */
0N/A boolean resolveContinues(JCTree tree) {
0N/A boolean result = false;
0N/A List<PendingExit> exits = pendingExits.toList();
0N/A pendingExits = new ListBuffer<PendingExit>();
0N/A for (; exits.nonEmpty(); exits = exits.tail) {
0N/A PendingExit exit = exits.head;
0N/A if (exit.tree.getTag() == JCTree.CONTINUE &&
0N/A ((JCContinue) exit.tree).target == tree) {
0N/A inits.andSet(exit.inits);
0N/A uninits.andSet(exit.uninits);
0N/A result = true;
0N/A } else {
0N/A pendingExits.append(exit);
0N/A }
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A /** Record that statement is unreachable.
0N/A */
0N/A void markDead() {
0N/A inits.inclRange(firstadr, nextadr);
0N/A uninits.inclRange(firstadr, nextadr);
0N/A alive = false;
0N/A }
0N/A
0N/A /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets
0N/A */
675N/A void split(boolean setToNull) {
0N/A initsWhenFalse = inits.dup();
0N/A uninitsWhenFalse = uninits.dup();
0N/A initsWhenTrue = inits;
0N/A uninitsWhenTrue = uninits;
675N/A if (setToNull)
675N/A inits = uninits = null;
0N/A }
0N/A
0N/A /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets.
0N/A */
0N/A void merge() {
0N/A inits = initsWhenFalse.andSet(initsWhenTrue);
0N/A uninits = uninitsWhenFalse.andSet(uninitsWhenTrue);
0N/A }
0N/A
0N/A/* ************************************************************************
0N/A * Visitor methods for statements and definitions
0N/A *************************************************************************/
0N/A
0N/A /** Analyze a definition.
0N/A */
0N/A void scanDef(JCTree tree) {
0N/A scanStat(tree);
0N/A if (tree != null && tree.getTag() == JCTree.BLOCK && !alive) {
0N/A log.error(tree.pos(),
0N/A "initializer.must.be.able.to.complete.normally");
0N/A }
0N/A }
0N/A
0N/A /** Analyze a statement. Check that statement is reachable.
0N/A */
0N/A void scanStat(JCTree tree) {
0N/A if (!alive && tree != null) {
0N/A log.error(tree.pos(), "unreachable.stmt");
0N/A if (tree.getTag() != JCTree.SKIP) alive = true;
0N/A }
0N/A scan(tree);
0N/A }
0N/A
0N/A /** Analyze list of statements.
0N/A */
0N/A void scanStats(List<? extends JCStatement> trees) {
0N/A if (trees != null)
0N/A for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
0N/A scanStat(l.head);
0N/A }
0N/A
0N/A /** Analyze an expression. Make sure to set (un)inits rather than
0N/A * (un)initsWhenTrue(WhenFalse) on exit.
0N/A */
0N/A void scanExpr(JCTree tree) {
0N/A if (tree != null) {
0N/A scan(tree);
0N/A if (inits == null) merge();
0N/A }
0N/A }
0N/A
0N/A /** Analyze a list of expressions.
0N/A */
0N/A void scanExprs(List<? extends JCExpression> trees) {
0N/A if (trees != null)
0N/A for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail)
0N/A scanExpr(l.head);
0N/A }
0N/A
0N/A /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
0N/A * rather than (un)inits on exit.
0N/A */
0N/A void scanCond(JCTree tree) {
0N/A if (tree.type.isFalse()) {
0N/A if (inits == null) merge();
0N/A initsWhenTrue = inits.dup();
0N/A initsWhenTrue.inclRange(firstadr, nextadr);
0N/A uninitsWhenTrue = uninits.dup();
0N/A uninitsWhenTrue.inclRange(firstadr, nextadr);
0N/A initsWhenFalse = inits;
0N/A uninitsWhenFalse = uninits;
0N/A } else if (tree.type.isTrue()) {
0N/A if (inits == null) merge();
0N/A initsWhenFalse = inits.dup();
0N/A initsWhenFalse.inclRange(firstadr, nextadr);
0N/A uninitsWhenFalse = uninits.dup();
0N/A uninitsWhenFalse.inclRange(firstadr, nextadr);
0N/A initsWhenTrue = inits;
0N/A uninitsWhenTrue = uninits;
0N/A } else {
0N/A scan(tree);
675N/A if (inits != null)
675N/A split(tree.type != syms.unknownType);
0N/A }
675N/A if (tree.type != syms.unknownType)
675N/A inits = uninits = null;
0N/A }
0N/A
0N/A /* ------------ Visitor methods for various sorts of trees -------------*/
0N/A
0N/A public void visitClassDef(JCClassDecl tree) {
0N/A if (tree.sym == null) return;
0N/A
0N/A JCClassDecl classDefPrev = classDef;
0N/A List<Type> thrownPrev = thrown;
0N/A List<Type> caughtPrev = caught;
0N/A boolean alivePrev = alive;
0N/A int firstadrPrev = firstadr;
0N/A int nextadrPrev = nextadr;
0N/A ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
0N/A Lint lintPrev = lint;
0N/A
0N/A pendingExits = new ListBuffer<PendingExit>();
0N/A if (tree.name != names.empty) {
0N/A caught = List.nil();
0N/A firstadr = nextadr;
0N/A }
0N/A classDef = tree;
0N/A thrown = List.nil();
0N/A lint = lint.augment(tree.sym.attributes_field);
0N/A
0N/A try {
0N/A // define all the static fields
0N/A for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
0N/A if (l.head.getTag() == JCTree.VARDEF) {
0N/A JCVariableDecl def = (JCVariableDecl)l.head;
0N/A if ((def.mods.flags & STATIC) != 0) {
0N/A VarSymbol sym = def.sym;
0N/A if (trackable(sym))
0N/A newVar(sym);
0N/A }
0N/A }
0N/A }
0N/A
0N/A // process all the static initializers
0N/A for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
0N/A if (l.head.getTag() != JCTree.METHODDEF &&
0N/A (TreeInfo.flags(l.head) & STATIC) != 0) {
0N/A scanDef(l.head);
0N/A errorUncaught();
0N/A }
0N/A }
0N/A
0N/A // add intersection of all thrown clauses of initial constructors
0N/A // to set of caught exceptions, unless class is anonymous.
0N/A if (tree.name != names.empty) {
0N/A boolean firstConstructor = true;
0N/A for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
0N/A if (TreeInfo.isInitialConstructor(l.head)) {
0N/A List<Type> mthrown =
0N/A ((JCMethodDecl) l.head).sym.type.getThrownTypes();
0N/A if (firstConstructor) {
0N/A caught = mthrown;
0N/A firstConstructor = false;
0N/A } else {
0N/A caught = chk.intersect(mthrown, caught);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // define all the instance fields
0N/A for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
0N/A if (l.head.getTag() == JCTree.VARDEF) {
0N/A JCVariableDecl def = (JCVariableDecl)l.head;
0N/A if ((def.mods.flags & STATIC) == 0) {
0N/A VarSymbol sym = def.sym;
0N/A if (trackable(sym))
0N/A newVar(sym);
0N/A }
0N/A }
0N/A }
0N/A
0N/A // process all the instance initializers
0N/A for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
0N/A if (l.head.getTag() != JCTree.METHODDEF &&
0N/A (TreeInfo.flags(l.head) & STATIC) == 0) {
0N/A scanDef(l.head);
0N/A errorUncaught();
0N/A }
0N/A }
0N/A
0N/A // in an anonymous class, add the set of thrown exceptions to
0N/A // the throws clause of the synthetic constructor and propagate
0N/A // outwards.
879N/A // Changing the throws clause on the fly is okay here because
879N/A // the anonymous constructor can't be invoked anywhere else,
879N/A // and its type hasn't been cached.
0N/A if (tree.name == names.empty) {
0N/A for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
0N/A if (TreeInfo.isInitialConstructor(l.head)) {
0N/A JCMethodDecl mdef = (JCMethodDecl)l.head;
0N/A mdef.thrown = make.Types(thrown);
879N/A mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown);
0N/A }
0N/A }
0N/A thrownPrev = chk.union(thrown, thrownPrev);
0N/A }
0N/A
0N/A // process all the methods
0N/A for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
0N/A if (l.head.getTag() == JCTree.METHODDEF) {
0N/A scan(l.head);
0N/A errorUncaught();
0N/A }
0N/A }
0N/A
0N/A thrown = thrownPrev;
0N/A } finally {
0N/A pendingExits = pendingExitsPrev;
0N/A alive = alivePrev;
0N/A nextadr = nextadrPrev;
0N/A firstadr = firstadrPrev;
0N/A caught = caughtPrev;
0N/A classDef = classDefPrev;
0N/A lint = lintPrev;
0N/A }
0N/A }
0N/A
0N/A public void visitMethodDef(JCMethodDecl tree) {
0N/A if (tree.body == null) return;
0N/A
0N/A List<Type> caughtPrev = caught;
0N/A List<Type> mthrown = tree.sym.type.getThrownTypes();
0N/A Bits initsPrev = inits.dup();
0N/A Bits uninitsPrev = uninits.dup();
0N/A int nextadrPrev = nextadr;
0N/A int firstadrPrev = firstadr;
0N/A Lint lintPrev = lint;
0N/A
0N/A lint = lint.augment(tree.sym.attributes_field);
0N/A
815N/A Assert.check(pendingExits.isEmpty());
0N/A
0N/A try {
0N/A boolean isInitialConstructor =
0N/A TreeInfo.isInitialConstructor(tree);
0N/A
0N/A if (!isInitialConstructor)
0N/A firstadr = nextadr;
0N/A for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
0N/A JCVariableDecl def = l.head;
0N/A scan(def);
0N/A inits.incl(def.sym.adr);
0N/A uninits.excl(def.sym.adr);
0N/A }
0N/A if (isInitialConstructor)
0N/A caught = chk.union(caught, mthrown);
0N/A else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK)
0N/A caught = mthrown;
0N/A // else we are in an instance initializer block;
0N/A // leave caught unchanged.
0N/A
0N/A alive = true;
0N/A scanStat(tree.body);
0N/A
0N/A if (alive && tree.sym.type.getReturnType().tag != VOID)
0N/A log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
0N/A
0N/A if (isInitialConstructor) {
0N/A for (int i = firstadr; i < nextadr; i++)
0N/A if (vars[i].owner == classDef.sym)
0N/A checkInit(TreeInfo.diagEndPos(tree.body), vars[i]);
0N/A }
0N/A List<PendingExit> exits = pendingExits.toList();
0N/A pendingExits = new ListBuffer<PendingExit>();
0N/A while (exits.nonEmpty()) {
0N/A PendingExit exit = exits.head;
0N/A exits = exits.tail;
0N/A if (exit.thrown == null) {
815N/A Assert.check(exit.tree.getTag() == JCTree.RETURN);
0N/A if (isInitialConstructor) {
0N/A inits = exit.inits;
0N/A for (int i = firstadr; i < nextadr; i++)
0N/A checkInit(exit.tree.pos(), vars[i]);
0N/A }
0N/A } else {
0N/A // uncaught throws will be reported later
0N/A pendingExits.append(exit);
0N/A }
0N/A }
0N/A } finally {
0N/A inits = initsPrev;
0N/A uninits = uninitsPrev;
0N/A nextadr = nextadrPrev;
0N/A firstadr = firstadrPrev;
0N/A caught = caughtPrev;
0N/A lint = lintPrev;
0N/A }
0N/A }
0N/A
0N/A public void visitVarDef(JCVariableDecl tree) {
0N/A boolean track = trackable(tree.sym);
0N/A if (track && tree.sym.owner.kind == MTH) newVar(tree.sym);
0N/A if (tree.init != null) {
0N/A Lint lintPrev = lint;
0N/A lint = lint.augment(tree.sym.attributes_field);
0N/A try{
0N/A scanExpr(tree.init);
0N/A if (track) letInit(tree.pos(), tree.sym);
0N/A } finally {
0N/A lint = lintPrev;
0N/A }
0N/A }
0N/A }
0N/A
0N/A public void visitBlock(JCBlock tree) {
0N/A int nextadrPrev = nextadr;
0N/A scanStats(tree.stats);
0N/A nextadr = nextadrPrev;
0N/A }
0N/A
0N/A public void visitDoLoop(JCDoWhileLoop tree) {
0N/A ListBuffer<PendingExit> prevPendingExits = pendingExits;
0N/A boolean prevLoopPassTwo = loopPassTwo;
0N/A pendingExits = new ListBuffer<PendingExit>();
905N/A int prevErrors = log.nerrors;
0N/A do {
0N/A Bits uninitsEntry = uninits.dup();
905N/A uninitsEntry.excludeFrom(nextadr);
0N/A scanStat(tree.body);
0N/A alive |= resolveContinues(tree);
0N/A scanCond(tree.cond);
905N/A if (log.nerrors != prevErrors ||
0N/A loopPassTwo ||
905N/A uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
0N/A break;
0N/A inits = initsWhenTrue;
0N/A uninits = uninitsEntry.andSet(uninitsWhenTrue);
0N/A loopPassTwo = true;
0N/A alive = true;
0N/A } while (true);
0N/A loopPassTwo = prevLoopPassTwo;
0N/A inits = initsWhenFalse;
0N/A uninits = uninitsWhenFalse;
0N/A alive = alive && !tree.cond.type.isTrue();
0N/A alive |= resolveBreaks(tree, prevPendingExits);
0N/A }
0N/A
0N/A public void visitWhileLoop(JCWhileLoop tree) {
0N/A ListBuffer<PendingExit> prevPendingExits = pendingExits;
0N/A boolean prevLoopPassTwo = loopPassTwo;
0N/A Bits initsCond;
0N/A Bits uninitsCond;
0N/A pendingExits = new ListBuffer<PendingExit>();
905N/A int prevErrors = log.nerrors;
0N/A do {
0N/A Bits uninitsEntry = uninits.dup();
905N/A uninitsEntry.excludeFrom(nextadr);
0N/A scanCond(tree.cond);
0N/A initsCond = initsWhenFalse;
0N/A uninitsCond = uninitsWhenFalse;
0N/A inits = initsWhenTrue;
0N/A uninits = uninitsWhenTrue;
0N/A alive = !tree.cond.type.isFalse();
0N/A scanStat(tree.body);
0N/A alive |= resolveContinues(tree);
905N/A if (log.nerrors != prevErrors ||
0N/A loopPassTwo ||
905N/A uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
0N/A break;
0N/A uninits = uninitsEntry.andSet(uninits);
0N/A loopPassTwo = true;
0N/A alive = true;
0N/A } while (true);
0N/A loopPassTwo = prevLoopPassTwo;
0N/A inits = initsCond;
0N/A uninits = uninitsCond;
0N/A alive = resolveBreaks(tree, prevPendingExits) ||
0N/A !tree.cond.type.isTrue();
0N/A }
0N/A
0N/A public void visitForLoop(JCForLoop tree) {
0N/A ListBuffer<PendingExit> prevPendingExits = pendingExits;
0N/A boolean prevLoopPassTwo = loopPassTwo;
0N/A int nextadrPrev = nextadr;
0N/A scanStats(tree.init);
0N/A Bits initsCond;
0N/A Bits uninitsCond;
0N/A pendingExits = new ListBuffer<PendingExit>();
905N/A int prevErrors = log.nerrors;
0N/A do {
0N/A Bits uninitsEntry = uninits.dup();
905N/A uninitsEntry.excludeFrom(nextadr);
0N/A if (tree.cond != null) {
0N/A scanCond(tree.cond);
0N/A initsCond = initsWhenFalse;
0N/A uninitsCond = uninitsWhenFalse;
0N/A inits = initsWhenTrue;
0N/A uninits = uninitsWhenTrue;
0N/A alive = !tree.cond.type.isFalse();
0N/A } else {
0N/A initsCond = inits.dup();
0N/A initsCond.inclRange(firstadr, nextadr);
0N/A uninitsCond = uninits.dup();
0N/A uninitsCond.inclRange(firstadr, nextadr);
0N/A alive = true;
0N/A }
0N/A scanStat(tree.body);
0N/A alive |= resolveContinues(tree);
0N/A scan(tree.step);
905N/A if (log.nerrors != prevErrors ||
0N/A loopPassTwo ||
0N/A uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
0N/A break;
0N/A uninits = uninitsEntry.andSet(uninits);
0N/A loopPassTwo = true;
0N/A alive = true;
0N/A } while (true);
0N/A loopPassTwo = prevLoopPassTwo;
0N/A inits = initsCond;
0N/A uninits = uninitsCond;
0N/A alive = resolveBreaks(tree, prevPendingExits) ||
0N/A tree.cond != null && !tree.cond.type.isTrue();
0N/A nextadr = nextadrPrev;
0N/A }
0N/A
0N/A public void visitForeachLoop(JCEnhancedForLoop tree) {
0N/A visitVarDef(tree.var);
0N/A
0N/A ListBuffer<PendingExit> prevPendingExits = pendingExits;
0N/A boolean prevLoopPassTwo = loopPassTwo;
0N/A int nextadrPrev = nextadr;
0N/A scan(tree.expr);
0N/A Bits initsStart = inits.dup();
0N/A Bits uninitsStart = uninits.dup();
0N/A
0N/A letInit(tree.pos(), tree.var.sym);
0N/A pendingExits = new ListBuffer<PendingExit>();
905N/A int prevErrors = log.nerrors;
0N/A do {
0N/A Bits uninitsEntry = uninits.dup();
905N/A uninitsEntry.excludeFrom(nextadr);
0N/A scanStat(tree.body);
0N/A alive |= resolveContinues(tree);
905N/A if (log.nerrors != prevErrors ||
0N/A loopPassTwo ||
905N/A uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
0N/A break;
0N/A uninits = uninitsEntry.andSet(uninits);
0N/A loopPassTwo = true;
0N/A alive = true;
0N/A } while (true);
0N/A loopPassTwo = prevLoopPassTwo;
0N/A inits = initsStart;
0N/A uninits = uninitsStart.andSet(uninits);
0N/A resolveBreaks(tree, prevPendingExits);
0N/A alive = true;
0N/A nextadr = nextadrPrev;
0N/A }
0N/A
0N/A public void visitLabelled(JCLabeledStatement tree) {
0N/A ListBuffer<PendingExit> prevPendingExits = pendingExits;
0N/A pendingExits = new ListBuffer<PendingExit>();
0N/A scanStat(tree.body);
0N/A alive |= resolveBreaks(tree, prevPendingExits);
0N/A }
0N/A
0N/A public void visitSwitch(JCSwitch tree) {
0N/A ListBuffer<PendingExit> prevPendingExits = pendingExits;
0N/A pendingExits = new ListBuffer<PendingExit>();
0N/A int nextadrPrev = nextadr;
0N/A scanExpr(tree.selector);
0N/A Bits initsSwitch = inits;
0N/A Bits uninitsSwitch = uninits.dup();
0N/A boolean hasDefault = false;
0N/A for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
0N/A alive = true;
0N/A inits = initsSwitch.dup();
0N/A uninits = uninits.andSet(uninitsSwitch);
0N/A JCCase c = l.head;
0N/A if (c.pat == null)
0N/A hasDefault = true;
0N/A else
0N/A scanExpr(c.pat);
0N/A scanStats(c.stats);
0N/A addVars(c.stats, initsSwitch, uninitsSwitch);
0N/A // Warn about fall-through if lint switch fallthrough enabled.
0N/A if (!loopPassTwo &&
0N/A alive &&
0N/A lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
0N/A c.stats.nonEmpty() && l.tail.nonEmpty())
611N/A log.warning(Lint.LintCategory.FALLTHROUGH,
611N/A l.tail.head.pos(),
0N/A "possible.fall-through.into.case");
0N/A }
0N/A if (!hasDefault) {
0N/A inits.andSet(initsSwitch);
0N/A alive = true;
0N/A }
0N/A alive |= resolveBreaks(tree, prevPendingExits);
0N/A nextadr = nextadrPrev;
0N/A }
0N/A // where
0N/A /** Add any variables defined in stats to inits and uninits. */
0N/A private static void addVars(List<JCStatement> stats, Bits inits,
0N/A Bits uninits) {
0N/A for (;stats.nonEmpty(); stats = stats.tail) {
0N/A JCTree stat = stats.head;
0N/A if (stat.getTag() == JCTree.VARDEF) {
0N/A int adr = ((JCVariableDecl) stat).sym.adr;
0N/A inits.excl(adr);
0N/A uninits.incl(adr);
0N/A }
0N/A }
0N/A }
0N/A
0N/A public void visitTry(JCTry tree) {
0N/A List<Type> caughtPrev = caught;
0N/A List<Type> thrownPrev = thrown;
0N/A thrown = List.nil();
549N/A for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
549N/A List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
968N/A ((JCTypeUnion)l.head.param.vartype).alternatives :
549N/A List.of(l.head.param.vartype);
549N/A for (JCExpression ct : subClauses) {
549N/A caught = chk.incl(ct.type, caught);
549N/A }
549N/A }
904N/A ListBuffer<JCVariableDecl> resourceVarDecls = ListBuffer.lb();
0N/A Bits uninitsTryPrev = uninitsTry;
0N/A ListBuffer<PendingExit> prevPendingExits = pendingExits;
0N/A pendingExits = new ListBuffer<PendingExit>();
0N/A Bits initsTry = inits.dup();
0N/A uninitsTry = uninits.dup();
608N/A for (JCTree resource : tree.resources) {
608N/A if (resource instanceof JCVariableDecl) {
608N/A JCVariableDecl vdecl = (JCVariableDecl) resource;
608N/A visitVarDef(vdecl);
904N/A unrefdResources.enter(vdecl.sym);
904N/A resourceVarDecls.append(vdecl);
608N/A } else if (resource instanceof JCExpression) {
608N/A scanExpr((JCExpression) resource);
608N/A } else {
608N/A throw new AssertionError(tree); // parser error
608N/A }
608N/A }
608N/A for (JCTree resource : tree.resources) {
608N/A List<Type> closeableSupertypes = resource.type.isCompound() ?
608N/A types.interfaces(resource.type).prepend(types.supertype(resource.type)) :
608N/A List.of(resource.type);
608N/A for (Type sup : closeableSupertypes) {
608N/A if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) {
675N/A Symbol closeMethod = rs.resolveQualifiedMethod(tree,
616N/A attrEnv,
616N/A sup,
616N/A names.close,
616N/A List.<Type>nil(),
616N/A List.<Type>nil());
1425N/A Type mt = types.memberType(resource.type, closeMethod);
616N/A if (closeMethod.kind == MTH) {
1425N/A for (Type t : mt.getThrownTypes()) {
877N/A markThrown(resource, t);
616N/A }
608N/A }
608N/A }
608N/A }
608N/A }
0N/A scanStat(tree.body);
934N/A List<Type> thrownInTry = allowImprovedCatchAnalysis ?
934N/A chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) :
934N/A thrown;
0N/A thrown = thrownPrev;
0N/A caught = caughtPrev;
0N/A boolean aliveEnd = alive;
0N/A uninitsTry.andSet(uninits);
0N/A Bits initsEnd = inits;
0N/A Bits uninitsEnd = uninits;
0N/A int nextadrCatch = nextadr;
0N/A
904N/A if (!resourceVarDecls.isEmpty() &&
742N/A lint.isEnabled(Lint.LintCategory.TRY)) {
904N/A for (JCVariableDecl resVar : resourceVarDecls) {
904N/A if (unrefdResources.includes(resVar.sym)) {
904N/A log.warning(Lint.LintCategory.TRY, resVar.pos(),
904N/A "try.resource.not.referenced", resVar.sym);
904N/A unrefdResources.remove(resVar.sym);
904N/A }
608N/A }
608N/A }
608N/A
0N/A List<Type> caughtInTry = List.nil();
0N/A for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
0N/A alive = true;
0N/A JCVariableDecl param = l.head.param;
549N/A List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
968N/A ((JCTypeUnion)l.head.param.vartype).alternatives :
549N/A List.of(l.head.param.vartype);
549N/A List<Type> ctypes = List.nil();
549N/A List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry);
549N/A for (JCExpression ct : subClauses) {
549N/A Type exc = ct.type;
675N/A if (exc != syms.unknownType) {
675N/A ctypes = ctypes.append(exc);
675N/A if (types.isSameType(exc, syms.objectType))
675N/A continue;
934N/A checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry);
675N/A caughtInTry = chk.incl(exc, caughtInTry);
549N/A }
0N/A }
0N/A inits = initsTry.dup();
0N/A uninits = uninitsTry.dup();
0N/A scan(param);
0N/A inits.incl(param.sym.adr);
0N/A uninits.excl(param.sym.adr);
734N/A preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes));
0N/A scanStat(l.head.body);
0N/A initsEnd.andSet(inits);
0N/A uninitsEnd.andSet(uninits);
0N/A nextadr = nextadrCatch;
734N/A preciseRethrowTypes.remove(param.sym);
0N/A aliveEnd |= alive;
0N/A }
0N/A if (tree.finalizer != null) {
0N/A List<Type> savedThrown = thrown;
0N/A thrown = List.nil();
0N/A inits = initsTry.dup();
0N/A uninits = uninitsTry.dup();
0N/A ListBuffer<PendingExit> exits = pendingExits;
0N/A pendingExits = prevPendingExits;
0N/A alive = true;
0N/A scanStat(tree.finalizer);
0N/A if (!alive) {
0N/A // discard exits and exceptions from try and finally
0N/A thrown = chk.union(thrown, thrownPrev);
0N/A if (!loopPassTwo &&
0N/A lint.isEnabled(Lint.LintCategory.FINALLY)) {
611N/A log.warning(Lint.LintCategory.FINALLY,
611N/A TreeInfo.diagEndPos(tree.finalizer),
611N/A "finally.cannot.complete");
0N/A }
0N/A } else {
0N/A thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
0N/A thrown = chk.union(thrown, savedThrown);
0N/A uninits.andSet(uninitsEnd);
0N/A // FIX: this doesn't preserve source order of exits in catch
0N/A // versus finally!
0N/A while (exits.nonEmpty()) {
0N/A PendingExit exit = exits.next();
0N/A if (exit.inits != null) {
0N/A exit.inits.orSet(inits);
0N/A exit.uninits.andSet(uninits);
0N/A }
0N/A pendingExits.append(exit);
0N/A }
0N/A inits.orSet(initsEnd);
0N/A alive = aliveEnd;
0N/A }
0N/A } else {
0N/A thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
0N/A inits = initsEnd;
0N/A uninits = uninitsEnd;
0N/A alive = aliveEnd;
0N/A ListBuffer<PendingExit> exits = pendingExits;
0N/A pendingExits = prevPendingExits;
0N/A while (exits.nonEmpty()) pendingExits.append(exits.next());
0N/A }
0N/A uninitsTry.andSet(uninitsTryPrev).andSet(uninits);
0N/A }
0N/A
934N/A void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
934N/A if (chk.subset(exc, caughtInTry)) {
934N/A log.error(pos, "except.already.caught", exc);
934N/A } else if (!chk.isUnchecked(pos, exc) &&
989N/A !isExceptionOrThrowable(exc) &&
934N/A !chk.intersects(exc, thrownInTry)) {
934N/A log.error(pos, "except.never.thrown.in.try", exc);
934N/A } else if (allowImprovedCatchAnalysis) {
934N/A List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
934N/A // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
934N/A // unchecked exception, the result list would not be empty, as the augmented
934N/A // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
934N/A // exception, that would have been covered in the branch above
989N/A if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
989N/A !isExceptionOrThrowable(exc)) {
934N/A String key = catchableThrownTypes.length() == 1 ?
934N/A "unreachable.catch" :
934N/A "unreachable.catch.1";
934N/A log.warning(pos, key, catchableThrownTypes);
934N/A }
934N/A }
934N/A }
989N/A //where
989N/A private boolean isExceptionOrThrowable(Type exc) {
989N/A return exc.tsym == syms.throwableType.tsym ||
989N/A exc.tsym == syms.exceptionType.tsym;
989N/A }
989N/A
934N/A
0N/A public void visitConditional(JCConditional tree) {
0N/A scanCond(tree.cond);
0N/A Bits initsBeforeElse = initsWhenFalse;
0N/A Bits uninitsBeforeElse = uninitsWhenFalse;
0N/A inits = initsWhenTrue;
0N/A uninits = uninitsWhenTrue;
0N/A if (tree.truepart.type.tag == BOOLEAN &&
0N/A tree.falsepart.type.tag == BOOLEAN) {
0N/A // if b and c are boolean valued, then
0N/A // v is (un)assigned after a?b:c when true iff
0N/A // v is (un)assigned after b when true and
0N/A // v is (un)assigned after c when true
0N/A scanCond(tree.truepart);
0N/A Bits initsAfterThenWhenTrue = initsWhenTrue.dup();
0N/A Bits initsAfterThenWhenFalse = initsWhenFalse.dup();
0N/A Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup();
0N/A Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup();
0N/A inits = initsBeforeElse;
0N/A uninits = uninitsBeforeElse;
0N/A scanCond(tree.falsepart);
0N/A initsWhenTrue.andSet(initsAfterThenWhenTrue);
0N/A initsWhenFalse.andSet(initsAfterThenWhenFalse);
0N/A uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue);
0N/A uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse);
0N/A } else {
0N/A scanExpr(tree.truepart);
0N/A Bits initsAfterThen = inits.dup();
0N/A Bits uninitsAfterThen = uninits.dup();
0N/A inits = initsBeforeElse;
0N/A uninits = uninitsBeforeElse;
0N/A scanExpr(tree.falsepart);
0N/A inits.andSet(initsAfterThen);
0N/A uninits.andSet(uninitsAfterThen);
0N/A }
0N/A }
0N/A
0N/A public void visitIf(JCIf tree) {
0N/A scanCond(tree.cond);
0N/A Bits initsBeforeElse = initsWhenFalse;
0N/A Bits uninitsBeforeElse = uninitsWhenFalse;
0N/A inits = initsWhenTrue;
0N/A uninits = uninitsWhenTrue;
0N/A scanStat(tree.thenpart);
0N/A if (tree.elsepart != null) {
0N/A boolean aliveAfterThen = alive;
0N/A alive = true;
0N/A Bits initsAfterThen = inits.dup();
0N/A Bits uninitsAfterThen = uninits.dup();
0N/A inits = initsBeforeElse;
0N/A uninits = uninitsBeforeElse;
0N/A scanStat(tree.elsepart);
0N/A inits.andSet(initsAfterThen);
0N/A uninits.andSet(uninitsAfterThen);
0N/A alive = alive | aliveAfterThen;
0N/A } else {
0N/A inits.andSet(initsBeforeElse);
0N/A uninits.andSet(uninitsBeforeElse);
0N/A alive = true;
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A public void visitBreak(JCBreak tree) {
0N/A recordExit(tree);
0N/A }
0N/A
0N/A public void visitContinue(JCContinue tree) {
0N/A recordExit(tree);
0N/A }
0N/A
0N/A public void visitReturn(JCReturn tree) {
0N/A scanExpr(tree.expr);
0N/A // if not initial constructor, should markDead instead of recordExit
0N/A recordExit(tree);
0N/A }
0N/A
0N/A public void visitThrow(JCThrow tree) {
0N/A scanExpr(tree.expr);
549N/A Symbol sym = TreeInfo.symbol(tree.expr);
549N/A if (sym != null &&
549N/A sym.kind == VAR &&
734N/A (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
734N/A preciseRethrowTypes.get(sym) != null &&
934N/A allowImprovedRethrowAnalysis) {
734N/A for (Type t : preciseRethrowTypes.get(sym)) {
549N/A markThrown(tree, t);
549N/A }
549N/A }
549N/A else {
549N/A markThrown(tree, tree.expr.type);
549N/A }
0N/A markDead();
0N/A }
0N/A
0N/A public void visitApply(JCMethodInvocation tree) {
0N/A scanExpr(tree.meth);
0N/A scanExprs(tree.args);
0N/A for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
0N/A markThrown(tree, l.head);
0N/A }
0N/A
0N/A public void visitNewClass(JCNewClass tree) {
0N/A scanExpr(tree.encl);
0N/A scanExprs(tree.args);
0N/A // scan(tree.def);
185N/A for (List<Type> l = tree.constructorType.getThrownTypes();
0N/A l.nonEmpty();
185N/A l = l.tail) {
0N/A markThrown(tree, l.head);
185N/A }
185N/A List<Type> caughtPrev = caught;
185N/A try {
185N/A // If the new class expression defines an anonymous class,
185N/A // analysis of the anonymous constructor may encounter thrown
185N/A // types which are unsubstituted type variables.
185N/A // However, since the constructor's actual thrown types have
185N/A // already been marked as thrown, it is safe to simply include
185N/A // each of the constructor's formal thrown types in the set of
185N/A // 'caught/declared to be thrown' types, for the duration of
185N/A // the class def analysis.
185N/A if (tree.def != null)
185N/A for (List<Type> l = tree.constructor.type.getThrownTypes();
185N/A l.nonEmpty();
185N/A l = l.tail) {
185N/A caught = chk.incl(l.head, caught);
185N/A }
185N/A scan(tree.def);
185N/A }
185N/A finally {
185N/A caught = caughtPrev;
185N/A }
0N/A }
0N/A
0N/A public void visitNewArray(JCNewArray tree) {
0N/A scanExprs(tree.dims);
0N/A scanExprs(tree.elems);
0N/A }
0N/A
0N/A public void visitAssert(JCAssert tree) {
0N/A Bits initsExit = inits.dup();
0N/A Bits uninitsExit = uninits.dup();
0N/A scanCond(tree.cond);
0N/A uninitsExit.andSet(uninitsWhenTrue);
0N/A if (tree.detail != null) {
0N/A inits = initsWhenFalse;
0N/A uninits = uninitsWhenFalse;
0N/A scanExpr(tree.detail);
0N/A }
0N/A inits = initsExit;
0N/A uninits = uninitsExit;
0N/A }
0N/A
0N/A public void visitAssign(JCAssign tree) {
0N/A JCTree lhs = TreeInfo.skipParens(tree.lhs);
0N/A if (!(lhs instanceof JCIdent)) scanExpr(lhs);
0N/A scanExpr(tree.rhs);
0N/A letInit(lhs);
0N/A }
0N/A
0N/A public void visitAssignop(JCAssignOp tree) {
0N/A scanExpr(tree.lhs);
0N/A scanExpr(tree.rhs);
0N/A letInit(tree.lhs);
0N/A }
0N/A
0N/A public void visitUnary(JCUnary tree) {
0N/A switch (tree.getTag()) {
0N/A case JCTree.NOT:
0N/A scanCond(tree.arg);
0N/A Bits t = initsWhenFalse;
0N/A initsWhenFalse = initsWhenTrue;
0N/A initsWhenTrue = t;
0N/A t = uninitsWhenFalse;
0N/A uninitsWhenFalse = uninitsWhenTrue;
0N/A uninitsWhenTrue = t;
0N/A break;
0N/A case JCTree.PREINC: case JCTree.POSTINC:
0N/A case JCTree.PREDEC: case JCTree.POSTDEC:
0N/A scanExpr(tree.arg);
0N/A letInit(tree.arg);
0N/A break;
0N/A default:
0N/A scanExpr(tree.arg);
0N/A }
0N/A }
0N/A
0N/A public void visitBinary(JCBinary tree) {
0N/A switch (tree.getTag()) {
0N/A case JCTree.AND:
0N/A scanCond(tree.lhs);
0N/A Bits initsWhenFalseLeft = initsWhenFalse;
0N/A Bits uninitsWhenFalseLeft = uninitsWhenFalse;
0N/A inits = initsWhenTrue;
0N/A uninits = uninitsWhenTrue;
0N/A scanCond(tree.rhs);
0N/A initsWhenFalse.andSet(initsWhenFalseLeft);
0N/A uninitsWhenFalse.andSet(uninitsWhenFalseLeft);
0N/A break;
0N/A case JCTree.OR:
0N/A scanCond(tree.lhs);
0N/A Bits initsWhenTrueLeft = initsWhenTrue;
0N/A Bits uninitsWhenTrueLeft = uninitsWhenTrue;
0N/A inits = initsWhenFalse;
0N/A uninits = uninitsWhenFalse;
0N/A scanCond(tree.rhs);
0N/A initsWhenTrue.andSet(initsWhenTrueLeft);
0N/A uninitsWhenTrue.andSet(uninitsWhenTrueLeft);
0N/A break;
0N/A default:
0N/A scanExpr(tree.lhs);
0N/A scanExpr(tree.rhs);
0N/A }
0N/A }
0N/A
0N/A public void visitIdent(JCIdent tree) {
608N/A if (tree.sym.kind == VAR) {
0N/A checkInit(tree.pos(), (VarSymbol)tree.sym);
608N/A referenced(tree.sym);
608N/A }
608N/A }
608N/A
608N/A void referenced(Symbol sym) {
904N/A unrefdResources.remove(sym);
0N/A }
0N/A
0N/A public void visitTypeCast(JCTypeCast tree) {
0N/A super.visitTypeCast(tree);
0N/A if (!tree.type.isErroneous()
0N/A && lint.isEnabled(Lint.LintCategory.CAST)
307N/A && types.isSameType(tree.expr.type, tree.clazz.type)
741N/A && !is292targetTypeCast(tree)) {
611N/A log.warning(Lint.LintCategory.CAST,
611N/A tree.pos(), "redundant.cast", tree.expr.type);
0N/A }
0N/A }
741N/A //where
741N/A private boolean is292targetTypeCast(JCTypeCast tree) {
741N/A boolean is292targetTypeCast = false;
819N/A JCExpression expr = TreeInfo.skipParens(tree.expr);
819N/A if (expr.getTag() == JCTree.APPLY) {
819N/A JCMethodInvocation apply = (JCMethodInvocation)expr;
741N/A Symbol sym = TreeInfo.symbol(apply.meth);
741N/A is292targetTypeCast = sym != null &&
741N/A sym.kind == MTH &&
741N/A (sym.flags() & POLYMORPHIC_SIGNATURE) != 0;
741N/A }
741N/A return is292targetTypeCast;
741N/A }
0N/A
0N/A public void visitTopLevel(JCCompilationUnit tree) {
0N/A // Do nothing for TopLevel since each class is visited individually
0N/A }
0N/A
0N/A/**************************************************************************
0N/A * main method
0N/A *************************************************************************/
0N/A
0N/A /** Perform definite assignment/unassignment analysis on a tree.
0N/A */
616N/A public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
0N/A try {
616N/A attrEnv = env;
616N/A JCTree tree = env.tree;
0N/A this.make = make;
0N/A inits = new Bits();
0N/A uninits = new Bits();
0N/A uninitsTry = new Bits();
0N/A initsWhenTrue = initsWhenFalse =
0N/A uninitsWhenTrue = uninitsWhenFalse = null;
0N/A if (vars == null)
0N/A vars = new VarSymbol[32];
0N/A else
0N/A for (int i=0; i<vars.length; i++)
0N/A vars[i] = null;
0N/A firstadr = 0;
0N/A nextadr = 0;
0N/A pendingExits = new ListBuffer<PendingExit>();
734N/A preciseRethrowTypes = new HashMap<Symbol, List<Type>>();
0N/A alive = true;
0N/A this.thrown = this.caught = null;
0N/A this.classDef = null;
904N/A unrefdResources = new Scope(env.enclClass.sym);
0N/A scan(tree);
0N/A } finally {
0N/A // note that recursive invocations of this method fail hard
0N/A inits = uninits = uninitsTry = null;
0N/A initsWhenTrue = initsWhenFalse =
0N/A uninitsWhenTrue = uninitsWhenFalse = null;
0N/A if (vars != null) for (int i=0; i<vars.length; i++)
0N/A vars[i] = null;
0N/A firstadr = 0;
0N/A nextadr = 0;
0N/A pendingExits = null;
0N/A this.make = null;
0N/A this.thrown = this.caught = null;
0N/A this.classDef = null;
904N/A unrefdResources = null;
0N/A }
0N/A }
0N/A}