< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
Print this page
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
+ import java.util.function.Consumer;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Scope.WriteableScope;
/** The currently pending exits that go from current inner blocks
* to an enclosing block, in source order.
*/
ListBuffer<PendingExit> pendingExits;
+ /** A class whose initializers we are scanning. Because initializer
+ * scans can be triggered out of sequence when visiting certain nodes
+ * (e.g., super()), we protect against infinite loops that could be
+ * triggered by incorrect code (e.g., super() inside initializer).
+ */
+ JCClassDecl initScanClass;
+
/** 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.
JCBreak brk = make.at(Position.NOPOS).Break(null);
brk.target = swtch;
scan(brk);
}
}
+
+ // Do something with all static or non-static field initializers and initialization blocks.
+ // Note: This method also sends nested class definitions to the handler.
+ protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consumer<? super JCTree> handler) {
+ if (classDef == initScanClass) // avoid infinite loops
+ return;
+ JCClassDecl initScanClassPrev = initScanClass;
+ initScanClass = classDef;
+ try {
+ for (List<JCTree> defs = classDef.defs; defs.nonEmpty(); defs = defs.tail) {
+ JCTree def = defs.head;
+ if (!def.hasTag(METHODDEF) && ((TreeInfo.flags(def) & STATIC) != 0) == isStatic)
+ handler.accept(def);
+ }
+ } finally {
+ initScanClass = initScanClassPrev;
+ }
+ }
}
/**
* This pass implements the first step of the dataflow analysis, namely
* the liveness analysis check. This checks that every statement is reachable.
pendingExits = new ListBuffer<>();
lint = lint.augment(tree.sym);
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);
- clearPendingExits(false);
- }
- }
// 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);
- clearPendingExits(false);
- }
- }
// process all the methods
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (l.head.hasTag(METHODDEF)) {
scan(l.head);
pendingExits = new ListBuffer<>();
lint = lint.augment(tree.sym);
try {
// process all the static initializers
! forEachInitializer(tree, true, def -> {
! scanDef(def);
! clearPendingExits(false);
! });
// process all the instance initializers
! forEachInitializer(tree, false, def -> {
! scanDef(def);
! clearPendingExits(false);
! });
// process all the methods
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (l.head.hasTag(METHODDEF)) {
scan(l.head);
thrown = List.nil();
lint = lint.augment(tree.sym);
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) {
! scan(l.head);
- errorUncaught();
- }
- }
-
- // add intersection of all throws clauses of initial constructors
- // to set of caught exceptions, unless class is anonymous.
- if (!anonymousClass) {
- 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) {
- scan(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
thrown = List.nil();
lint = lint.augment(tree.sym);
try {
// process all the static initializers
! forEachInitializer(tree, true, def -> {
! scan(def);
! 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
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.
try {
for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
JCVariableDecl def = l.head;
scan(def);
}
! if (TreeInfo.hasConstructorCall(tree, names._super))
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.
}
public void visitApply(JCMethodInvocation tree) {
scan(tree.meth);
scan(tree.args);
+
+ // Mark as thrown the exceptions thrown by the method being invoked
for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
markThrown(tree, l.head);
+
+ // After super(), scan initializers to uncover any exceptions they throw
+ if (TreeInfo.name(tree.meth) == names._super) {
+ forEachInitializer(classDef, false, def -> {
+ scan(def);
+ errorUncaught();
+ });
+ }
}
public void visitNewClass(JCNewClass tree) {
scan(tree.encl);
scan(tree.args);
initsWhenFalse = new Bits(true);
uninitsWhenTrue = new Bits(true);
uninitsWhenFalse = new Bits(true);
}
! private boolean isInitialConstructor = false;
@Override
protected void markDead() {
! if (!isInitialConstructor) {
inits.inclRange(returnadr, nextadr);
} else {
for (int address = returnadr; address < nextadr; address++) {
if (!(isFinalUninitializedStaticField(vardecls[address].sym))) {
inits.incl(address);
initsWhenFalse = new Bits(true);
uninitsWhenTrue = new Bits(true);
uninitsWhenFalse = new Bits(true);
}
! private boolean isConstructor;
@Override
protected void markDead() {
! if (!isConstructor) {
inits.inclRange(returnadr, nextadr);
} else {
for (int address = returnadr; address < nextadr; address++) {
if (!(isFinalUninitializedStaticField(vardecls[address].sym))) {
inits.incl(address);
}
}
}
// 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);
- clearPendingExits(false);
- }
- }
// verify all static final fields got initailized
for (int i = firstadr; i < nextadr; i++) {
JCVariableDecl vardecl = vardecls[i];
VarSymbol var = vardecl.sym;
}
}
}
// process all the static initializers
! forEachInitializer(tree, true, def -> {
! scan(def);
! clearPendingExits(false);
! });
// verify all static final fields got initailized
for (int i = firstadr; i < nextadr; i++) {
JCVariableDecl vardecl = vardecls[i];
VarSymbol var = vardecl.sym;
}
}
}
}
- // 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) {
- scan(l.head);
- clearPendingExits(false);
- }
- }
-
// process all the methods
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (l.head.hasTag(METHODDEF)) {
scan(l.head);
}
int nextadrPrev = nextadr;
int firstadrPrev = firstadr;
int returnadrPrev = returnadr;
Assert.check(pendingExits.isEmpty());
! boolean lastInitialConstructor = isInitialConstructor;
try {
! 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);
Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag");
/* If we are executing the code from Gen, then there can be
int nextadrPrev = nextadr;
int firstadrPrev = firstadr;
int returnadrPrev = returnadr;
Assert.check(pendingExits.isEmpty());
! boolean isConstructorPrev = isConstructor;
try {
! isConstructor = TreeInfo.isConstructor(tree);
! // We only track field initialization inside constructors
+ if (!isConstructor) {
firstadr = nextadr;
}
+
+ // Mark all method parameters as DA
for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
JCVariableDecl def = l.head;
scan(def);
Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag");
/* If we are executing the code from Gen, then there can be
// leave caught unchanged.
scan(tree.body);
boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
(tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD);
! if (isInitialConstructor) {
boolean isSynthesized = (tree.sym.flags() &
GENERATEDCONSTR) != 0;
for (int i = firstadr; i < nextadr; i++) {
JCVariableDecl vardecl = vardecls[i];
VarSymbol var = vardecl.sym;
// leave caught unchanged.
scan(tree.body);
boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
(tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD);
! if (isConstructor) {
boolean isSynthesized = (tree.sym.flags() &
GENERATEDCONSTR) != 0;
for (int i = firstadr; i < nextadr; i++) {
JCVariableDecl vardecl = vardecls[i];
VarSymbol var = vardecl.sym;
inits.assign(initsPrev);
uninits.assign(uninitsPrev);
nextadr = nextadrPrev;
firstadr = firstadrPrev;
returnadr = returnadrPrev;
! isInitialConstructor = lastInitialConstructor;
}
} finally {
lint = lintPrev;
}
}
inits.assign(initsPrev);
uninits.assign(uninitsPrev);
nextadr = nextadrPrev;
firstadr = firstadrPrev;
returnadr = returnadrPrev;
! isConstructor = isConstructorPrev;
}
} finally {
lint = lintPrev;
}
}
PendingExit exit = exits.head;
exits = exits.tail;
Assert.check((inMethod && exit.tree.hasTag(RETURN)) ||
log.hasErrorOn(exit.tree.pos()),
exit.tree);
! if (inMethod && isInitialConstructor) {
Assert.check(exit instanceof AssignPendingExit);
inits.assign(((AssignPendingExit) exit).exit_inits);
for (int i = firstadr; i < nextadr; i++) {
checkInit(exit.tree.pos(), vardecls[i].sym);
}
PendingExit exit = exits.head;
exits = exits.tail;
Assert.check((inMethod && exit.tree.hasTag(RETURN)) ||
log.hasErrorOn(exit.tree.pos()),
exit.tree);
! if (inMethod && isConstructor) {
Assert.check(exit instanceof AssignPendingExit);
inits.assign(((AssignPendingExit) exit).exit_inits);
for (int i = firstadr; i < nextadr; i++) {
checkInit(exit.tree.pos(), vardecls[i].sym);
}
}
public void visitApply(JCMethodInvocation tree) {
scanExpr(tree.meth);
scanExprs(tree.args);
+
+ // Handle superclass constructor invocations
+ if (isConstructor) {
+
+ // If super(): at this point all initialization blocks will execute
+ Name name = TreeInfo.name(tree.meth);
+ if (name == names._super) {
+ forEachInitializer(classDef, false, def -> {
+ scan(def);
+ clearPendingExits(false);
+ });
+ }
+
+ // If this(): at this point all final uninitialized fields will get initialized
+ else if (name == names._this) {
+ for (int address = firstadr; address < nextadr; address++) {
+ VarSymbol sym = vardecls[address].sym;
+ if (isFinalUninitializedField(sym) && !sym.isStatic())
+ letInit(tree.pos(), sym);
+ }
+ }
+ }
}
public void visitNewClass(JCNewClass tree) {
scanExpr(tree.encl);
scanExprs(tree.args);
< prev index next >