< prev index next >

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

Print this page
@@ -30,10 +30,11 @@
  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;

@@ -384,10 +385,17 @@
          /** 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.

@@ -469,10 +477,28 @@
                  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.

@@ -534,26 +560,20 @@
              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);
-                     }
-                 }
+                 forEachInitializer(tree, true, def -> {
+                     scanDef(def);
+                     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);
-                     }
-                 }
+                 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);

@@ -1360,44 +1380,14 @@
              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();
-                     }
-                 }
+                 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

@@ -1448,11 +1438,11 @@
              try {
                  for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
                      JCVariableDecl def = l.head;
                      scan(def);
                  }
-                 if (TreeInfo.isInitialConstructor(tree))
+                 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.

@@ -1749,12 +1739,22 @@
          }
  
          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);

@@ -2093,15 +2093,15 @@
              initsWhenFalse = new Bits(true);
              uninitsWhenTrue = new Bits(true);
              uninitsWhenFalse = new Bits(true);
          }
  
-         private boolean isInitialConstructor = false;
+         private boolean isConstructor;
  
          @Override
          protected void markDead() {
-             if (!isInitialConstructor) {
+             if (!isConstructor) {
                  inits.inclRange(returnadr, nextadr);
              } else {
                  for (int address = returnadr; address < nextadr; address++) {
                      if (!(isFinalUninitializedStaticField(vardecls[address].sym))) {
                          inits.incl(address);

@@ -2344,17 +2344,14 @@
                              }
                          }
                      }
  
                      // 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);
-                         }
-                     }
+                     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;

@@ -2374,19 +2371,10 @@
                                  }
                              }
                          }
                      }
  
-                     // 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);
                          }

@@ -2421,17 +2409,20 @@
                  int nextadrPrev = nextadr;
                  int firstadrPrev = firstadr;
                  int returnadrPrev = returnadr;
  
                  Assert.check(pendingExits.isEmpty());
-                 boolean lastInitialConstructor = isInitialConstructor;
+                 boolean isConstructorPrev = isConstructor;
                  try {
-                     isInitialConstructor = TreeInfo.isInitialConstructor(tree);
+                     isConstructor = TreeInfo.isConstructor(tree);
  
-                     if (!isInitialConstructor) {
+                     // 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

@@ -2443,11 +2434,11 @@
                      // 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) {
+                     if (isConstructor) {
                          boolean isSynthesized = (tree.sym.flags() &
                                                   GENERATEDCONSTR) != 0;
                          for (int i = firstadr; i < nextadr; i++) {
                              JCVariableDecl vardecl = vardecls[i];
                              VarSymbol var = vardecl.sym;

@@ -2485,11 +2476,11 @@
                      inits.assign(initsPrev);
                      uninits.assign(uninitsPrev);
                      nextadr = nextadrPrev;
                      firstadr = firstadrPrev;
                      returnadr = returnadrPrev;
-                     isInitialConstructor = lastInitialConstructor;
+                     isConstructor = isConstructorPrev;
                  }
              } finally {
                  lint = lintPrev;
              }
          }

@@ -2501,11 +2492,11 @@
                  PendingExit exit = exits.head;
                  exits = exits.tail;
                  Assert.check((inMethod && exit.tree.hasTag(RETURN)) ||
                                   log.hasErrorOn(exit.tree.pos()),
                               exit.tree);
-                 if (inMethod && isInitialConstructor) {
+                 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);
                      }

@@ -2957,10 +2948,32 @@
          }
  
          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 >