< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java

Print this page
*** 53,10 ***
--- 53,12 ---
  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;

*** 1594,11 ***
              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) {
                          Symbol closeMethod = rs.resolveQualifiedMethod(tree,
                                  attrEnv,
                                  types.skipTypeVars(sup, false),
                                  names.close,
                                  List.nil(),
--- 1596,11 ---
              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.referenceProjectionOrSelf(), syms.autoCloseableType.tsym) != null) {
                          Symbol closeMethod = rs.resolveQualifiedMethod(tree,
                                  attrEnv,
                                  types.skipTypeVars(sup, false),
                                  names.close,
                                  List.nil(),

*** 2010,10 ***
--- 2012,18 ---
          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

*** 2098,10 ***
--- 2108,13 ---
                  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);

*** 2223,10 ***
--- 2236,32 ---
                      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));
          }

*** 2428,10 ***
--- 2463,11 ---
                  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;

*** 2442,10 ***
--- 2478,16 ---
                  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");

*** 2504,10 ***
--- 2546,11 ---
                      returnadr = returnadrPrev;
                      isInitialConstructor = lastInitialConstructor;
                  }
              } finally {
                  lint = lintPrev;
+                 this.thisExposability = priorThisExposability;
              }
          }
  
          private void clearPendingExits(boolean inMethod) {
              List<PendingExit> exits = pendingExits.toList();

*** 2972,16 ***
--- 3015,27 ---
          }
  
          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);

*** 3047,14 ***
          }
  
          // check fields accessed through this.<field> are definitely
          // assigned before reading their value
          public void visitSelect(JCFieldAccess tree) {
!             super.visitSelect(tree);
              if (TreeInfo.isThisQualifier(tree.selected) &&
                  tree.sym.kind == VAR) {
!                 checkInit(tree.pos(), (VarSymbol)tree.sym);
              }
          }
  
          public void visitAssignop(JCAssignOp tree) {
              scanExpr(tree.lhs);
--- 3101,24 ---
          }
  
          // check fields accessed through this.<field> are definitely
          // assigned before reading their value
          public void visitSelect(JCFieldAccess 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);
+                 }
+             } finally {
+                  this.thisExposability = priorThisExposability;
              }
          }
  
          public void visitAssignop(JCAssignOp tree) {
              scanExpr(tree.lhs);

*** 3114,10 ***
--- 3178,13 ---
          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 >