Flow.java revision 723
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
//todo: one might eliminate uninits.andSets when monotonic
/** This pass implements dataflow analysis for Java programs.
* Liveness analysis checks that every statement is reachable.
* Exception analysis ensures that every checked exception that is
* thrown is declared or caught. Definite assignment analysis
* ensures that each variable is assigned when used. Definite
* unassignment analysis ensures that no final variable is assigned
* more than once.
*
* <p>The second edition of the JLS has a number of problems in the
* specification of these flow analysis problems. This implementation
* attempts to address those issues.
*
* <p>First, there is no accommodation for a finally clause that cannot
* complete normally. For liveness analysis, an intervening finally
* clause can cause a break, continue, or return not to reach its
* target. For exception analysis, an intervening finally clause can
* state to the target. In addition, code in the finally clause can
*
* <p>For try statements, we introduce the idea of a variable being
* definitely unassigned "everywhere" in a block. A variable V is
* "unassigned everywhere" in a block iff it is unassigned at the
* beginning of the block and there is no reachable assignment to V
* in the block. An assignment V=e is reachable iff V is not DA
* after e. Then we can say that V is DU at the beginning of the
* catch block iff V is DU everywhere in the try block. Similarly, V
* is DU at the beginning of the finally block iff V is DU everywhere
* in the try block and in every catch block. Specifically, the
* following bullet is added to 16.2.2
* <pre>
* V is <em>unassigned everywhere</em> in a block if it is
* unassigned before the block and there is no reachable
* assignment to V within the block.
* </pre>
* <p>In 16.2.15, the third bullet (and all of its sub-bullets) for all
* try blocks is changed to
* <pre>
* V is definitely unassigned before a catch block iff V is
* definitely unassigned everywhere in the try block.
* </pre>
* <p>The last bullet (and all of its sub-bullets) for try blocks that
* have a finally block is changed to
* <pre>
* V is definitely unassigned before the finally block iff
* V is definitely unassigned everywhere in the try block
* and everywhere in each catch block of the try statement.
* </pre>
* <p>In addition,
* <pre>
* V is definitely assigned at the end of a constructor iff
* V is definitely assigned after the block that is the body
* of the constructor and V is definitely assigned at every
* return that can return from the constructor.
* </pre>
* <p>In addition, each continue statement with the loop as its target
* is treated as a jump to the end of the loop body, and "intervening"
* finally clauses are treated as follows: V is DA "due to the
* continue" iff V is DA before the continue statement or V is DA at
* the end of any intervening finally block. V is DU "due to the
* continue" iff any intervening finally cannot complete normally or V
* is DU at the end of every intervening finally block. This "due to
* the continue" concept is then used in the spec for the loops.
*
* <p>Similarly, break statements must consider intervening finally
* blocks. For liveness analysis, a break statement for which any
* intervening finally cannot complete normally is not considered to
* cause the target statement to be able to complete normally. Then
* we say V is DA "due to the break" iff V is DA before the break or
* V is DA at the end of any intervening finally block. V is DU "due
* to the break" iff any intervening finally cannot complete normally
* or V is DU at the break and at the end of every intervening
* finally block. (I suspect this latter condition can be
* simplified.) This "due to the break" is then used in the spec for
* all statements that can be "broken".
*
* <p>The return statement is treated similarly. V is DA "due to a
* return statement" iff V is DA before the return statement or V is
* DA at the end of any intervening finally block. Note that we
* don't have to worry about the return expression because this
* concept is only used for construcrors.
*
* <p>There is no spec in JLS2 for when a variable is definitely
* assigned at the end of a constructor, which is needed for final
* fields (8.3.1.2). We implement the rule that V is DA at the end
* of the constructor iff it is DA and the end of the body of the
* constructor and V is DA "due to" every return of the constructor.
*
* <p>Intervening finally blocks similarly affect exception analysis. An
* intervening finally that cannot complete normally allows us to ignore
* an otherwise uncaught exception.
*
* <p>To implement the semantics of intervening finally clauses, all
* nonlocal transfers (break, continue, return, throw, method call that
* can throw a checked exception, and a constructor invocation that can
* thrown a checked exception) are recorded in a queue, and removed
* from the queue when we complete processing the target of the
* nonlocal transfer. This allows us to modify the queue in accordance
* with the above rules when we encounter a finally clause. The only
* exception to this [no pun intended] is that checked exceptions that
* are known to be caught or declared to be caught in the enclosing
* method are not recorded in the queue, but instead are recorded in a
* global variable "Set<Type> thrown" that records the type of all
* exceptions that can be thrown.
*
* <p>Other minor issues the treatment of members of other classes
* (always considered DA except that within an anonymous class
* constructor, where DA status from the enclosing scope is
* preserved), treatment of the case expression (V is DA before the
* case expression iff V is DA after the switch expression),
* treatment of variables declared in a switch block (the implied
* variables defined in a switch block), the treatment of boolean ?:
* expressions (The JLS rules only handle b and c non-boolean; the
* new rule is that if b and c are boolean valued, then V is
*
* <p>There is the remaining question of what syntactic forms constitute a
* reference to a variable. It is conventional to allow this.x on the
* left-hand-side to initialize a final instance field named x, yet
* this.x isn't considered a "use" when appearing on a right-hand-side
* in most implementations. Should parentheses affect what is
* considered a variable reference? The simplest rule would be to
* allow unqualified forms only, parentheses optional, and phase out
* support for assigning to a final field via this.x.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Flow extends TreeScanner {
private final boolean allowRethrowAnalysis;
return instance;
}
}
/** A flag that indicates whether the last statement could
* complete normally.
*/
private boolean alive;
/** The set of definitely assigned variables.
*/
/** The set of definitely unassigned variables.
*/
/** 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.
*/
/** When analyzing a condition, inits and uninits are null.
* Instead we have:
*/
/** A mapping from addresses to variable symbols.
*/
/** The current class being defined.
*/
/** The first variable sequence number in this class definition.
*/
int firstadr;
/** The next available variable sequence number.
*/
int nextadr;
/** The list of possibly thrown declarable exceptions.
*/
/** The list of exceptions that are either caught or declared to be
* thrown.
*/
/** The list of unreferenced automatic resources.
*/
/** Set when processing a loop body the second time for DU analysis. */
boolean loopPassTwo = false;
/*-------------------- Environments ----------------------*/
/** 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 {
}
}
}
/** The currently pending exits that go from current inner blocks
* to an enclosing block, in source order.
*/
/*-------------------- Exceptions ----------------------*/
/** Complain that pending exceptions are not caught.
*/
void errorUncaught() {
? "unreported.exception.default.constructor"
: "unreported.exception.need.to.catch.or.throw",
}
}
/** Record that exception is potentially thrown and check that it
* is caught.
*/
}
}
/*-------------- Processing variables ----------------------*/
* I.e. is symbol either a local or a blank final variable?
*/
return
}
/** 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.
*/
}
nextadr++;
}
/** Record an initialization of a trackable variable.
*/
sym);
}
else {
sym);
}
? "var.might.be.assigned.in.loop"
: "var.might.already.be.assigned",
sym);
// reachable assignment
} else {
//log.rawWarning(pos, "unreachable assignment");//DEBUG
}
}
}
}
/** 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.
*/
}
}
}
/** Check that trackable variable is initialized.
*/
sym);
}
}
/*-------------------- Handling jumps ----------------------*/
/** Record an outward transfer of control. */
markDead();
}
/** Resolve all breaks of this statement. */
boolean result = false;
result = true;
} else {
}
}
return result;
}
/** Resolve all continues of this statement. */
boolean result = false;
result = true;
} else {
}
}
return result;
}
/** Record that statement is unreachable.
*/
void markDead() {
alive = false;
}
*/
if (setToNull)
}
*/
void merge() {
}
/* ************************************************************************
* Visitor methods for statements and definitions
*************************************************************************/
/** Analyze a definition.
*/
"initializer.must.be.able.to.complete.normally");
}
}
/** Analyze a statement. Check that statement is reachable.
*/
}
}
/** Analyze list of statements.
*/
}
/** Analyze an expression. Make sure to set (un)inits rather than
* (un)initsWhenTrue(WhenFalse) on exit.
*/
}
}
/** Analyze a list of expressions.
*/
}
/** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
* rather than (un)inits on exit.
*/
} else {
}
}
/* ------------ Visitor methods for various sorts of trees -------------*/
int firstadrPrev = firstadr;
int nextadrPrev = nextadr;
}
try {
// define all the static fields
}
}
}
// process all the static initializers
}
}
// add intersection of all thrown clauses of initial constructors
// to set of caught exceptions, unless class is anonymous.
boolean firstConstructor = true;
if (firstConstructor) {
firstConstructor = false;
} else {
}
}
}
}
// define all the instance fields
}
}
}
// process all the instance initializers
}
}
// in an anonymous class, add the set of thrown exceptions to
// the throws clause of the synthetic constructor and propagate
// outwards.
}
}
}
// process all the methods
}
}
thrown = thrownPrev;
} finally {
caught = caughtPrev;
}
}
int nextadrPrev = nextadr;
int firstadrPrev = firstadr;
assert pendingExits.isEmpty();
try {
boolean isInitialConstructor =
if (!isInitialConstructor)
}
if (isInitialConstructor)
// else we are in an instance initializer block;
// leave caught unchanged.
alive = true;
if (isInitialConstructor) {
}
if (isInitialConstructor) {
}
} else {
// uncaught throws will be reported later
}
}
} finally {
caught = caughtPrev;
}
}
try{
} finally {
}
}
}
int nextadrPrev = nextadr;
}
boolean prevLoopPassTwo = loopPassTwo;
do {
loopPassTwo ||
break;
loopPassTwo = true;
alive = true;
} while (true);
}
boolean prevLoopPassTwo = loopPassTwo;
do {
loopPassTwo ||
break;
loopPassTwo = true;
alive = true;
} while (true);
}
boolean prevLoopPassTwo = loopPassTwo;
int nextadrPrev = nextadr;
do {
} else {
alive = true;
}
loopPassTwo ||
break;
loopPassTwo = true;
alive = true;
} while (true);
}
boolean prevLoopPassTwo = loopPassTwo;
int nextadrPrev = nextadr;
do {
loopPassTwo ||
break;
loopPassTwo = true;
alive = true;
} while (true);
inits = initsStart;
alive = true;
}
}
int nextadrPrev = nextadr;
boolean hasDefault = false;
alive = true;
hasDefault = true;
else
// Warn about fall-through if lint switch fallthrough enabled.
if (!loopPassTwo &&
alive &&
"possible.fall-through.into.case");
}
if (!hasDefault) {
alive = true;
}
}
// where
/** Add any variables defined in stats to inits and uninits. */
}
}
}
}
}
if (resource instanceof JCVariableDecl) {
} else if (resource instanceof JCExpression) {
} else {
}
}
sup,
}
}
}
}
}
thrown = thrownPrev;
caught = caughtPrev;
int nextadrCatch = nextadr;
if (!unrefdResources.isEmpty() &&
"automatic.resource.not.referenced", e.getKey());
}
}
alive = true;
continue;
"except.already.caught", exc);
"except.never.thrown.in.try", exc);
}
}
}
}
alive = true;
if (!alive) {
// discard exits and exceptions from try and finally
if (!loopPassTwo &&
"finally.cannot.complete");
}
} else {
// FIX: this doesn't preserve source order of exits in catch
// versus finally!
}
}
}
} else {
}
}
// 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
} else {
}
}
boolean aliveAfterThen = alive;
alive = true;
} else {
alive = true;
}
}
}
}
// if not initial constructor, should markDead instead of recordExit
}
markThrown(tree, t);
}
}
else {
}
markDead();
}
}
// scan(tree.def);
l.nonEmpty();
l = l.tail) {
}
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
// the class def analysis.
l.nonEmpty();
l = l.tail) {
}
}
finally {
caught = caughtPrev;
}
}
}
}
}
}
}
Bits t = initsWhenFalse;
initsWhenTrue = t;
t = uninitsWhenFalse;
uninitsWhenTrue = t;
break;
break;
default:
}
}
break;
break;
default:
}
}
// annotations don't get scanned
}
}
}
}
}
super.visitTypeCast(tree);
}
}
// Do nothing for TopLevel since each class is visited individually
}
/**************************************************************************
* utility methods for ignoring type-annotated casts lint checking
*************************************************************************/
private static final boolean ignoreAnnotatedCasts = true;
private static class AnnotationFinder extends TreeScanner {
public boolean foundTypeAnno = false;
}
}
private boolean containsTypeAnnotation(JCTree e) {
return finder.foundTypeAnno;
}
/**************************************************************************
* main method
*************************************************************************/
/** Perform definite assignment/unassignment analysis on a tree.
*/
try {
uninitsTry = new Bits();
else
firstadr = 0;
nextadr = 0;
alive = true;
} finally {
// note that recursive invocations of this method fail hard
firstadr = 0;
nextadr = 0;
pendingExits = null;
}
}
}