< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
Print this page
//todo: one might eliminate uninits.andSets when monotonic
package com.sun.tools.javac.comp;
+ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
private final Resolve rs;
private final JCDiagnostic.Factory diags;
private Env<AttrContext> attrEnv;
private Lint lint;
private final Infer infer;
+ private final UnsetFieldsInfo unsetFieldsInfo;
public static Flow instance(Context context) {
Flow instance = context.get(flowKey);
if (instance == null)
instance = new Flow(context);
chk = Check.instance(context);
lint = Lint.instance(context);
infer = Infer.instance(context);
rs = Resolve.instance(context);
diags = JCDiagnostic.Factory.instance(context);
- Source source = Source.instance(context);
+ unsetFieldsInfo = UnsetFieldsInfo.instance(context);
}
/**
* Base visitor class for all visitors implementing dataflow analysis logic.
* This class define the shared logic for handling jumps (break/continue statements).
brk.target = swtch;
scan(brk);
}
}
- // Do something with all static or non-static field initializers and initialization blocks.
+ // Do something with static or non-static field initializers and initialization blocks.
protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consumer<? super JCTree> handler) {
+ forEachInitializer(classDef, isStatic, false, handler);
+ }
+
+ /* Do something with static or non-static field initializers and initialization blocks.
+ * the `earlyOnly` argument will determine if we will deal or not with early variable instance
+ * initializers we want to process only those before a super() invocation and ignore them after
+ * it.
+ */
+ protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, boolean earlyOnly,
+ Consumer<? super JCTree> handler) {
if (classDef == initScanClass) // avoid infinite loops
return;
JCClassDecl initScanClassPrev = initScanClass;
initScanClass = classDef;
try {
/* 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);
+ if (!def.hasTag(METHODDEF) && (isDefStatic == isStatic)) {
+ if (def instanceof JCVariableDecl varDecl) {
+ boolean isEarly = varDecl.init != null &&
+ varDecl.sym.isStrict() &&
+ !varDecl.sym.isStatic();
+ if (isEarly == earlyOnly) {
+ handler.accept(def);
+ }
+ } else if (!earlyOnly) {
+ handler.accept(def);
+ }
+ }
}
} finally {
initScanClass = initScanClassPrev;
}
}
*/
protected boolean trackable(VarSymbol sym) {
return
sym.pos >= startPos &&
((sym.owner.kind == MTH || sym.owner.kind == VAR ||
- isFinalUninitializedField(sym)));
+ isFinalOrStrictUninitializedField(sym)));
}
- boolean isFinalUninitializedField(VarSymbol sym) {
+ boolean isFinalOrStrictUninitializedField(VarSymbol sym) {
return sym.owner.kind == TYP &&
- ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
+ (((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL ||
+ (sym.flags() & (STRICT | HASINIT | PARAMETER)) == STRICT) &&
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
/** 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);
+ if (isConstructor && sym.isStrict()) {
+ /* we are initializing a strict field inside of a constructor, we now need to find which fields
+ * haven't been initialized yet
+ */
+ unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, assign != null ? assign : tree, findUninitStrictFields());
+ }
}
}
}
/** Check that trackable variable is initialized.
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);
+ log.error(pos, errkey);
inits.incl(sym.adr);
}
}
/** Utility method to reset several Bits instances.
/* If we are executing the code from Gen, then there can be
* synthetic or mandated variables, ignore them.
*/
initParam(def);
}
+ if (isConstructor) {
+ Set<VarSymbol> unsetFields = findUninitStrictFields();
+ if (unsetFields != null && !unsetFields.isEmpty()) {
+ unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree.body, unsetFields);
+ }
+ }
+
// else we are in an instance initializer block;
// leave caught unchanged.
scan(tree.body);
boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
} finally {
lint = lintPrev;
}
}
+ Set<VarSymbol> findUninitStrictFields() {
+ Set<VarSymbol> unsetFields = new LinkedHashSet<>();
+ for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) {
+ JCVariableDecl variableDecl = vardecls[i];
+ if (variableDecl.sym.isStrict()) {
+ unsetFields.add(variableDecl.sym);
+ }
+ }
+ return unsetFields;
+ }
+
private void clearPendingExits(boolean inMethod) {
List<PendingExit> exits = pendingExits.toList();
pendingExits = new ListBuffer<>();
while (exits.nonEmpty()) {
PendingExit exit = exits.head;
scanExpr(tree.expr);
markDead();
}
public void visitApply(JCMethodInvocation tree) {
+ Name name = TreeInfo.name(tree.meth);
+ // let's process early initializers
+ if (name == names._super) {
+ forEachInitializer(classDef, false, true, def -> {
+ scan(def);
+ clearPendingExits(false);
+ });
+ }
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())
+ if (isFinalOrStrictUninitializedField(sym) && !sym.isStatic())
letInit(tree.pos(), sym);
}
}
}
}
public void visitAssign(JCAssign tree) {
if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
scanExpr(tree.lhs);
scanExpr(tree.rhs);
- letInit(tree.lhs);
+ 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 >