< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
Print this page
import com.sun.tools.javac.code.Kinds.Kind;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import com.sun.tools.javac.code.Type.TypeVar;
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
import static com.sun.tools.javac.code.TypeTag.VOID;
+ import static com.sun.tools.javac.comp.Flow.ThisExposability.ALLOWED;
+ import static com.sun.tools.javac.comp.Flow.ThisExposability.BANNED;
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.Collections;
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) {
+ if (types.asSuper(sup.referenceProjectionOrSelf(), syms.autoCloseableType.tsym) != null) {
Symbol closeMethod = rs.resolveQualifiedMethod(tree,
attrEnv,
types.skipTypeVars(sup, false),
names.close,
List.nil(),
public void visitClassDef(JCClassDecl tree) {
//skip
}
}
+ /** Enum to model whether constructors allowed to "leak" this reference before
+ all instance fields are DA.
+ */
+ enum ThisExposability {
+ ALLOWED, // identity Object classes - NOP
+ BANNED, // primitive/value classes - Error
+ }
+
/**
* 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. This pass is also used to mark
inits.andSet(exit_inits);
uninits.andSet(exit_uninits);
}
}
+ // Are constructors allowed to leak this reference ?
+ ThisExposability thisExposability = ALLOWED;
+
public AssignAnalyzer() {
this.inits = new Bits();
uninits = new Bits();
uninitsTry = new Bits();
initsWhenTrue = new Bits(true);
letInit(tree.pos(), (VarSymbol)sym);
}
}
}
+ void checkEmbryonicThisExposure(JCTree node) {
+ if (this.thisExposability == ALLOWED || classDef == null)
+ return;
+
+ // Note: for non-initial constructors, firstadr is post all instance fields.
+ for (int i = firstadr; i < nextadr; i++) {
+ VarSymbol sym = vardecls[i].sym;
+ if (sym.owner != classDef.sym)
+ continue;
+ if ((sym.flags() & (FINAL | HASINIT | STATIC | PARAMETER)) != FINAL)
+ continue;
+ if (sym.pos < startPos || sym.adr < firstadr)
+ continue;
+ if (!inits.isMember(sym.adr)) {
+ if (this.thisExposability == BANNED) {
+ log.error(node, Errors.ThisExposedPrematurely);
+ }
+ return; // don't flog a dead horse.
+ }
+ }
+ }
+
/** Check that trackable variable is initialized.
*/
void checkInit(DiagnosticPosition pos, VarSymbol sym) {
checkInit(pos, sym, Errors.VarMightNotHaveBeenInitialized(sym));
}
return;
}
Lint lintPrev = lint;
lint = lint.augment(tree.sym);
+ ThisExposability priorThisExposability = this.thisExposability;
try {
final Bits initsPrev = new Bits(inits);
final Bits uninitsPrev = new Bits(uninits);
int nextadrPrev = nextadr;
int firstadrPrev = firstadr;
try {
isInitialConstructor = TreeInfo.isInitialConstructor(tree);
if (!isInitialConstructor) {
firstadr = nextadr;
+ this.thisExposability = ALLOWED;
+ } else {
+ if (tree.sym.owner.type.isValueClass())
+ this.thisExposability = BANNED;
+ else
+ this.thisExposability = ALLOWED;
}
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");
returnadr = returnadrPrev;
isInitialConstructor = lastInitialConstructor;
}
} finally {
lint = lintPrev;
+ this.thisExposability = priorThisExposability;
}
}
private void clearPendingExits(boolean inMethod) {
List<PendingExit> exits = pendingExits.toList();
}
public void visitApply(JCMethodInvocation tree) {
scanExpr(tree.meth);
scanExprs(tree.args);
+ if (tree.meth.hasTag(IDENT)) {
+ JCIdent ident = (JCIdent) tree.meth;
+ if (ident.name != names._super && !ident.sym.isStatic())
+ checkEmbryonicThisExposure(tree);
+ }
}
public void visitNewClass(JCNewClass tree) {
scanExpr(tree.encl);
scanExprs(tree.args);
scan(tree.def);
+ if (classDef != null && tree.encl == null && tree.clazz.hasTag(IDENT)) {
+ JCIdent clazz = (JCIdent) tree.clazz;
+ if (!clazz.sym.isStatic() && clazz.type.getEnclosingType().tsym == classDef.sym) {
+ checkEmbryonicThisExposure(tree);
+ }
+ }
}
@Override
public void visitLambda(JCLambda tree) {
final Bits prevUninits = new Bits(uninits);
}
// check fields accessed through this.<field> are definitely
// assigned before reading their value
public void visitSelect(JCFieldAccess tree) {
- super.visitSelect(tree);
+ ThisExposability priorThisExposability = this.thisExposability;
+ try {
+ if (tree.name == names._this && classDef != null && tree.sym.owner == classDef.sym) {
+ checkEmbryonicThisExposure(tree);
+ } else if (tree.sym.kind == VAR || tree.sym.isStatic()) {
+ this.thisExposability = ALLOWED;
+ }
+ super.visitSelect(tree);
if (TreeInfo.isThisQualifier(tree.selected) &&
tree.sym.kind == VAR) {
- checkInit(tree.pos(), (VarSymbol)tree.sym);
+ checkInit(tree.pos(), (VarSymbol)tree.sym);
+ }
+ } finally {
+ this.thisExposability = priorThisExposability;
}
}
public void visitAssignop(JCAssignOp tree) {
scanExpr(tree.lhs);
public void visitIdent(JCIdent tree) {
if (tree.sym.kind == VAR) {
checkInit(tree.pos(), (VarSymbol)tree.sym);
referenced(tree.sym);
}
+ if (tree.name == names._this) {
+ checkEmbryonicThisExposure(tree);
+ }
}
@Override
public void visitTypeTest(JCInstanceOf tree) {
scanExpr(tree.expr);
< prev index next >