< 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 ***
              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);
-                     }
-                 }
  
                  // 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);
-                     }
-                 }
  
                  // process all the methods
                  for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                      if (l.head.hasTag(METHODDEF)) {
                          scan(l.head);
--- 560,20 ---
              pendingExits = new ListBuffer<>();
              lint = lint.augment(tree.sym);
  
              try {
                  // 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
                  for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                      if (l.head.hasTag(METHODDEF)) {
                          scan(l.head);

*** 1360,44 ***
              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();
-                     }
-                 }
  
                  // 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
--- 1380,14 ---
              thrown = List.nil();
              lint = lint.augment(tree.sym);
  
              try {
                  // process all the static initializers
!                 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 ***
              try {
                  for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
                      JCVariableDecl def = l.head;
                      scan(def);
                  }
!                 if (TreeInfo.isInitialConstructor(tree))
                      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.
--- 1438,11 ---
              try {
                  for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
                      JCVariableDecl def = l.head;
                      scan(def);
                  }
!                 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 ***
              initsWhenFalse = new Bits(true);
              uninitsWhenTrue = new Bits(true);
              uninitsWhenFalse = new Bits(true);
          }
  
!         private boolean isInitialConstructor = false;
  
          @Override
          protected void markDead() {
!             if (!isInitialConstructor) {
                  inits.inclRange(returnadr, nextadr);
              } else {
                  for (int address = returnadr; address < nextadr; address++) {
                      if (!(isFinalUninitializedStaticField(vardecls[address].sym))) {
                          inits.incl(address);
--- 2093,15 ---
              initsWhenFalse = new Bits(true);
              uninitsWhenTrue = new Bits(true);
              uninitsWhenFalse = new Bits(true);
          }
  
!         private boolean isConstructor;
  
          @Override
          protected void markDead() {
!             if (!isConstructor) {
                  inits.inclRange(returnadr, nextadr);
              } else {
                  for (int address = returnadr; address < nextadr; address++) {
                      if (!(isFinalUninitializedStaticField(vardecls[address].sym))) {
                          inits.incl(address);

*** 2344,17 ***
                              }
                          }
                      }
  
                      // 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);
-                         }
-                     }
  
                      // verify all static final fields got initailized
                      for (int i = firstadr; i < nextadr; i++) {
                          JCVariableDecl vardecl = vardecls[i];
                          VarSymbol var = vardecl.sym;
--- 2344,14 ---
                              }
                          }
                      }
  
                      // process all the static initializers
!                     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 ***
                                  }
                              }
                          }
                      }
  
-                     // 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);
                          }
--- 2371,10 ---

*** 2421,17 ***
                  int nextadrPrev = nextadr;
                  int firstadrPrev = firstadr;
                  int returnadrPrev = returnadr;
  
                  Assert.check(pendingExits.isEmpty());
!                 boolean lastInitialConstructor = isInitialConstructor;
                  try {
!                     isInitialConstructor = TreeInfo.isInitialConstructor(tree);
  
!                     if (!isInitialConstructor) {
                          firstadr = nextadr;
                      }
                      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
--- 2409,20 ---
                  int nextadrPrev = nextadr;
                  int firstadrPrev = firstadr;
                  int returnadrPrev = returnadr;
  
                  Assert.check(pendingExits.isEmpty());
!                 boolean isConstructorPrev = isConstructor;
                  try {
!                     isConstructor = TreeInfo.isConstructor(tree);
  
!                     // 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 ***
                      // 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) {
                          boolean isSynthesized = (tree.sym.flags() &
                                                   GENERATEDCONSTR) != 0;
                          for (int i = firstadr; i < nextadr; i++) {
                              JCVariableDecl vardecl = vardecls[i];
                              VarSymbol var = vardecl.sym;
--- 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 (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 ***
                      inits.assign(initsPrev);
                      uninits.assign(uninitsPrev);
                      nextadr = nextadrPrev;
                      firstadr = firstadrPrev;
                      returnadr = returnadrPrev;
!                     isInitialConstructor = lastInitialConstructor;
                  }
              } finally {
                  lint = lintPrev;
              }
          }
--- 2476,11 ---
                      inits.assign(initsPrev);
                      uninits.assign(uninitsPrev);
                      nextadr = nextadrPrev;
                      firstadr = firstadrPrev;
                      returnadr = returnadrPrev;
!                     isConstructor = isConstructorPrev;
                  }
              } finally {
                  lint = lintPrev;
              }
          }

*** 2501,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) {
                      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);
                      }
--- 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 && 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 >