--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Wed Mar 14 13:53:41 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Mon Mar 26 15:27:51 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,8 +28,6 @@
package com.sun.tools.javac.comp;
import java.util.HashMap;
-import java.util.Map;
-import java.util.LinkedHashMap;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.tree.*;
@@ -179,7 +177,7 @@
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
-public class Flow extends TreeScanner {
+public class Flow {
protected static final Context.Key<Flow> flowKey =
new Context.Key<Flow>();
@@ -202,6 +200,11 @@
return instance;
}
+ public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ new FlowAnalyzer().analyzeTree(env, make);
+ new AssignAnalyzer().analyzeTree(env, make);
+ }
+
protected Flow(Context context) {
context.put(flowKey, this);
names = Names.instance(context);
@@ -216,1268 +219,1572 @@
allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis();
}
- /** A flag that indicates whether the last statement could
- * complete normally.
- */
- private boolean alive;
-
- /** The set of definitely assigned variables.
- */
- Bits inits;
-
- /** The set of definitely unassigned variables.
- */
- Bits uninits;
-
- HashMap<Symbol, List<Type>> preciseRethrowTypes;
-
- /** The set of variables that are definitely unassigned everywhere
- * in current try block. This variable is maintained lazily; it is
- * updated only when something gets removed from uninits,
- * typically by being assigned in reachable code. To obtain the
- * correct set of variables which are definitely unassigned
- * anywhere in current try block, intersect uninitsTry and
- * uninits.
- */
- Bits uninitsTry;
-
- /** When analyzing a condition, inits and uninits are null.
- * Instead we have:
- */
- Bits initsWhenTrue;
- Bits initsWhenFalse;
- Bits uninitsWhenTrue;
- Bits uninitsWhenFalse;
-
- /** A mapping from addresses to variable symbols.
- */
- VarSymbol[] vars;
-
- /** The current class being defined.
- */
- JCClassDecl classDef;
-
- /** The first variable sequence number in this class definition.
+ /**
+ * Base visitor class for all visitors implementing dataflow analysis logic.
+ * This class define the shared logic for handling jumps (break/continue statements).
*/
- int firstadr;
+ static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner {
- /** The next available variable sequence number.
- */
- int nextadr;
+ enum JumpKind {
+ BREAK(JCTree.Tag.BREAK) {
+ @Override
+ JCTree getTarget(JCTree tree) {
+ return ((JCBreak)tree).target;
+ }
+ },
+ CONTINUE(JCTree.Tag.CONTINUE) {
+ @Override
+ JCTree getTarget(JCTree tree) {
+ return ((JCContinue)tree).target;
+ }
+ };
- /** The list of possibly thrown declarable exceptions.
- */
- List<Type> thrown;
+ JCTree.Tag treeTag;
- /** The list of exceptions that are either caught or declared to be
- * thrown.
- */
- List<Type> caught;
+ private JumpKind(Tag treeTag) {
+ this.treeTag = treeTag;
+ }
+
+ abstract JCTree getTarget(JCTree tree);
+ }
- /** The list of unreferenced automatic resources.
- */
- Scope unrefdResources;
+ /** The currently pending exits that go from current inner blocks
+ * to an enclosing block, in source order.
+ */
+ ListBuffer<P> pendingExits;
- /** Set when processing a loop body the second time for DU analysis. */
- boolean loopPassTwo = false;
+ /** A pending exit. These are the statements return, break, and
+ * continue. In addition, exception-throwing expressions or
+ * statements are put here when not known to be caught. This
+ * will typically result in an error unless it is within a
+ * try-finally whose finally block cannot complete normally.
+ */
+ abstract static class PendingExit {
+ JCTree tree;
- /*-------------------- Environments ----------------------*/
+ PendingExit(JCTree tree) {
+ this.tree = tree;
+ }
+
+ abstract void resolveJump();
+ }
+
+ abstract void markDead();
+
+ /** Record an outward transfer of control. */
+ void recordExit(JCTree tree, P pe) {
+ pendingExits.append(pe);
+ markDead();
+ }
- /** A pending exit. These are the statements return, break, and
- * continue. In addition, exception-throwing expressions or
- * statements are put here when not known to be caught. This
- * will typically result in an error unless it is within a
- * try-finally whose finally block cannot complete normally.
- */
- static class PendingExit {
- JCTree tree;
- Bits inits;
- Bits uninits;
- Type thrown;
- PendingExit(JCTree tree, Bits inits, Bits uninits) {
- this.tree = tree;
- this.inits = inits.dup();
- this.uninits = uninits.dup();
+ /** Resolve all jumps of this statement. */
+ private boolean resolveJump(JCTree tree,
+ ListBuffer<P> oldPendingExits,
+ JumpKind jk) {
+ boolean resolved = false;
+ List<P> exits = pendingExits.toList();
+ pendingExits = oldPendingExits;
+ for (; exits.nonEmpty(); exits = exits.tail) {
+ P exit = exits.head;
+ if (exit.tree.hasTag(jk.treeTag) &&
+ jk.getTarget(exit.tree) == tree) {
+ exit.resolveJump();
+ resolved = true;
+ } else {
+ pendingExits.append(exit);
+ }
+ }
+ return resolved;
}
- PendingExit(JCTree tree, Type thrown) {
- this.tree = tree;
- this.thrown = thrown;
+
+ /** Resolve all breaks of this statement. */
+ boolean resolveContinues(JCTree tree) {
+ return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
+ }
+
+ /** Resolve all continues of this statement. */
+ boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
+ return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
}
}
- /** The currently pending exits that go from current inner blocks
- * to an enclosing block, in source order.
- */
- ListBuffer<PendingExit> pendingExits;
-
- /*-------------------- Exceptions ----------------------*/
-
- /** Complain that pending exceptions are not caught.
+ /**
+ * This pass implements the first two steps of the dataflow analysis:
+ * (i) liveness analysis checks that every statement is reachable and (ii)
+ * exception analysis to ensure that every checked exception that is
+ * thrown is declared or caught.
*/
- void errorUncaught() {
- for (PendingExit exit = pendingExits.next();
- exit != null;
- exit = pendingExits.next()) {
- if (classDef != null &&
- classDef.pos == exit.tree.pos) {
- log.error(exit.tree.pos(),
- "unreported.exception.default.constructor",
- exit.thrown);
- } else if (exit.tree.hasTag(VARDEF) &&
- ((JCVariableDecl)exit.tree).sym.isResourceVariable()) {
- log.error(exit.tree.pos(),
- "unreported.exception.implicit.close",
- exit.thrown,
- ((JCVariableDecl)exit.tree).sym.name);
- } else {
- log.error(exit.tree.pos(),
- "unreported.exception.need.to.catch.or.throw",
- exit.thrown);
+ class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> {
+
+ /** A flag that indicates whether the last statement could
+ * complete normally.
+ */
+ private boolean alive;
+
+ HashMap<Symbol, List<Type>> preciseRethrowTypes;
+
+ /** The current class being defined.
+ */
+ JCClassDecl classDef;
+
+ /** The list of possibly thrown declarable exceptions.
+ */
+ List<Type> thrown;
+
+ /** The list of exceptions that are either caught or declared to be
+ * thrown.
+ */
+ List<Type> caught;
+
+ class FlowPendingExit extends BaseAnalyzer.PendingExit {
+
+ Type thrown;
+
+ FlowPendingExit(JCTree tree, Type thrown) {
+ super(tree);
+ this.thrown = thrown;
+ }
+
+ void resolveJump() { /*do nothing*/ }
+ }
+
+ @Override
+ void markDead() {
+ alive = false;
+ }
+
+ /*-------------------- Exceptions ----------------------*/
+
+ /** Complain that pending exceptions are not caught.
+ */
+ void errorUncaught() {
+ for (FlowPendingExit exit = pendingExits.next();
+ exit != null;
+ exit = pendingExits.next()) {
+ if (classDef != null &&
+ classDef.pos == exit.tree.pos) {
+ log.error(exit.tree.pos(),
+ "unreported.exception.default.constructor",
+ exit.thrown);
+ } else if (exit.tree.hasTag(VARDEF) &&
+ ((JCVariableDecl)exit.tree).sym.isResourceVariable()) {
+ log.error(exit.tree.pos(),
+ "unreported.exception.implicit.close",
+ exit.thrown,
+ ((JCVariableDecl)exit.tree).sym.name);
+ } else {
+ log.error(exit.tree.pos(),
+ "unreported.exception.need.to.catch.or.throw",
+ exit.thrown);
+ }
+ }
+ }
+
+ /** Record that exception is potentially thrown and check that it
+ * is caught.
+ */
+ void markThrown(JCTree tree, Type exc) {
+ if (!chk.isUnchecked(tree.pos(), exc)) {
+ if (!chk.isHandled(exc, caught))
+ pendingExits.append(new FlowPendingExit(tree, exc));
+ thrown = chk.incl(exc, thrown);
+ }
+ }
+
+ /*************************************************************************
+ * Visitor methods for statements and definitions
+ *************************************************************************/
+
+ /** Analyze a definition.
+ */
+ void scanDef(JCTree tree) {
+ scanStat(tree);
+ if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) {
+ log.error(tree.pos(),
+ "initializer.must.be.able.to.complete.normally");
+ }
+ }
+
+ /** Analyze a statement. Check that statement is reachable.
+ */
+ void scanStat(JCTree tree) {
+ if (!alive && tree != null) {
+ log.error(tree.pos(), "unreachable.stmt");
+ if (!tree.hasTag(SKIP)) alive = true;
+ }
+ scan(tree);
+ }
+
+ /** Analyze list of statements.
+ */
+ void scanStats(List<? extends JCStatement> trees) {
+ if (trees != null)
+ for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
+ scanStat(l.head);
+ }
+
+ /* ------------ Visitor methods for various sorts of trees -------------*/
+
+ public void visitClassDef(JCClassDecl tree) {
+ if (tree.sym == null) return;
+
+ JCClassDecl classDefPrev = classDef;
+ List<Type> thrownPrev = thrown;
+ List<Type> caughtPrev = caught;
+ boolean alivePrev = alive;
+ ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits;
+ Lint lintPrev = lint;
+
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ if (tree.name != names.empty) {
+ caught = List.nil();
+ }
+ classDef = tree;
+ thrown = List.nil();
+ lint = lint.augment(tree.sym.attributes_field);
+
+ try {
+ // process all the static initializers
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (!l.head.hasTag(METHODDEF) &&
+ (TreeInfo.flags(l.head) & STATIC) != 0) {
+ scanDef(l.head);
+ errorUncaught();
+ }
+ }
+
+ // add intersection of all thrown clauses of initial constructors
+ // to set of caught exceptions, unless class is anonymous.
+ if (tree.name != names.empty) {
+ boolean firstConstructor = true;
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (TreeInfo.isInitialConstructor(l.head)) {
+ List<Type> mthrown =
+ ((JCMethodDecl) l.head).sym.type.getThrownTypes();
+ if (firstConstructor) {
+ caught = mthrown;
+ firstConstructor = false;
+ } else {
+ caught = chk.intersect(mthrown, caught);
+ }
+ }
+ }
+ }
+
+ // process all the instance initializers
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (!l.head.hasTag(METHODDEF) &&
+ (TreeInfo.flags(l.head) & STATIC) == 0) {
+ scanDef(l.head);
+ errorUncaught();
+ }
+ }
+
+ // in an anonymous class, add the set of thrown exceptions to
+ // the throws clause of the synthetic constructor and propagate
+ // outwards.
+ // Changing the throws clause on the fly is okay here because
+ // the anonymous constructor can't be invoked anywhere else,
+ // and its type hasn't been cached.
+ if (tree.name == names.empty) {
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (TreeInfo.isInitialConstructor(l.head)) {
+ JCMethodDecl mdef = (JCMethodDecl)l.head;
+ mdef.thrown = make.Types(thrown);
+ mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown);
+ }
+ }
+ thrownPrev = chk.union(thrown, thrownPrev);
+ }
+
+ // process all the methods
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (l.head.hasTag(METHODDEF)) {
+ scan(l.head);
+ errorUncaught();
+ }
+ }
+
+ thrown = thrownPrev;
+ } finally {
+ pendingExits = pendingExitsPrev;
+ alive = alivePrev;
+ caught = caughtPrev;
+ classDef = classDefPrev;
+ lint = lintPrev;
+ }
+ }
+
+ public void visitMethodDef(JCMethodDecl tree) {
+ if (tree.body == null) return;
+
+ List<Type> caughtPrev = caught;
+ List<Type> mthrown = tree.sym.type.getThrownTypes();
+ Lint lintPrev = lint;
+
+ lint = lint.augment(tree.sym.attributes_field);
+
+ Assert.check(pendingExits.isEmpty());
+
+ try {
+ for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
+ JCVariableDecl def = l.head;
+ scan(def);
+ }
+ if (TreeInfo.isInitialConstructor(tree))
+ caught = chk.union(caught, mthrown);
+ else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK)
+ caught = mthrown;
+ // else we are in an instance initializer block;
+ // leave caught unchanged.
+
+ alive = true;
+ scanStat(tree.body);
+
+ if (alive && tree.sym.type.getReturnType().tag != VOID)
+ log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
+
+ List<FlowPendingExit> exits = pendingExits.toList();
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ while (exits.nonEmpty()) {
+ FlowPendingExit exit = exits.head;
+ exits = exits.tail;
+ if (exit.thrown == null) {
+ Assert.check(exit.tree.hasTag(RETURN));
+ } else {
+ // uncaught throws will be reported later
+ pendingExits.append(exit);
+ }
+ }
+ } finally {
+ caught = caughtPrev;
+ lint = lintPrev;
+ }
+ }
+
+ public void visitVarDef(JCVariableDecl tree) {
+ if (tree.init != null) {
+ Lint lintPrev = lint;
+ lint = lint.augment(tree.sym.attributes_field);
+ try{
+ scan(tree.init);
+ } finally {
+ lint = lintPrev;
+ }
}
}
- }
+
+ public void visitBlock(JCBlock tree) {
+ scanStats(tree.stats);
+ }
- /** Record that exception is potentially thrown and check that it
- * is caught.
- */
- void markThrown(JCTree tree, Type exc) {
- if (!chk.isUnchecked(tree.pos(), exc)) {
- if (!chk.isHandled(exc, caught))
- pendingExits.append(new PendingExit(tree, exc));
- thrown = chk.incl(exc, thrown);
+ public void visitDoLoop(JCDoWhileLoop tree) {
+ ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ scan(tree.cond);
+ alive = alive && !tree.cond.type.isTrue();
+ alive |= resolveBreaks(tree, prevPendingExits);
}
- }
- /*-------------- Processing variables ----------------------*/
+ public void visitWhileLoop(JCWhileLoop tree) {
+ ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ scan(tree.cond);
+ alive = !tree.cond.type.isFalse();
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ alive = resolveBreaks(tree, prevPendingExits) ||
+ !tree.cond.type.isTrue();
+ }
- /** Do we need to track init/uninit state of this symbol?
- * I.e. is symbol either a local or a blank final variable?
- */
- boolean trackable(VarSymbol sym) {
- return
- (sym.owner.kind == MTH ||
- ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
- classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)));
- }
+ public void visitForLoop(JCForLoop tree) {
+ ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+ scanStats(tree.init);
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ if (tree.cond != null) {
+ scan(tree.cond);
+ alive = !tree.cond.type.isFalse();
+ } else {
+ alive = true;
+ }
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ scan(tree.step);
+ alive = resolveBreaks(tree, prevPendingExits) ||
+ tree.cond != null && !tree.cond.type.isTrue();
+ }
- /** Initialize new trackable variable by setting its address field
- * to the next available sequence number and entering it under that
- * index into the vars array.
- */
- void newVar(VarSymbol sym) {
- if (nextadr == vars.length) {
- VarSymbol[] newvars = new VarSymbol[nextadr * 2];
- System.arraycopy(vars, 0, newvars, 0, nextadr);
- vars = newvars;
+ public void visitForeachLoop(JCEnhancedForLoop tree) {
+ visitVarDef(tree.var);
+ ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+ scan(tree.expr);
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ scanStat(tree.body);
+ alive |= resolveContinues(tree);
+ resolveBreaks(tree, prevPendingExits);
+ alive = true;
+ }
+
+ public void visitLabelled(JCLabeledStatement tree) {
+ ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ scanStat(tree.body);
+ alive |= resolveBreaks(tree, prevPendingExits);
}
- sym.adr = nextadr;
- vars[nextadr] = sym;
- inits.excl(nextadr);
- uninits.incl(nextadr);
- nextadr++;
- }
+
+ public void visitSwitch(JCSwitch tree) {
+ ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ scan(tree.selector);
+ boolean hasDefault = false;
+ for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
+ alive = true;
+ JCCase c = l.head;
+ if (c.pat == null)
+ hasDefault = true;
+ else
+ scan(c.pat);
+ scanStats(c.stats);
+ // Warn about fall-through if lint switch fallthrough enabled.
+ if (alive &&
+ lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
+ c.stats.nonEmpty() && l.tail.nonEmpty())
+ log.warning(Lint.LintCategory.FALLTHROUGH,
+ l.tail.head.pos(),
+ "possible.fall-through.into.case");
+ }
+ if (!hasDefault) {
+ alive = true;
+ }
+ alive |= resolveBreaks(tree, prevPendingExits);
+ }
- /** Record an initialization of a trackable variable.
- */
- void letInit(DiagnosticPosition pos, VarSymbol sym) {
- if (sym.adr >= firstadr && trackable(sym)) {
- if ((sym.flags() & FINAL) != 0) {
- if ((sym.flags() & PARAMETER) != 0) {
- if ((sym.flags() & UNION) != 0) { //multi-catch parameter
- log.error(pos, "multicatch.parameter.may.not.be.assigned",
- sym);
+ public void visitTry(JCTry tree) {
+ List<Type> caughtPrev = caught;
+ List<Type> thrownPrev = thrown;
+ thrown = List.nil();
+ for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
+ List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
+ ((JCTypeUnion)l.head.param.vartype).alternatives :
+ List.of(l.head.param.vartype);
+ for (JCExpression ct : subClauses) {
+ caught = chk.incl(ct.type, caught);
+ }
+ }
+
+ ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ for (JCTree resource : tree.resources) {
+ if (resource instanceof JCVariableDecl) {
+ JCVariableDecl vdecl = (JCVariableDecl) resource;
+ visitVarDef(vdecl);
+ } else if (resource instanceof JCExpression) {
+ scan((JCExpression) resource);
+ } else {
+ throw new AssertionError(tree); // parser error
+ }
+ }
+ for (JCTree resource : tree.resources) {
+ List<Type> closeableSupertypes = resource.type.isCompound() ?
+ types.interfaces(resource.type).prepend(types.supertype(resource.type)) :
+ List.of(resource.type);
+ for (Type sup : closeableSupertypes) {
+ if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) {
+ Symbol closeMethod = rs.resolveQualifiedMethod(tree,
+ attrEnv,
+ sup,
+ names.close,
+ List.<Type>nil(),
+ List.<Type>nil());
+ if (closeMethod.kind == MTH) {
+ for (Type t : ((MethodSymbol)closeMethod).getThrownTypes()) {
+ markThrown(resource, t);
+ }
+ }
}
- else {
- log.error(pos, "final.parameter.may.not.be.assigned",
- sym);
- }
- } else if (!uninits.isMember(sym.adr)) {
- log.error(pos,
- loopPassTwo
- ? "var.might.be.assigned.in.loop"
- : "var.might.already.be.assigned",
- sym);
- } else if (!inits.isMember(sym.adr)) {
- // reachable assignment
- uninits.excl(sym.adr);
- uninitsTry.excl(sym.adr);
- } else {
- //log.rawWarning(pos, "unreachable assignment");//DEBUG
- uninits.excl(sym.adr);
}
}
- inits.incl(sym.adr);
- } else if ((sym.flags() & FINAL) != 0) {
- log.error(pos, "var.might.already.be.assigned", sym);
+ scanStat(tree.body);
+ List<Type> thrownInTry = allowImprovedCatchAnalysis ?
+ chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) :
+ thrown;
+ thrown = thrownPrev;
+ caught = caughtPrev;
+ boolean aliveEnd = alive;
+
+ List<Type> caughtInTry = List.nil();
+ for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
+ alive = true;
+ JCVariableDecl param = l.head.param;
+ List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
+ ((JCTypeUnion)l.head.param.vartype).alternatives :
+ List.of(l.head.param.vartype);
+ List<Type> ctypes = List.nil();
+ List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry);
+ for (JCExpression ct : subClauses) {
+ Type exc = ct.type;
+ if (exc != syms.unknownType) {
+ ctypes = ctypes.append(exc);
+ if (types.isSameType(exc, syms.objectType))
+ continue;
+ checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry);
+ caughtInTry = chk.incl(exc, caughtInTry);
+ }
+ }
+ scan(param);
+ preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes));
+ scanStat(l.head.body);
+ preciseRethrowTypes.remove(param.sym);
+ aliveEnd |= alive;
+ }
+ if (tree.finalizer != null) {
+ List<Type> savedThrown = thrown;
+ thrown = List.nil();
+ ListBuffer<FlowPendingExit> exits = pendingExits;
+ pendingExits = prevPendingExits;
+ alive = true;
+ scanStat(tree.finalizer);
+ if (!alive) {
+ // discard exits and exceptions from try and finally
+ thrown = chk.union(thrown, thrownPrev);
+ if (lint.isEnabled(Lint.LintCategory.FINALLY)) {
+ log.warning(Lint.LintCategory.FINALLY,
+ TreeInfo.diagEndPos(tree.finalizer),
+ "finally.cannot.complete");
+ }
+ } else {
+ thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
+ thrown = chk.union(thrown, savedThrown);
+ // FIX: this doesn't preserve source order of exits in catch
+ // versus finally!
+ while (exits.nonEmpty()) {
+ pendingExits.append(exits.next());
+ }
+ alive = aliveEnd;
+ }
+ tree.finallyCanCompleteNormally = alive;
+ } else {
+ thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
+ alive = aliveEnd;
+ ListBuffer<FlowPendingExit> exits = pendingExits;
+ pendingExits = prevPendingExits;
+ while (exits.nonEmpty()) pendingExits.append(exits.next());
+ }
}
- }
+
+ @Override
+ public void visitIf(JCIf tree) {
+ scan(tree.cond);
+ scanStat(tree.thenpart);
+ if (tree.elsepart != null) {
+ boolean aliveAfterThen = alive;
+ alive = true;
+ scanStat(tree.elsepart);
+ alive = alive | aliveAfterThen;
+ } else {
+ alive = true;
+ }
+ }
- /** If tree is either a simple name or of the form this.name or
- * C.this.name, and tree represents a trackable variable,
- * record an initialization of the variable.
- */
- void letInit(JCTree tree) {
- tree = TreeInfo.skipParens(tree);
- if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
- Symbol sym = TreeInfo.symbol(tree);
- if (sym.kind == VAR) {
- letInit(tree.pos(), (VarSymbol)sym);
+ void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
+ if (chk.subset(exc, caughtInTry)) {
+ log.error(pos, "except.already.caught", exc);
+ } else if (!chk.isUnchecked(pos, exc) &&
+ !isExceptionOrThrowable(exc) &&
+ !chk.intersects(exc, thrownInTry)) {
+ log.error(pos, "except.never.thrown.in.try", exc);
+ } else if (allowImprovedCatchAnalysis) {
+ List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
+ // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
+ // unchecked exception, the result list would not be empty, as the augmented
+ // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
+ // exception, that would have been covered in the branch above
+ if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
+ !isExceptionOrThrowable(exc)) {
+ String key = catchableThrownTypes.length() == 1 ?
+ "unreachable.catch" :
+ "unreachable.catch.1";
+ log.warning(pos, key, catchableThrownTypes);
+ }
+ }
+ }
+ //where
+ private boolean isExceptionOrThrowable(Type exc) {
+ return exc.tsym == syms.throwableType.tsym ||
+ exc.tsym == syms.exceptionType.tsym;
+ }
+
+ public void visitBreak(JCBreak tree) {
+ recordExit(tree, new FlowPendingExit(tree, null));
+ }
+
+ public void visitContinue(JCContinue tree) {
+ recordExit(tree, new FlowPendingExit(tree, null));
+ }
+
+ public void visitReturn(JCReturn tree) {
+ scan(tree.expr);
+ // if not initial constructor, should markDead instead of recordExit
+ recordExit(tree, new FlowPendingExit(tree, null));
+ }
+
+ public void visitThrow(JCThrow tree) {
+ scan(tree.expr);
+ Symbol sym = TreeInfo.symbol(tree.expr);
+ if (sym != null &&
+ sym.kind == VAR &&
+ (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
+ preciseRethrowTypes.get(sym) != null &&
+ allowImprovedRethrowAnalysis) {
+ for (Type t : preciseRethrowTypes.get(sym)) {
+ markThrown(tree, t);
+ }
+ }
+ else {
+ markThrown(tree, tree.expr.type);
+ }
+ markDead();
+ }
+
+ public void visitApply(JCMethodInvocation tree) {
+ scan(tree.meth);
+ scan(tree.args);
+ for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
+ markThrown(tree, l.head);
+ }
+
+ public void visitNewClass(JCNewClass tree) {
+ scan(tree.encl);
+ scan(tree.args);
+ // scan(tree.def);
+ for (List<Type> l = tree.constructorType.getThrownTypes();
+ l.nonEmpty();
+ l = l.tail) {
+ markThrown(tree, l.head);
+ }
+ List<Type> caughtPrev = caught;
+ try {
+ // If the new class expression defines an anonymous class,
+ // analysis of the anonymous constructor may encounter thrown
+ // types which are unsubstituted type variables.
+ // However, since the constructor's actual thrown types have
+ // already been marked as thrown, it is safe to simply include
+ // each of the constructor's formal thrown types in the set of
+ // 'caught/declared to be thrown' types, for the duration of
+ // the class def analysis.
+ if (tree.def != null)
+ for (List<Type> l = tree.constructor.type.getThrownTypes();
+ l.nonEmpty();
+ l = l.tail) {
+ caught = chk.incl(l.head, caught);
+ }
+ scan(tree.def);
+ }
+ finally {
+ caught = caughtPrev;
+ }
+ }
+
+ public void visitTopLevel(JCCompilationUnit tree) {
+ // Do nothing for TopLevel since each class is visited individually
+ }
+
+ /**************************************************************************
+ * main method
+ *************************************************************************/
+
+ /** Perform definite assignment/unassignment analysis on a tree.
+ */
+ public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ try {
+ attrEnv = env;
+ JCTree tree = env.tree;
+ Flow.this.make = make;
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ preciseRethrowTypes = new HashMap<Symbol, List<Type>>();
+ alive = true;
+ this.thrown = this.caught = null;
+ this.classDef = null;
+ scan(tree);
+ } finally {
+ pendingExits = null;
+ Flow.this.make = null;
+ this.thrown = this.caught = null;
+ this.classDef = null;
}
}
}
- /** Check that trackable variable is initialized.
+ /**
+ * This pass implements (i) definite assignment analysis, which ensures that
+ * each variable is assigned when used and (ii) definite unassignment analysis,
+ * which ensures that no final variable is assigned more than once. This visitor
+ * depends on the results of the liveliness analyzer.
*/
- void checkInit(DiagnosticPosition pos, VarSymbol sym) {
- if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
- trackable(sym) &&
- !inits.isMember(sym.adr)) {
- log.error(pos, "var.might.not.have.been.initialized",
- sym);
- inits.incl(sym.adr);
- }
- }
+ class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> {
+
+ /** The set of definitely assigned variables.
+ */
+ Bits inits;
+
+ /** The set of definitely unassigned variables.
+ */
+ Bits uninits;
- /*-------------------- Handling jumps ----------------------*/
+ /** The set of variables that are definitely unassigned everywhere
+ * in current try block. This variable is maintained lazily; it is
+ * updated only when something gets removed from uninits,
+ * typically by being assigned in reachable code. To obtain the
+ * correct set of variables which are definitely unassigned
+ * anywhere in current try block, intersect uninitsTry and
+ * uninits.
+ */
+ Bits uninitsTry;
- /** Record an outward transfer of control. */
- void recordExit(JCTree tree) {
- pendingExits.append(new PendingExit(tree, inits, uninits));
- markDead();
- }
+ /** When analyzing a condition, inits and uninits are null.
+ * Instead we have:
+ */
+ Bits initsWhenTrue;
+ Bits initsWhenFalse;
+ Bits uninitsWhenTrue;
+ Bits uninitsWhenFalse;
- /** Resolve all breaks of this statement. */
- boolean resolveBreaks(JCTree tree,
- ListBuffer<PendingExit> oldPendingExits) {
- boolean result = false;
- List<PendingExit> exits = pendingExits.toList();
- pendingExits = oldPendingExits;
- for (; exits.nonEmpty(); exits = exits.tail) {
- PendingExit exit = exits.head;
- if (exit.tree.hasTag(BREAK) &&
- ((JCBreak) exit.tree).target == tree) {
- inits.andSet(exit.inits);
- uninits.andSet(exit.uninits);
- result = true;
- } else {
- pendingExits.append(exit);
+ /** A mapping from addresses to variable symbols.
+ */
+ VarSymbol[] vars;
+
+ /** The current class being defined.
+ */
+ JCClassDecl classDef;
+
+ /** The first variable sequence number in this class definition.
+ */
+ int firstadr;
+
+ /** The next available variable sequence number.
+ */
+ int nextadr;
+
+ /** The list of unreferenced automatic resources.
+ */
+ Scope unrefdResources;
+
+ /** Set when processing a loop body the second time for DU analysis. */
+ boolean loopPassTwo = false;
+
+ class AssignPendingExit extends BaseAnalyzer.PendingExit {
+
+ Bits exit_inits;
+ Bits exit_uninits;
+
+ AssignPendingExit(JCTree tree, Bits inits, Bits uninits) {
+ super(tree);
+ this.exit_inits = inits.dup();
+ this.exit_uninits = uninits.dup();
}
- }
- return result;
- }
- /** Resolve all continues of this statement. */
- boolean resolveContinues(JCTree tree) {
- boolean result = false;
- List<PendingExit> exits = pendingExits.toList();
- pendingExits = new ListBuffer<PendingExit>();
- for (; exits.nonEmpty(); exits = exits.tail) {
- PendingExit exit = exits.head;
- if (exit.tree.hasTag(CONTINUE) &&
- ((JCContinue) exit.tree).target == tree) {
- inits.andSet(exit.inits);
- uninits.andSet(exit.uninits);
- result = true;
- } else {
- pendingExits.append(exit);
+ void resolveJump() {
+ inits.andSet(exit_inits);
+ uninits.andSet(exit_uninits);
}
}
- return result;
- }
- /** Record that statement is unreachable.
- */
- void markDead() {
- inits.inclRange(firstadr, nextadr);
- uninits.inclRange(firstadr, nextadr);
- alive = false;
- }
+ @Override
+ void markDead() {
+ inits.inclRange(firstadr, nextadr);
+ uninits.inclRange(firstadr, nextadr);
+ }
+
+ /*-------------- Processing variables ----------------------*/
- /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets
- */
- void split(boolean setToNull) {
- initsWhenFalse = inits.dup();
- uninitsWhenFalse = uninits.dup();
- initsWhenTrue = inits;
- uninitsWhenTrue = uninits;
- if (setToNull)
- inits = uninits = null;
- }
+ /** Do we need to track init/uninit state of this symbol?
+ * I.e. is symbol either a local or a blank final variable?
+ */
+ boolean trackable(VarSymbol sym) {
+ return
+ (sym.owner.kind == MTH ||
+ ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
+ classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)));
+ }
- /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets.
- */
- void merge() {
- inits = initsWhenFalse.andSet(initsWhenTrue);
- uninits = uninitsWhenFalse.andSet(uninitsWhenTrue);
- }
-
-/* ************************************************************************
- * Visitor methods for statements and definitions
- *************************************************************************/
-
- /** Analyze a definition.
- */
- void scanDef(JCTree tree) {
- scanStat(tree);
- if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) {
- log.error(tree.pos(),
- "initializer.must.be.able.to.complete.normally");
+ /** Initialize new trackable variable by setting its address field
+ * to the next available sequence number and entering it under that
+ * index into the vars array.
+ */
+ void newVar(VarSymbol sym) {
+ if (nextadr == vars.length) {
+ VarSymbol[] newvars = new VarSymbol[nextadr * 2];
+ System.arraycopy(vars, 0, newvars, 0, nextadr);
+ vars = newvars;
+ }
+ sym.adr = nextadr;
+ vars[nextadr] = sym;
+ inits.excl(nextadr);
+ uninits.incl(nextadr);
+ nextadr++;
}
- }
- /** Analyze a statement. Check that statement is reachable.
- */
- void scanStat(JCTree tree) {
- if (!alive && tree != null) {
- log.error(tree.pos(), "unreachable.stmt");
- if (!tree.hasTag(SKIP)) alive = true;
- }
- scan(tree);
- }
-
- /** Analyze list of statements.
- */
- void scanStats(List<? extends JCStatement> trees) {
- if (trees != null)
- for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail)
- scanStat(l.head);
- }
-
- /** Analyze an expression. Make sure to set (un)inits rather than
- * (un)initsWhenTrue(WhenFalse) on exit.
- */
- void scanExpr(JCTree tree) {
- if (tree != null) {
- scan(tree);
- if (inits == null) merge();
+ /** Record an initialization of a trackable variable.
+ */
+ void letInit(DiagnosticPosition pos, VarSymbol sym) {
+ if (sym.adr >= firstadr && trackable(sym)) {
+ if ((sym.flags() & FINAL) != 0) {
+ if ((sym.flags() & PARAMETER) != 0) {
+ if ((sym.flags() & UNION) != 0) { //multi-catch parameter
+ log.error(pos, "multicatch.parameter.may.not.be.assigned",
+ sym);
+ }
+ else {
+ log.error(pos, "final.parameter.may.not.be.assigned",
+ sym);
+ }
+ } else if (!uninits.isMember(sym.adr)) {
+ log.error(pos,
+ loopPassTwo
+ ? "var.might.be.assigned.in.loop"
+ : "var.might.already.be.assigned",
+ sym);
+ } else if (!inits.isMember(sym.adr)) {
+ // reachable assignment
+ uninits.excl(sym.adr);
+ uninitsTry.excl(sym.adr);
+ } else {
+ //log.rawWarning(pos, "unreachable assignment");//DEBUG
+ uninits.excl(sym.adr);
+ }
+ }
+ inits.incl(sym.adr);
+ } else if ((sym.flags() & FINAL) != 0) {
+ log.error(pos, "var.might.already.be.assigned", sym);
+ }
}
- }
- /** Analyze a list of expressions.
- */
- void scanExprs(List<? extends JCExpression> trees) {
- if (trees != null)
- for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail)
- scanExpr(l.head);
- }
+ /** If tree is either a simple name or of the form this.name or
+ * C.this.name, and tree represents a trackable variable,
+ * record an initialization of the variable.
+ */
+ void letInit(JCTree tree) {
+ tree = TreeInfo.skipParens(tree);
+ if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
+ Symbol sym = TreeInfo.symbol(tree);
+ if (sym.kind == VAR) {
+ letInit(tree.pos(), (VarSymbol)sym);
+ }
+ }
+ }
- /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
- * rather than (un)inits on exit.
- */
- void scanCond(JCTree tree) {
- if (tree.type.isFalse()) {
- if (inits == null) merge();
- initsWhenTrue = inits.dup();
- initsWhenTrue.inclRange(firstadr, nextadr);
- uninitsWhenTrue = uninits.dup();
- uninitsWhenTrue.inclRange(firstadr, nextadr);
- initsWhenFalse = inits;
- uninitsWhenFalse = uninits;
- } else if (tree.type.isTrue()) {
- if (inits == null) merge();
+ /** Check that trackable variable is initialized.
+ */
+ void checkInit(DiagnosticPosition pos, VarSymbol sym) {
+ if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
+ trackable(sym) &&
+ !inits.isMember(sym.adr)) {
+ log.error(pos, "var.might.not.have.been.initialized",
+ sym);
+ inits.incl(sym.adr);
+ }
+ }
+
+ /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets
+ */
+ void split(boolean setToNull) {
initsWhenFalse = inits.dup();
- initsWhenFalse.inclRange(firstadr, nextadr);
uninitsWhenFalse = uninits.dup();
- uninitsWhenFalse.inclRange(firstadr, nextadr);
initsWhenTrue = inits;
uninitsWhenTrue = uninits;
- } else {
- scan(tree);
- if (inits != null)
- split(tree.type != syms.unknownType);
+ if (setToNull)
+ inits = uninits = null;
}
- if (tree.type != syms.unknownType)
- inits = uninits = null;
- }
- /* ------------ Visitor methods for various sorts of trees -------------*/
+ /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets.
+ */
+ void merge() {
+ inits = initsWhenFalse.andSet(initsWhenTrue);
+ uninits = uninitsWhenFalse.andSet(uninitsWhenTrue);
+ }
- public void visitClassDef(JCClassDecl tree) {
- if (tree.sym == null) return;
+ /* ************************************************************************
+ * Visitor methods for statements and definitions
+ *************************************************************************/
- JCClassDecl classDefPrev = classDef;
- List<Type> thrownPrev = thrown;
- List<Type> caughtPrev = caught;
- boolean alivePrev = alive;
- int firstadrPrev = firstadr;
- int nextadrPrev = nextadr;
- ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
- Lint lintPrev = lint;
+ /** Analyze an expression. Make sure to set (un)inits rather than
+ * (un)initsWhenTrue(WhenFalse) on exit.
+ */
+ void scanExpr(JCTree tree) {
+ if (tree != null) {
+ scan(tree);
+ if (inits == null) merge();
+ }
+ }
- pendingExits = new ListBuffer<PendingExit>();
- if (tree.name != names.empty) {
- caught = List.nil();
- firstadr = nextadr;
+ /** Analyze a list of expressions.
+ */
+ void scanExprs(List<? extends JCExpression> trees) {
+ if (trees != null)
+ for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail)
+ scanExpr(l.head);
}
- classDef = tree;
- thrown = List.nil();
- lint = lint.augment(tree.sym.attributes_field);
- try {
- // define all the static fields
- for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (l.head.hasTag(VARDEF)) {
- JCVariableDecl def = (JCVariableDecl)l.head;
- if ((def.mods.flags & STATIC) != 0) {
- VarSymbol sym = def.sym;
- if (trackable(sym))
- newVar(sym);
- }
- }
+ /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
+ * rather than (un)inits on exit.
+ */
+ void scanCond(JCTree tree) {
+ if (tree.type.isFalse()) {
+ if (inits == null) merge();
+ initsWhenTrue = inits.dup();
+ initsWhenTrue.inclRange(firstadr, nextadr);
+ uninitsWhenTrue = uninits.dup();
+ uninitsWhenTrue.inclRange(firstadr, nextadr);
+ initsWhenFalse = inits;
+ uninitsWhenFalse = uninits;
+ } else if (tree.type.isTrue()) {
+ if (inits == null) merge();
+ initsWhenFalse = inits.dup();
+ initsWhenFalse.inclRange(firstadr, nextadr);
+ uninitsWhenFalse = uninits.dup();
+ uninitsWhenFalse.inclRange(firstadr, nextadr);
+ initsWhenTrue = inits;
+ uninitsWhenTrue = uninits;
+ } else {
+ scan(tree);
+ if (inits != null)
+ split(tree.type != syms.unknownType);
}
+ if (tree.type != syms.unknownType)
+ inits = uninits = null;
+ }
- // process all the static initializers
- for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) != 0) {
- scanDef(l.head);
- errorUncaught();
- }
- }
+ /* ------------ Visitor methods for various sorts of trees -------------*/
+
+ public void visitClassDef(JCClassDecl tree) {
+ if (tree.sym == null) return;
+
+ JCClassDecl classDefPrev = classDef;
+ int firstadrPrev = firstadr;
+ int nextadrPrev = nextadr;
+ ListBuffer<AssignPendingExit> pendingExitsPrev = pendingExits;
+ Lint lintPrev = lint;
- // add intersection of all thrown clauses of initial constructors
- // to set of caught exceptions, unless class is anonymous.
+ pendingExits = new ListBuffer<AssignPendingExit>();
if (tree.name != names.empty) {
- boolean firstConstructor = true;
+ firstadr = nextadr;
+ }
+ classDef = tree;
+ lint = lint.augment(tree.sym.attributes_field);
+
+ try {
+ // define all the static fields
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (TreeInfo.isInitialConstructor(l.head)) {
- List<Type> mthrown =
- ((JCMethodDecl) l.head).sym.type.getThrownTypes();
- if (firstConstructor) {
- caught = mthrown;
- firstConstructor = false;
- } else {
- caught = chk.intersect(mthrown, caught);
+ if (l.head.hasTag(VARDEF)) {
+ JCVariableDecl def = (JCVariableDecl)l.head;
+ if ((def.mods.flags & STATIC) != 0) {
+ VarSymbol sym = def.sym;
+ if (trackable(sym))
+ newVar(sym);
}
}
}
- }
- // define all the instance fields
- for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (l.head.hasTag(VARDEF)) {
- JCVariableDecl def = (JCVariableDecl)l.head;
- if ((def.mods.flags & STATIC) == 0) {
- VarSymbol sym = def.sym;
- if (trackable(sym))
- newVar(sym);
+ // process all the static initializers
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (!l.head.hasTag(METHODDEF) &&
+ (TreeInfo.flags(l.head) & STATIC) != 0) {
+ scan(l.head);
}
}
- }
- // process all the instance initializers
- for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (!l.head.hasTag(METHODDEF) &&
- (TreeInfo.flags(l.head) & STATIC) == 0) {
- scanDef(l.head);
- errorUncaught();
+ // define all the instance fields
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (l.head.hasTag(VARDEF)) {
+ JCVariableDecl def = (JCVariableDecl)l.head;
+ if ((def.mods.flags & STATIC) == 0) {
+ VarSymbol sym = def.sym;
+ if (trackable(sym))
+ newVar(sym);
+ }
+ }
}
- }
- // in an anonymous class, add the set of thrown exceptions to
- // the throws clause of the synthetic constructor and propagate
- // outwards.
- // Changing the throws clause on the fly is okay here because
- // the anonymous constructor can't be invoked anywhere else,
- // and its type hasn't been cached.
- if (tree.name == names.empty) {
+ // process all the instance initializers
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (TreeInfo.isInitialConstructor(l.head)) {
- JCMethodDecl mdef = (JCMethodDecl)l.head;
- mdef.thrown = make.Types(thrown);
- mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown);
+ if (!l.head.hasTag(METHODDEF) &&
+ (TreeInfo.flags(l.head) & STATIC) == 0) {
+ scan(l.head);
+ }
+ }
+
+ // process all the methods
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ if (l.head.hasTag(METHODDEF)) {
+ scan(l.head);
}
}
- thrownPrev = chk.union(thrown, thrownPrev);
+ } finally {
+ pendingExits = pendingExitsPrev;
+ nextadr = nextadrPrev;
+ firstadr = firstadrPrev;
+ classDef = classDefPrev;
+ lint = lintPrev;
}
+ }
- // process all the methods
- for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
- if (l.head.hasTag(METHODDEF)) {
- scan(l.head);
- errorUncaught();
- }
- }
+ public void visitMethodDef(JCMethodDecl tree) {
+ if (tree.body == null) return;
- thrown = thrownPrev;
- } finally {
- pendingExits = pendingExitsPrev;
- alive = alivePrev;
- nextadr = nextadrPrev;
- firstadr = firstadrPrev;
- caught = caughtPrev;
- classDef = classDefPrev;
- lint = lintPrev;
- }
- }
+ Bits initsPrev = inits.dup();
+ Bits uninitsPrev = uninits.dup();
+ int nextadrPrev = nextadr;
+ int firstadrPrev = firstadr;
+ Lint lintPrev = lint;
- public void visitMethodDef(JCMethodDecl tree) {
- if (tree.body == null) return;
+ lint = lint.augment(tree.sym.attributes_field);
+
+ Assert.check(pendingExits.isEmpty());
- List<Type> caughtPrev = caught;
- List<Type> mthrown = tree.sym.type.getThrownTypes();
- Bits initsPrev = inits.dup();
- Bits uninitsPrev = uninits.dup();
- int nextadrPrev = nextadr;
- int firstadrPrev = firstadr;
- Lint lintPrev = lint;
-
- lint = lint.augment(tree.sym.attributes_field);
-
- Assert.check(pendingExits.isEmpty());
+ try {
+ boolean isInitialConstructor =
+ TreeInfo.isInitialConstructor(tree);
- try {
- boolean isInitialConstructor =
- TreeInfo.isInitialConstructor(tree);
-
- if (!isInitialConstructor)
- firstadr = nextadr;
- for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
- JCVariableDecl def = l.head;
- scan(def);
- inits.incl(def.sym.adr);
- uninits.excl(def.sym.adr);
- }
- if (isInitialConstructor)
- caught = chk.union(caught, mthrown);
- else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK)
- caught = mthrown;
- // else we are in an instance initializer block;
- // leave caught unchanged.
+ if (!isInitialConstructor)
+ firstadr = nextadr;
+ for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
+ JCVariableDecl def = l.head;
+ scan(def);
+ inits.incl(def.sym.adr);
+ uninits.excl(def.sym.adr);
+ }
+ // else we are in an instance initializer block;
+ // leave caught unchanged.
+ scan(tree.body);
- alive = true;
- scanStat(tree.body);
-
- if (alive && tree.sym.type.getReturnType().tag != VOID)
- log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
-
- if (isInitialConstructor) {
- for (int i = firstadr; i < nextadr; i++)
- if (vars[i].owner == classDef.sym)
- checkInit(TreeInfo.diagEndPos(tree.body), vars[i]);
- }
- List<PendingExit> exits = pendingExits.toList();
- pendingExits = new ListBuffer<PendingExit>();
- while (exits.nonEmpty()) {
- PendingExit exit = exits.head;
- exits = exits.tail;
- if (exit.thrown == null) {
- Assert.check(exit.tree.hasTag(RETURN));
+ if (isInitialConstructor) {
+ for (int i = firstadr; i < nextadr; i++)
+ if (vars[i].owner == classDef.sym)
+ checkInit(TreeInfo.diagEndPos(tree.body), vars[i]);
+ }
+ List<AssignPendingExit> exits = pendingExits.toList();
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ while (exits.nonEmpty()) {
+ AssignPendingExit exit = exits.head;
+ exits = exits.tail;
+ Assert.check(exit.tree.hasTag(RETURN), exit.tree);
if (isInitialConstructor) {
- inits = exit.inits;
+ inits = exit.exit_inits;
for (int i = firstadr; i < nextadr; i++)
checkInit(exit.tree.pos(), vars[i]);
}
- } else {
- // uncaught throws will be reported later
- pendingExits.append(exit);
}
- }
- } finally {
- inits = initsPrev;
- uninits = uninitsPrev;
- nextadr = nextadrPrev;
- firstadr = firstadrPrev;
- caught = caughtPrev;
- lint = lintPrev;
- }
- }
-
- public void visitVarDef(JCVariableDecl tree) {
- boolean track = trackable(tree.sym);
- if (track && tree.sym.owner.kind == MTH) newVar(tree.sym);
- if (tree.init != null) {
- Lint lintPrev = lint;
- lint = lint.augment(tree.sym.attributes_field);
- try{
- scanExpr(tree.init);
- if (track) letInit(tree.pos(), tree.sym);
} finally {
+ inits = initsPrev;
+ uninits = uninitsPrev;
+ nextadr = nextadrPrev;
+ firstadr = firstadrPrev;
lint = lintPrev;
}
}
- }
-
- public void visitBlock(JCBlock tree) {
- int nextadrPrev = nextadr;
- scanStats(tree.stats);
- nextadr = nextadrPrev;
- }
- public void visitDoLoop(JCDoWhileLoop tree) {
- ListBuffer<PendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
- pendingExits = new ListBuffer<PendingExit>();
- int prevErrors = log.nerrors;
- do {
- Bits uninitsEntry = uninits.dup();
- uninitsEntry.excludeFrom(nextadr);
- scanStat(tree.body);
- alive |= resolveContinues(tree);
- scanCond(tree.cond);
- if (log.nerrors != prevErrors ||
- loopPassTwo ||
- uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
- break;
- inits = initsWhenTrue;
- uninits = uninitsEntry.andSet(uninitsWhenTrue);
- loopPassTwo = true;
- alive = true;
- } while (true);
- loopPassTwo = prevLoopPassTwo;
- inits = initsWhenFalse;
- uninits = uninitsWhenFalse;
- alive = alive && !tree.cond.type.isTrue();
- alive |= resolveBreaks(tree, prevPendingExits);
- }
+ public void visitVarDef(JCVariableDecl tree) {
+ boolean track = trackable(tree.sym);
+ if (track && tree.sym.owner.kind == MTH) newVar(tree.sym);
+ if (tree.init != null) {
+ Lint lintPrev = lint;
+ lint = lint.augment(tree.sym.attributes_field);
+ try{
+ scanExpr(tree.init);
+ if (track) letInit(tree.pos(), tree.sym);
+ } finally {
+ lint = lintPrev;
+ }
+ }
+ }
+
+ public void visitBlock(JCBlock tree) {
+ int nextadrPrev = nextadr;
+ scan(tree.stats);
+ nextadr = nextadrPrev;
+ }
- public void visitWhileLoop(JCWhileLoop tree) {
- ListBuffer<PendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
- Bits initsCond;
- Bits uninitsCond;
- pendingExits = new ListBuffer<PendingExit>();
- int prevErrors = log.nerrors;
- do {
- Bits uninitsEntry = uninits.dup();
- uninitsEntry.excludeFrom(nextadr);
- scanCond(tree.cond);
- initsCond = initsWhenFalse;
- uninitsCond = uninitsWhenFalse;
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
- alive = !tree.cond.type.isFalse();
- scanStat(tree.body);
- alive |= resolveContinues(tree);
- if (log.nerrors != prevErrors ||
- loopPassTwo ||
- uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
- break;
- uninits = uninitsEntry.andSet(uninits);
- loopPassTwo = true;
- alive = true;
- } while (true);
- loopPassTwo = prevLoopPassTwo;
- inits = initsCond;
- uninits = uninitsCond;
- alive = resolveBreaks(tree, prevPendingExits) ||
- !tree.cond.type.isTrue();
- }
+ public void visitDoLoop(JCDoWhileLoop tree) {
+ ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+ boolean prevLoopPassTwo = loopPassTwo;
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ int prevErrors = log.nerrors;
+ do {
+ Bits uninitsEntry = uninits.dup();
+ uninitsEntry.excludeFrom(nextadr);
+ scan(tree.body);
+ resolveContinues(tree);
+ scanCond(tree.cond);
+ if (log.nerrors != prevErrors ||
+ loopPassTwo ||
+ uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
+ break;
+ inits = initsWhenTrue;
+ uninits = uninitsEntry.andSet(uninitsWhenTrue);
+ loopPassTwo = true;
+ } while (true);
+ loopPassTwo = prevLoopPassTwo;
+ inits = initsWhenFalse;
+ uninits = uninitsWhenFalse;
+ resolveBreaks(tree, prevPendingExits);
+ }
- public void visitForLoop(JCForLoop tree) {
- ListBuffer<PendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
- int nextadrPrev = nextadr;
- scanStats(tree.init);
- Bits initsCond;
- Bits uninitsCond;
- pendingExits = new ListBuffer<PendingExit>();
- int prevErrors = log.nerrors;
- do {
- Bits uninitsEntry = uninits.dup();
- uninitsEntry.excludeFrom(nextadr);
- if (tree.cond != null) {
+ public void visitWhileLoop(JCWhileLoop tree) {
+ ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+ boolean prevLoopPassTwo = loopPassTwo;
+ Bits initsCond;
+ Bits uninitsCond;
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ int prevErrors = log.nerrors;
+ do {
+ Bits uninitsEntry = uninits.dup();
+ uninitsEntry.excludeFrom(nextadr);
scanCond(tree.cond);
initsCond = initsWhenFalse;
uninitsCond = uninitsWhenFalse;
inits = initsWhenTrue;
uninits = uninitsWhenTrue;
- alive = !tree.cond.type.isFalse();
- } else {
- initsCond = inits.dup();
- initsCond.inclRange(firstadr, nextadr);
- uninitsCond = uninits.dup();
- uninitsCond.inclRange(firstadr, nextadr);
- alive = true;
- }
- scanStat(tree.body);
- alive |= resolveContinues(tree);
- scan(tree.step);
- if (log.nerrors != prevErrors ||
- loopPassTwo ||
- uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
- break;
- uninits = uninitsEntry.andSet(uninits);
- loopPassTwo = true;
- alive = true;
- } while (true);
- loopPassTwo = prevLoopPassTwo;
- inits = initsCond;
- uninits = uninitsCond;
- alive = resolveBreaks(tree, prevPendingExits) ||
- tree.cond != null && !tree.cond.type.isTrue();
- nextadr = nextadrPrev;
- }
+ scan(tree.body);
+ resolveContinues(tree);
+ if (log.nerrors != prevErrors ||
+ loopPassTwo ||
+ uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
+ break;
+ uninits = uninitsEntry.andSet(uninits);
+ loopPassTwo = true;
+ } while (true);
+ loopPassTwo = prevLoopPassTwo;
+ inits = initsCond;
+ uninits = uninitsCond;
+ resolveBreaks(tree, prevPendingExits);
+ }
- public void visitForeachLoop(JCEnhancedForLoop tree) {
- visitVarDef(tree.var);
+ public void visitForLoop(JCForLoop tree) {
+ ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+ boolean prevLoopPassTwo = loopPassTwo;
+ int nextadrPrev = nextadr;
+ scan(tree.init);
+ Bits initsCond;
+ Bits uninitsCond;
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ int prevErrors = log.nerrors;
+ do {
+ Bits uninitsEntry = uninits.dup();
+ uninitsEntry.excludeFrom(nextadr);
+ if (tree.cond != null) {
+ scanCond(tree.cond);
+ initsCond = initsWhenFalse;
+ uninitsCond = uninitsWhenFalse;
+ inits = initsWhenTrue;
+ uninits = uninitsWhenTrue;
+ } else {
+ initsCond = inits.dup();
+ initsCond.inclRange(firstadr, nextadr);
+ uninitsCond = uninits.dup();
+ uninitsCond.inclRange(firstadr, nextadr);
+ }
+ scan(tree.body);
+ resolveContinues(tree);
+ scan(tree.step);
+ if (log.nerrors != prevErrors ||
+ loopPassTwo ||
+ uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
+ break;
+ uninits = uninitsEntry.andSet(uninits);
+ loopPassTwo = true;
+ } while (true);
+ loopPassTwo = prevLoopPassTwo;
+ inits = initsCond;
+ uninits = uninitsCond;
+ resolveBreaks(tree, prevPendingExits);
+ nextadr = nextadrPrev;
+ }
- ListBuffer<PendingExit> prevPendingExits = pendingExits;
- boolean prevLoopPassTwo = loopPassTwo;
- int nextadrPrev = nextadr;
- scan(tree.expr);
- Bits initsStart = inits.dup();
- Bits uninitsStart = uninits.dup();
+ public void visitForeachLoop(JCEnhancedForLoop tree) {
+ visitVarDef(tree.var);
+
+ ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+ boolean prevLoopPassTwo = loopPassTwo;
+ int nextadrPrev = nextadr;
+ scan(tree.expr);
+ Bits initsStart = inits.dup();
+ Bits uninitsStart = uninits.dup();
+
+ letInit(tree.pos(), tree.var.sym);
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ int prevErrors = log.nerrors;
+ do {
+ Bits uninitsEntry = uninits.dup();
+ uninitsEntry.excludeFrom(nextadr);
+ scan(tree.body);
+ resolveContinues(tree);
+ if (log.nerrors != prevErrors ||
+ loopPassTwo ||
+ uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
+ break;
+ uninits = uninitsEntry.andSet(uninits);
+ loopPassTwo = true;
+ } while (true);
+ loopPassTwo = prevLoopPassTwo;
+ inits = initsStart;
+ uninits = uninitsStart.andSet(uninits);
+ resolveBreaks(tree, prevPendingExits);
+ nextadr = nextadrPrev;
+ }
+
+ public void visitLabelled(JCLabeledStatement tree) {
+ ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ scan(tree.body);
+ resolveBreaks(tree, prevPendingExits);
+ }
- letInit(tree.pos(), tree.var.sym);
- pendingExits = new ListBuffer<PendingExit>();
- int prevErrors = log.nerrors;
- do {
- Bits uninitsEntry = uninits.dup();
- uninitsEntry.excludeFrom(nextadr);
- scanStat(tree.body);
- alive |= resolveContinues(tree);
- if (log.nerrors != prevErrors ||
- loopPassTwo ||
- uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
- break;
- uninits = uninitsEntry.andSet(uninits);
- loopPassTwo = true;
- alive = true;
- } while (true);
- loopPassTwo = prevLoopPassTwo;
- inits = initsStart;
- uninits = uninitsStart.andSet(uninits);
- resolveBreaks(tree, prevPendingExits);
- alive = true;
- nextadr = nextadrPrev;
- }
+ public void visitSwitch(JCSwitch tree) {
+ ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ int nextadrPrev = nextadr;
+ scanExpr(tree.selector);
+ Bits initsSwitch = inits;
+ Bits uninitsSwitch = uninits.dup();
+ boolean hasDefault = false;
+ for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
+ inits = initsSwitch.dup();
+ uninits = uninits.andSet(uninitsSwitch);
+ JCCase c = l.head;
+ if (c.pat == null)
+ hasDefault = true;
+ else
+ scanExpr(c.pat);
+ scan(c.stats);
+ addVars(c.stats, initsSwitch, uninitsSwitch);
+ // Warn about fall-through if lint switch fallthrough enabled.
+ }
+ if (!hasDefault) {
+ inits.andSet(initsSwitch);
+ }
+ resolveBreaks(tree, prevPendingExits);
+ nextadr = nextadrPrev;
+ }
+ // where
+ /** Add any variables defined in stats to inits and uninits. */
+ private void addVars(List<JCStatement> stats, Bits inits,
+ Bits uninits) {
+ for (;stats.nonEmpty(); stats = stats.tail) {
+ JCTree stat = stats.head;
+ if (stat.hasTag(VARDEF)) {
+ int adr = ((JCVariableDecl) stat).sym.adr;
+ inits.excl(adr);
+ uninits.incl(adr);
+ }
+ }
+ }
- public void visitLabelled(JCLabeledStatement tree) {
- ListBuffer<PendingExit> prevPendingExits = pendingExits;
- pendingExits = new ListBuffer<PendingExit>();
- scanStat(tree.body);
- alive |= resolveBreaks(tree, prevPendingExits);
- }
+ public void visitTry(JCTry tree) {
+ ListBuffer<JCVariableDecl> resourceVarDecls = ListBuffer.lb();
+ Bits uninitsTryPrev = uninitsTry;
+ ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ Bits initsTry = inits.dup();
+ uninitsTry = uninits.dup();
+ for (JCTree resource : tree.resources) {
+ if (resource instanceof JCVariableDecl) {
+ JCVariableDecl vdecl = (JCVariableDecl) resource;
+ visitVarDef(vdecl);
+ unrefdResources.enter(vdecl.sym);
+ resourceVarDecls.append(vdecl);
+ } else if (resource instanceof JCExpression) {
+ scanExpr((JCExpression) resource);
+ } else {
+ throw new AssertionError(tree); // parser error
+ }
+ }
+ scan(tree.body);
+ uninitsTry.andSet(uninits);
+ Bits initsEnd = inits;
+ Bits uninitsEnd = uninits;
+ int nextadrCatch = nextadr;
+
+ if (!resourceVarDecls.isEmpty() &&
+ lint.isEnabled(Lint.LintCategory.TRY)) {
+ for (JCVariableDecl resVar : resourceVarDecls) {
+ if (unrefdResources.includes(resVar.sym)) {
+ log.warning(Lint.LintCategory.TRY, resVar.pos(),
+ "try.resource.not.referenced", resVar.sym);
+ unrefdResources.remove(resVar.sym);
+ }
+ }
+ }
- public void visitSwitch(JCSwitch tree) {
- ListBuffer<PendingExit> prevPendingExits = pendingExits;
- pendingExits = new ListBuffer<PendingExit>();
- int nextadrPrev = nextadr;
- scanExpr(tree.selector);
- Bits initsSwitch = inits;
- Bits uninitsSwitch = uninits.dup();
- boolean hasDefault = false;
- for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
- alive = true;
- inits = initsSwitch.dup();
- uninits = uninits.andSet(uninitsSwitch);
- JCCase c = l.head;
- if (c.pat == null)
- hasDefault = true;
- else
- scanExpr(c.pat);
- scanStats(c.stats);
- addVars(c.stats, initsSwitch, uninitsSwitch);
- // Warn about fall-through if lint switch fallthrough enabled.
- if (!loopPassTwo &&
- alive &&
- lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
- c.stats.nonEmpty() && l.tail.nonEmpty())
- log.warning(Lint.LintCategory.FALLTHROUGH,
- l.tail.head.pos(),
- "possible.fall-through.into.case");
+ for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
+ JCVariableDecl param = l.head.param;
+ inits = initsTry.dup();
+ uninits = uninitsTry.dup();
+ scan(param);
+ inits.incl(param.sym.adr);
+ uninits.excl(param.sym.adr);
+ scan(l.head.body);
+ initsEnd.andSet(inits);
+ uninitsEnd.andSet(uninits);
+ nextadr = nextadrCatch;
+ }
+ if (tree.finalizer != null) {
+ inits = initsTry.dup();
+ uninits = uninitsTry.dup();
+ ListBuffer<AssignPendingExit> exits = pendingExits;
+ pendingExits = prevPendingExits;
+ scan(tree.finalizer);
+ if (!tree.finallyCanCompleteNormally) {
+ // discard exits and exceptions from try and finally
+ } else {
+ uninits.andSet(uninitsEnd);
+ // FIX: this doesn't preserve source order of exits in catch
+ // versus finally!
+ while (exits.nonEmpty()) {
+ AssignPendingExit exit = exits.next();
+ if (exit.exit_inits != null) {
+ exit.exit_inits.orSet(inits);
+ exit.exit_uninits.andSet(uninits);
+ }
+ pendingExits.append(exit);
+ }
+ inits.orSet(initsEnd);
+ }
+ } else {
+ inits = initsEnd;
+ uninits = uninitsEnd;
+ ListBuffer<AssignPendingExit> exits = pendingExits;
+ pendingExits = prevPendingExits;
+ while (exits.nonEmpty()) pendingExits.append(exits.next());
+ }
+ uninitsTry.andSet(uninitsTryPrev).andSet(uninits);
}
- if (!hasDefault) {
- inits.andSet(initsSwitch);
- alive = true;
- }
- alive |= resolveBreaks(tree, prevPendingExits);
- nextadr = nextadrPrev;
- }
- // where
- /** Add any variables defined in stats to inits and uninits. */
- private static void addVars(List<JCStatement> stats, Bits inits,
- Bits uninits) {
- for (;stats.nonEmpty(); stats = stats.tail) {
- JCTree stat = stats.head;
- if (stat.hasTag(VARDEF)) {
- int adr = ((JCVariableDecl) stat).sym.adr;
- inits.excl(adr);
- uninits.incl(adr);
- }
+
+ public void visitConditional(JCConditional tree) {
+ scanCond(tree.cond);
+ Bits initsBeforeElse = initsWhenFalse;
+ Bits uninitsBeforeElse = uninitsWhenFalse;
+ inits = initsWhenTrue;
+ uninits = uninitsWhenTrue;
+ if (tree.truepart.type.tag == BOOLEAN &&
+ tree.falsepart.type.tag == BOOLEAN) {
+ // if b and c are boolean valued, then
+ // v is (un)assigned after a?b:c when true iff
+ // v is (un)assigned after b when true and
+ // v is (un)assigned after c when true
+ scanCond(tree.truepart);
+ Bits initsAfterThenWhenTrue = initsWhenTrue.dup();
+ Bits initsAfterThenWhenFalse = initsWhenFalse.dup();
+ Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup();
+ Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup();
+ inits = initsBeforeElse;
+ uninits = uninitsBeforeElse;
+ scanCond(tree.falsepart);
+ initsWhenTrue.andSet(initsAfterThenWhenTrue);
+ initsWhenFalse.andSet(initsAfterThenWhenFalse);
+ uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue);
+ uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse);
+ } else {
+ scanExpr(tree.truepart);
+ Bits initsAfterThen = inits.dup();
+ Bits uninitsAfterThen = uninits.dup();
+ inits = initsBeforeElse;
+ uninits = uninitsBeforeElse;
+ scanExpr(tree.falsepart);
+ inits.andSet(initsAfterThen);
+ uninits.andSet(uninitsAfterThen);
}
}
- public void visitTry(JCTry tree) {
- List<Type> caughtPrev = caught;
- List<Type> thrownPrev = thrown;
- thrown = List.nil();
- for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
- List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
- ((JCTypeUnion)l.head.param.vartype).alternatives :
- List.of(l.head.param.vartype);
- for (JCExpression ct : subClauses) {
- caught = chk.incl(ct.type, caught);
- }
- }
- ListBuffer<JCVariableDecl> resourceVarDecls = ListBuffer.lb();
- Bits uninitsTryPrev = uninitsTry;
- ListBuffer<PendingExit> prevPendingExits = pendingExits;
- pendingExits = new ListBuffer<PendingExit>();
- Bits initsTry = inits.dup();
- uninitsTry = uninits.dup();
- for (JCTree resource : tree.resources) {
- if (resource instanceof JCVariableDecl) {
- JCVariableDecl vdecl = (JCVariableDecl) resource;
- visitVarDef(vdecl);
- unrefdResources.enter(vdecl.sym);
- resourceVarDecls.append(vdecl);
- } else if (resource instanceof JCExpression) {
- scanExpr((JCExpression) resource);
+ public void visitIf(JCIf tree) {
+ scanCond(tree.cond);
+ Bits initsBeforeElse = initsWhenFalse;
+ Bits uninitsBeforeElse = uninitsWhenFalse;
+ inits = initsWhenTrue;
+ uninits = uninitsWhenTrue;
+ scan(tree.thenpart);
+ if (tree.elsepart != null) {
+ Bits initsAfterThen = inits.dup();
+ Bits uninitsAfterThen = uninits.dup();
+ inits = initsBeforeElse;
+ uninits = uninitsBeforeElse;
+ scan(tree.elsepart);
+ inits.andSet(initsAfterThen);
+ uninits.andSet(uninitsAfterThen);
} else {
- throw new AssertionError(tree); // parser error
- }
- }
- for (JCTree resource : tree.resources) {
- List<Type> closeableSupertypes = resource.type.isCompound() ?
- types.interfaces(resource.type).prepend(types.supertype(resource.type)) :
- List.of(resource.type);
- for (Type sup : closeableSupertypes) {
- if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) {
- Symbol closeMethod = rs.resolveQualifiedMethod(tree,
- attrEnv,
- sup,
- names.close,
- List.<Type>nil(),
- List.<Type>nil());
- if (closeMethod.kind == MTH) {
- for (Type t : ((MethodSymbol)closeMethod).getThrownTypes()) {
- markThrown(resource, t);
- }
- }
- }
- }
- }
- scanStat(tree.body);
- List<Type> thrownInTry = allowImprovedCatchAnalysis ?
- chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) :
- thrown;
- thrown = thrownPrev;
- caught = caughtPrev;
- boolean aliveEnd = alive;
- uninitsTry.andSet(uninits);
- Bits initsEnd = inits;
- Bits uninitsEnd = uninits;
- int nextadrCatch = nextadr;
-
- if (!resourceVarDecls.isEmpty() &&
- lint.isEnabled(Lint.LintCategory.TRY)) {
- for (JCVariableDecl resVar : resourceVarDecls) {
- if (unrefdResources.includes(resVar.sym)) {
- log.warning(Lint.LintCategory.TRY, resVar.pos(),
- "try.resource.not.referenced", resVar.sym);
- unrefdResources.remove(resVar.sym);
- }
+ inits.andSet(initsBeforeElse);
+ uninits.andSet(uninitsBeforeElse);
}
}
- List<Type> caughtInTry = List.nil();
- for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
- alive = true;
- JCVariableDecl param = l.head.param;
- List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
- ((JCTypeUnion)l.head.param.vartype).alternatives :
- List.of(l.head.param.vartype);
- List<Type> ctypes = List.nil();
- List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry);
- for (JCExpression ct : subClauses) {
- Type exc = ct.type;
- if (exc != syms.unknownType) {
- ctypes = ctypes.append(exc);
- if (types.isSameType(exc, syms.objectType))
- continue;
- checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry);
- caughtInTry = chk.incl(exc, caughtInTry);
- }
+ public void visitBreak(JCBreak tree) {
+ recordExit(tree, new AssignPendingExit(tree, inits, uninits));
+ }
+
+ public void visitContinue(JCContinue tree) {
+ recordExit(tree, new AssignPendingExit(tree, inits, uninits));
+ }
+
+ public void visitReturn(JCReturn tree) {
+ scanExpr(tree.expr);
+ // if not initial constructor, should markDead instead of recordExit
+ recordExit(tree, new AssignPendingExit(tree, inits, uninits));
+ }
+
+ public void visitThrow(JCThrow tree) {
+ scanExpr(tree.expr);
+ markDead();
+ }
+
+ public void visitApply(JCMethodInvocation tree) {
+ scanExpr(tree.meth);
+ scanExprs(tree.args);
+ }
+
+ public void visitNewClass(JCNewClass tree) {
+ scanExpr(tree.encl);
+ scanExprs(tree.args);
+ scan(tree.def);
+ }
+
+ public void visitNewArray(JCNewArray tree) {
+ scanExprs(tree.dims);
+ scanExprs(tree.elems);
+ }
+
+ public void visitAssert(JCAssert tree) {
+ Bits initsExit = inits.dup();
+ Bits uninitsExit = uninits.dup();
+ scanCond(tree.cond);
+ uninitsExit.andSet(uninitsWhenTrue);
+ if (tree.detail != null) {
+ inits = initsWhenFalse;
+ uninits = uninitsWhenFalse;
+ scanExpr(tree.detail);
}
- inits = initsTry.dup();
- uninits = uninitsTry.dup();
- scan(param);
- inits.incl(param.sym.adr);
- uninits.excl(param.sym.adr);
- preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes));
- scanStat(l.head.body);
- initsEnd.andSet(inits);
- uninitsEnd.andSet(uninits);
- nextadr = nextadrCatch;
- preciseRethrowTypes.remove(param.sym);
- aliveEnd |= alive;
+ inits = initsExit;
+ uninits = uninitsExit;
+ }
+
+ public void visitAssign(JCAssign tree) {
+ JCTree lhs = TreeInfo.skipParens(tree.lhs);
+ if (!(lhs instanceof JCIdent)) scanExpr(lhs);
+ scanExpr(tree.rhs);
+ letInit(lhs);
+ }
+
+ public void visitAssignop(JCAssignOp tree) {
+ scanExpr(tree.lhs);
+ scanExpr(tree.rhs);
+ letInit(tree.lhs);
+ }
+
+ public void visitUnary(JCUnary tree) {
+ switch (tree.getTag()) {
+ case NOT:
+ scanCond(tree.arg);
+ Bits t = initsWhenFalse;
+ initsWhenFalse = initsWhenTrue;
+ initsWhenTrue = t;
+ t = uninitsWhenFalse;
+ uninitsWhenFalse = uninitsWhenTrue;
+ uninitsWhenTrue = t;
+ break;
+ case PREINC: case POSTINC:
+ case PREDEC: case POSTDEC:
+ scanExpr(tree.arg);
+ letInit(tree.arg);
+ break;
+ default:
+ scanExpr(tree.arg);
+ }
}
- if (tree.finalizer != null) {
- List<Type> savedThrown = thrown;
- thrown = List.nil();
- inits = initsTry.dup();
- uninits = uninitsTry.dup();
- ListBuffer<PendingExit> exits = pendingExits;
- pendingExits = prevPendingExits;
- alive = true;
- scanStat(tree.finalizer);
- if (!alive) {
- // discard exits and exceptions from try and finally
- thrown = chk.union(thrown, thrownPrev);
- if (!loopPassTwo &&
- lint.isEnabled(Lint.LintCategory.FINALLY)) {
- log.warning(Lint.LintCategory.FINALLY,
- TreeInfo.diagEndPos(tree.finalizer),
- "finally.cannot.complete");
- }
- } else {
- thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
- thrown = chk.union(thrown, savedThrown);
- uninits.andSet(uninitsEnd);
- // FIX: this doesn't preserve source order of exits in catch
- // versus finally!
- while (exits.nonEmpty()) {
- PendingExit exit = exits.next();
- if (exit.inits != null) {
- exit.inits.orSet(inits);
- exit.uninits.andSet(uninits);
- }
- pendingExits.append(exit);
- }
- inits.orSet(initsEnd);
- alive = aliveEnd;
+
+ public void visitBinary(JCBinary tree) {
+ switch (tree.getTag()) {
+ case AND:
+ scanCond(tree.lhs);
+ Bits initsWhenFalseLeft = initsWhenFalse;
+ Bits uninitsWhenFalseLeft = uninitsWhenFalse;
+ inits = initsWhenTrue;
+ uninits = uninitsWhenTrue;
+ scanCond(tree.rhs);
+ initsWhenFalse.andSet(initsWhenFalseLeft);
+ uninitsWhenFalse.andSet(uninitsWhenFalseLeft);
+ break;
+ case OR:
+ scanCond(tree.lhs);
+ Bits initsWhenTrueLeft = initsWhenTrue;
+ Bits uninitsWhenTrueLeft = uninitsWhenTrue;
+ inits = initsWhenFalse;
+ uninits = uninitsWhenFalse;
+ scanCond(tree.rhs);
+ initsWhenTrue.andSet(initsWhenTrueLeft);
+ uninitsWhenTrue.andSet(uninitsWhenTrueLeft);
+ break;
+ default:
+ scanExpr(tree.lhs);
+ scanExpr(tree.rhs);
+ }
+ }
+
+ public void visitIdent(JCIdent tree) {
+ if (tree.sym.kind == VAR) {
+ checkInit(tree.pos(), (VarSymbol)tree.sym);
+ referenced(tree.sym);
}
- } else {
- thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry));
- inits = initsEnd;
- uninits = uninitsEnd;
- alive = aliveEnd;
- ListBuffer<PendingExit> exits = pendingExits;
- pendingExits = prevPendingExits;
- while (exits.nonEmpty()) pendingExits.append(exits.next());
+ }
+
+ void referenced(Symbol sym) {
+ unrefdResources.remove(sym);
}
- uninitsTry.andSet(uninitsTryPrev).andSet(uninits);
- }
+
+ public void visitTopLevel(JCCompilationUnit tree) {
+ // Do nothing for TopLevel since each class is visited individually
+ }
+
+ /**************************************************************************
+ * main method
+ *************************************************************************/
- void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
- if (chk.subset(exc, caughtInTry)) {
- log.error(pos, "except.already.caught", exc);
- } else if (!chk.isUnchecked(pos, exc) &&
- !isExceptionOrThrowable(exc) &&
- !chk.intersects(exc, thrownInTry)) {
- log.error(pos, "except.never.thrown.in.try", exc);
- } else if (allowImprovedCatchAnalysis) {
- List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
- // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
- // unchecked exception, the result list would not be empty, as the augmented
- // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
- // exception, that would have been covered in the branch above
- if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
- !isExceptionOrThrowable(exc)) {
- String key = catchableThrownTypes.length() == 1 ?
- "unreachable.catch" :
- "unreachable.catch.1";
- log.warning(pos, key, catchableThrownTypes);
+ /** Perform definite assignment/unassignment analysis on a tree.
+ */
+ public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
+ try {
+ attrEnv = env;
+ JCTree tree = env.tree;
+ Flow.this.make = make;
+ inits = new Bits();
+ uninits = new Bits();
+ uninitsTry = new Bits();
+ initsWhenTrue = initsWhenFalse =
+ uninitsWhenTrue = uninitsWhenFalse = null;
+ if (vars == null)
+ vars = new VarSymbol[32];
+ else
+ for (int i=0; i<vars.length; i++)
+ vars[i] = null;
+ firstadr = 0;
+ nextadr = 0;
+ pendingExits = new ListBuffer<AssignPendingExit>();
+ this.classDef = null;
+ unrefdResources = new Scope(env.enclClass.sym);
+ scan(tree);
+ } finally {
+ // note that recursive invocations of this method fail hard
+ inits = uninits = uninitsTry = null;
+ initsWhenTrue = initsWhenFalse =
+ uninitsWhenTrue = uninitsWhenFalse = null;
+ if (vars != null) for (int i=0; i<vars.length; i++)
+ vars[i] = null;
+ firstadr = 0;
+ nextadr = 0;
+ pendingExits = null;
+ Flow.this.make = null;
+ this.classDef = null;
+ unrefdResources = null;
}
}
}
- //where
- private boolean isExceptionOrThrowable(Type exc) {
- return exc.tsym == syms.throwableType.tsym ||
- exc.tsym == syms.exceptionType.tsym;
- }
-
-
- public void visitConditional(JCConditional tree) {
- scanCond(tree.cond);
- Bits initsBeforeElse = initsWhenFalse;
- Bits uninitsBeforeElse = uninitsWhenFalse;
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
- if (tree.truepart.type.tag == BOOLEAN &&
- tree.falsepart.type.tag == BOOLEAN) {
- // if b and c are boolean valued, then
- // v is (un)assigned after a?b:c when true iff
- // v is (un)assigned after b when true and
- // v is (un)assigned after c when true
- scanCond(tree.truepart);
- Bits initsAfterThenWhenTrue = initsWhenTrue.dup();
- Bits initsAfterThenWhenFalse = initsWhenFalse.dup();
- Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup();
- Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup();
- inits = initsBeforeElse;
- uninits = uninitsBeforeElse;
- scanCond(tree.falsepart);
- initsWhenTrue.andSet(initsAfterThenWhenTrue);
- initsWhenFalse.andSet(initsAfterThenWhenFalse);
- uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue);
- uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse);
- } else {
- scanExpr(tree.truepart);
- Bits initsAfterThen = inits.dup();
- Bits uninitsAfterThen = uninits.dup();
- inits = initsBeforeElse;
- uninits = uninitsBeforeElse;
- scanExpr(tree.falsepart);
- inits.andSet(initsAfterThen);
- uninits.andSet(uninitsAfterThen);
- }
- }
-
- public void visitIf(JCIf tree) {
- scanCond(tree.cond);
- Bits initsBeforeElse = initsWhenFalse;
- Bits uninitsBeforeElse = uninitsWhenFalse;
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
- scanStat(tree.thenpart);
- if (tree.elsepart != null) {
- boolean aliveAfterThen = alive;
- alive = true;
- Bits initsAfterThen = inits.dup();
- Bits uninitsAfterThen = uninits.dup();
- inits = initsBeforeElse;
- uninits = uninitsBeforeElse;
- scanStat(tree.elsepart);
- inits.andSet(initsAfterThen);
- uninits.andSet(uninitsAfterThen);
- alive = alive | aliveAfterThen;
- } else {
- inits.andSet(initsBeforeElse);
- uninits.andSet(uninitsBeforeElse);
- alive = true;
- }
- }
-
-
-
- public void visitBreak(JCBreak tree) {
- recordExit(tree);
- }
-
- public void visitContinue(JCContinue tree) {
- recordExit(tree);
- }
-
- public void visitReturn(JCReturn tree) {
- scanExpr(tree.expr);
- // if not initial constructor, should markDead instead of recordExit
- recordExit(tree);
- }
-
- public void visitThrow(JCThrow tree) {
- scanExpr(tree.expr);
- Symbol sym = TreeInfo.symbol(tree.expr);
- if (sym != null &&
- sym.kind == VAR &&
- (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
- preciseRethrowTypes.get(sym) != null &&
- allowImprovedRethrowAnalysis) {
- for (Type t : preciseRethrowTypes.get(sym)) {
- markThrown(tree, t);
- }
- }
- else {
- markThrown(tree, tree.expr.type);
- }
- markDead();
- }
-
- public void visitApply(JCMethodInvocation tree) {
- scanExpr(tree.meth);
- scanExprs(tree.args);
- for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
- markThrown(tree, l.head);
- }
-
- public void visitNewClass(JCNewClass tree) {
- scanExpr(tree.encl);
- scanExprs(tree.args);
- // scan(tree.def);
- for (List<Type> l = tree.constructorType.getThrownTypes();
- l.nonEmpty();
- l = l.tail) {
- markThrown(tree, l.head);
- }
- List<Type> caughtPrev = caught;
- try {
- // If the new class expression defines an anonymous class,
- // analysis of the anonymous constructor may encounter thrown
- // types which are unsubstituted type variables.
- // However, since the constructor's actual thrown types have
- // already been marked as thrown, it is safe to simply include
- // each of the constructor's formal thrown types in the set of
- // 'caught/declared to be thrown' types, for the duration of
- // the class def analysis.
- if (tree.def != null)
- for (List<Type> l = tree.constructor.type.getThrownTypes();
- l.nonEmpty();
- l = l.tail) {
- caught = chk.incl(l.head, caught);
- }
- scan(tree.def);
- }
- finally {
- caught = caughtPrev;
- }
- }
-
- public void visitNewArray(JCNewArray tree) {
- scanExprs(tree.dims);
- scanExprs(tree.elems);
- }
-
- public void visitAssert(JCAssert tree) {
- Bits initsExit = inits.dup();
- Bits uninitsExit = uninits.dup();
- scanCond(tree.cond);
- uninitsExit.andSet(uninitsWhenTrue);
- if (tree.detail != null) {
- inits = initsWhenFalse;
- uninits = uninitsWhenFalse;
- scanExpr(tree.detail);
- }
- inits = initsExit;
- uninits = uninitsExit;
- }
-
- public void visitAssign(JCAssign tree) {
- JCTree lhs = TreeInfo.skipParens(tree.lhs);
- if (!(lhs instanceof JCIdent)) scanExpr(lhs);
- scanExpr(tree.rhs);
- letInit(lhs);
- }
-
- public void visitAssignop(JCAssignOp tree) {
- scanExpr(tree.lhs);
- scanExpr(tree.rhs);
- letInit(tree.lhs);
- }
-
- public void visitUnary(JCUnary tree) {
- switch (tree.getTag()) {
- case NOT:
- scanCond(tree.arg);
- Bits t = initsWhenFalse;
- initsWhenFalse = initsWhenTrue;
- initsWhenTrue = t;
- t = uninitsWhenFalse;
- uninitsWhenFalse = uninitsWhenTrue;
- uninitsWhenTrue = t;
- break;
- case PREINC: case POSTINC:
- case PREDEC: case POSTDEC:
- scanExpr(tree.arg);
- letInit(tree.arg);
- break;
- default:
- scanExpr(tree.arg);
- }
- }
-
- public void visitBinary(JCBinary tree) {
- switch (tree.getTag()) {
- case AND:
- scanCond(tree.lhs);
- Bits initsWhenFalseLeft = initsWhenFalse;
- Bits uninitsWhenFalseLeft = uninitsWhenFalse;
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
- scanCond(tree.rhs);
- initsWhenFalse.andSet(initsWhenFalseLeft);
- uninitsWhenFalse.andSet(uninitsWhenFalseLeft);
- break;
- case OR:
- scanCond(tree.lhs);
- Bits initsWhenTrueLeft = initsWhenTrue;
- Bits uninitsWhenTrueLeft = uninitsWhenTrue;
- inits = initsWhenFalse;
- uninits = uninitsWhenFalse;
- scanCond(tree.rhs);
- initsWhenTrue.andSet(initsWhenTrueLeft);
- uninitsWhenTrue.andSet(uninitsWhenTrueLeft);
- break;
- default:
- scanExpr(tree.lhs);
- scanExpr(tree.rhs);
- }
- }
-
- public void visitIdent(JCIdent tree) {
- if (tree.sym.kind == VAR) {
- checkInit(tree.pos(), (VarSymbol)tree.sym);
- referenced(tree.sym);
- }
- }
-
- void referenced(Symbol sym) {
- unrefdResources.remove(sym);
- }
-
- public void visitTypeCast(JCTypeCast tree) {
- super.visitTypeCast(tree);
- if (!tree.type.isErroneous()
- && lint.isEnabled(Lint.LintCategory.CAST)
- && types.isSameType(tree.expr.type, tree.clazz.type)
- && !is292targetTypeCast(tree)) {
- log.warning(Lint.LintCategory.CAST,
- tree.pos(), "redundant.cast", tree.expr.type);
- }
- }
- //where
- private boolean is292targetTypeCast(JCTypeCast tree) {
- boolean is292targetTypeCast = false;
- JCExpression expr = TreeInfo.skipParens(tree.expr);
- if (expr.hasTag(APPLY)) {
- JCMethodInvocation apply = (JCMethodInvocation)expr;
- Symbol sym = TreeInfo.symbol(apply.meth);
- is292targetTypeCast = sym != null &&
- sym.kind == MTH &&
- (sym.flags() & POLYMORPHIC_SIGNATURE) != 0;
- }
- return is292targetTypeCast;
- }
-
- public void visitTopLevel(JCCompilationUnit tree) {
- // Do nothing for TopLevel since each class is visited individually
- }
-
-/**************************************************************************
- * main method
- *************************************************************************/
-
- /** Perform definite assignment/unassignment analysis on a tree.
- */
- public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
- try {
- attrEnv = env;
- JCTree tree = env.tree;
- this.make = make;
- inits = new Bits();
- uninits = new Bits();
- uninitsTry = new Bits();
- initsWhenTrue = initsWhenFalse =
- uninitsWhenTrue = uninitsWhenFalse = null;
- if (vars == null)
- vars = new VarSymbol[32];
- else
- for (int i=0; i<vars.length; i++)
- vars[i] = null;
- firstadr = 0;
- nextadr = 0;
- pendingExits = new ListBuffer<PendingExit>();
- preciseRethrowTypes = new HashMap<Symbol, List<Type>>();
- alive = true;
- this.thrown = this.caught = null;
- this.classDef = null;
- unrefdResources = new Scope(env.enclClass.sym);
- scan(tree);
- } finally {
- // note that recursive invocations of this method fail hard
- inits = uninits = uninitsTry = null;
- initsWhenTrue = initsWhenFalse =
- uninitsWhenTrue = uninitsWhenFalse = null;
- if (vars != null) for (int i=0; i<vars.length; i++)
- vars[i] = null;
- firstadr = 0;
- nextadr = 0;
- pendingExits = null;
- this.make = null;
- this.thrown = this.caught = null;
- this.classDef = null;
- unrefdResources = null;
- }
- }
}