< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
Print this page
import com.sun.tools.javac.comp.ExhaustivenessComputer.RecordPattern;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
import java.util.Arrays;
+ import java.util.function.Predicate;
/** This pass implements dataflow analysis for Java programs though
* different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
* every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that
* every checked exception that is thrown is declared or caught. Definite assignment analysis
brk.target = swtch;
scan(brk);
}
}
! // Do something with all static or non-static field initializers and initialization blocks.
! 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;
! // Don't recurse into nested classes
- if (def.hasTag(CLASSDEF))
continue;
! /* we need to check for flags in the symbol too as there could be cases for which implicit flags are
- * represented in the symbol but not in the tree modifiers as they were not originally in the source
- * code
- */
- boolean isDefStatic = ((TreeInfo.flags(def) | (TreeInfo.symbolFor(def) == null ? 0 : TreeInfo.symbolFor(def).flags_field)) & STATIC) != 0;
- if (!def.hasTag(METHODDEF) && (isDefStatic == isStatic))
- handler.accept(def);
}
} finally {
initScanClass = initScanClassPrev;
}
}
brk.target = swtch;
scan(brk);
}
}
! /** a predicate that selects all static initializers */
! static final Predicate<JCTree> STATIC_INITS = tree ->
+ switch (tree) {
+ case JCVariableDecl vdecl -> vdecl.sym.isStatic();
+ case JCBlock b -> b.isStatic();
+ default -> false;
+ };
+
+ /** a predicate that selects early instance initializers */
+ static final Predicate<JCTree> EARLY_INSTANCE_INITS = tree ->
+ tree instanceof JCVariableDecl vdecl && vdecl.sym.isStrictInstance();
+
+ /** a predicate that selects late instance initializers */
+ static final Predicate<JCTree> LATE_INSTANCE_INITS = tree ->
+ switch (tree) {
+ case JCVariableDecl vdecl -> !vdecl.sym.isStatic() && !vdecl.sym.isStrict();
+ case JCBlock b -> !b.isStatic();
+ default -> false;
+ };
+
+ /** a predicate that all instance initializers */
+ static final Predicate<JCTree> INSTANCE_INITS = EARLY_INSTANCE_INITS.or(LATE_INSTANCE_INITS);
+
+ protected void forEachInitializer(JCClassDecl classDef, Predicate<? super JCTree> initFilter,
+ 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 (!initFilter.test(def)) {
continue;
+ }
! handler.accept(def);
}
} finally {
initScanClass = initScanClassPrev;
}
}
scan(l.head);
}
}
// 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
scan(l.head);
}
}
// process all the static initializers
! forEachInitializer(tree, STATIC_INITS, def -> {
scanDef(def);
clearPendingExits(false);
});
// process all the instance initializers
! forEachInitializer(tree, INSTANCE_INITS, def -> {
scanDef(def);
clearPendingExits(false);
});
// process all the methods
scan(l.head);
}
}
// process all the static initializers
! forEachInitializer(tree, true, def -> {
scan(def);
errorUncaught();
});
// in an anonymous class, add the set of thrown exceptions to
scan(l.head);
}
}
// process all the static initializers
! forEachInitializer(tree, STATIC_INITS, def -> {
scan(def);
errorUncaught();
});
// in an anonymous class, add the set of thrown exceptions to
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();
});
}
}
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, INSTANCE_INITS, def -> {
scan(def);
errorUncaught();
});
}
}
uninitsWhenTrue = new Bits(true);
uninitsWhenFalse = new Bits(true);
}
private boolean isConstructor;
+ private boolean isCompactOrGeneratedRecordConstructor;
@Override
protected void markDead() {
inits.inclRange(returnadr, nextadr);
uninits.inclRange(returnadr, nextadr);
*/
protected boolean trackable(VarSymbol sym) {
return
sym.pos >= startPos &&
((sym.owner.kind == MTH || sym.owner.kind == VAR ||
! isFinalUninitializedField(sym)));
}
! boolean isFinalUninitializedField(VarSymbol sym) {
return sym.owner.kind == TYP &&
! ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
! classDef.sym.isEnclosedBy((ClassSymbol)sym.owner));
}
/** 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.
*/
protected boolean trackable(VarSymbol sym) {
return
sym.pos >= startPos &&
((sym.owner.kind == MTH || sym.owner.kind == VAR ||
! isBlankFinalOrStrictField(sym)));
}
! boolean isBlankFinalOrStrictField(VarSymbol sym) {
return sym.owner.kind == TYP &&
! (sym.flags() & (HASINIT | PARAMETER)) == 0 &&
! (sym.isFinal() || sym.isStrict()) &&
+ classDef.sym.isEnclosedBy((ClassSymbol)sym.owner);
}
/** 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.
/** 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) {
+ letInit(tree, (JCAssign) null);
+ }
+
+ void letInit(JCTree tree, JCAssign assign) {
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 checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) {
if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
trackable(sym) &&
!inits.isMember(sym.adr) &&
(sym.flags_field & CLASH) == 0) {
! log.error(pos, errkey);
inits.incl(sym.adr);
}
}
/** Utility method to reset several Bits instances.
void checkInit(DiagnosticPosition pos, VarSymbol sym, Error errkey) {
if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
trackable(sym) &&
!inits.isMember(sym.adr) &&
(sym.flags_field & CLASH) == 0) {
! log.error(pos, errkey);
inits.incl(sym.adr);
}
}
/** Utility method to reset several Bits instances.
}
}
}
// process all the static initializers
! forEachInitializer(tree, true, def -> {
scan(def);
clearPendingExits(false);
});
// verify all static final fields got initialized
}
}
}
// process all the static initializers
! forEachInitializer(tree, STATIC_INITS, def -> {
scan(def);
clearPendingExits(false);
});
// verify all static final fields got initialized
int firstadrPrev = firstadr;
int returnadrPrev = returnadr;
Assert.check(pendingExits.isEmpty());
boolean isConstructorPrev = isConstructor;
+ boolean isCompactOrGeneratedRecordConstructorPrev = isCompactOrGeneratedRecordConstructor;
try {
isConstructor = TreeInfo.isConstructor(tree);
+ isCompactOrGeneratedRecordConstructor = isConstructor && ((tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
+ (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD));
// We only track field initialization inside constructors
if (!isConstructor) {
firstadr = nextadr;
}
/* If we are executing the code from Gen, then there can be
* synthetic or mandated variables, ignore them.
*/
initParam(def);
}
// else we are in an instance initializer block;
// 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];
/* If we are executing the code from Gen, then there can be
* synthetic or mandated variables, ignore them.
*/
initParam(def);
}
+ if (isConstructor &&
+ !TreeInfo.hasConstructorCall(tree, names._this)) {
+ // Strict fields initializers are executed before the first statement
+ // in the constructor body, unless the constructor is an alternate constructor
+ forEachInitializer(classDef, EARLY_INSTANCE_INITS, def -> {
+ scan(def);
+ clearPendingExits(false);
+ });
+ }
// else we are in an instance initializer block;
// leave caught unchanged.
scan(tree.body);
if (isConstructor) {
boolean isSynthesized = (tree.sym.flags() &
GENERATEDCONSTR) != 0;
for (int i = firstadr; i < nextadr; i++) {
JCVariableDecl vardecl = vardecls[i];
uninits.assign(uninitsPrev);
nextadr = nextadrPrev;
firstadr = firstadrPrev;
returnadr = returnadrPrev;
isConstructor = isConstructorPrev;
+ isCompactOrGeneratedRecordConstructor = isCompactOrGeneratedRecordConstructorPrev;
}
}
private void clearPendingExits(boolean inMethod) {
List<PendingExit> exits = pendingExits.toList();
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);
scan(tree.def);
}
if (isConstructor) {
// If super(): at this point all initialization blocks will execute
Name name = TreeInfo.name(tree.meth);
if (name == names._super) {
! if (!isCompactOrGeneratedRecordConstructor) {
! // all strict fields must be initialized at this point
! checkStrictFieldsInitializedBeforeSuper(tree);
! }
+ forEachInitializer(classDef, LATE_INSTANCE_INITS,
+ 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 (isBlankFinalOrStrictField(sym) && !sym.isStatic())
letInit(tree.pos(), sym);
}
}
}
}
+ void checkStrictFieldsInitializedBeforeSuper(JCMethodInvocation tree) {
+ for (int i = firstadr; i < nextadr; i++) {
+ JCVariableDecl vardecl = vardecls[i];
+ VarSymbol var = vardecl.sym;
+ if (var.owner == classDef.sym && var.isStrictInstance()) {
+ checkInit(TreeInfo.diagEndPos(tree), var, Errors.StrictFieldNotHaveBeenInitializedBeforeSuper(var));
+ }
+ }
+ }
+
public void visitNewClass(JCNewClass tree) {
scanExpr(tree.encl);
scanExprs(tree.args);
scan(tree.def);
}
public void visitAssign(JCAssign tree) {
if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
scanExpr(tree.lhs);
scanExpr(tree.rhs);
! letInit(tree.lhs);
}
// check fields accessed through this.<field> are definitely
// assigned before reading their value
public void visitSelect(JCFieldAccess tree) {
public void visitAssign(JCAssign tree) {
if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
scanExpr(tree.lhs);
scanExpr(tree.rhs);
! letInit(tree.lhs, tree);
}
// check fields accessed through this.<field> are definitely
// assigned before reading their value
public void visitSelect(JCFieldAccess tree) {
< prev index next >