< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java

Print this page
*** 1,7 ***
  /*
!  * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
--- 1,7 ---
  /*
!  * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this

*** 95,10 ***
--- 95,12 ---
  
      /** Constant pool writer, set by genClass.
       */
      final PoolWriter poolWriter;
  
+     private final UnsetFieldsInfo unsetFieldsInfo;
+ 
      @SuppressWarnings("this-escape")
      protected Gen(Context context) {
          context.put(genKey, this);
  
          names = Names.instance(context);

*** 125,24 ***
--- 127,27 ---
              : options.isSet(G_CUSTOM, "vars");
          genCrt = options.isSet(XJCOV);
          debugCode = options.isSet("debug.code");
          disableVirtualizedPrivateInvoke = options.isSet("disableVirtualizedPrivateInvoke");
          poolWriter = new PoolWriter(types, names);
+         unsetFieldsInfo = UnsetFieldsInfo.instance(context);
  
          // ignore cldc because we cannot have both stackmap formats
          this.stackMap = StackMapFormat.JSR202;
          annotate = Annotate.instance(context);
          qualifiedSymbolCache = new HashMap<>();
+         generateAssertUnsetFieldsFrame = options.isSet("generateAssertUnsetFieldsFrame");
      }
  
      /** Switches
       */
      private final boolean lineDebugInfo;
      private final boolean varDebugInfo;
      private final boolean genCrt;
      private final boolean debugCode;
      private boolean disableVirtualizedPrivateInvoke;
+     private boolean generateAssertUnsetFieldsFrame;
  
      /** Code buffer, set by genMethod.
       */
      private Code code;
  

*** 422,10 ***
--- 427,12 ---
       *  @param defs         The list of class member declarations.
       *  @param c            The enclosing class.
       */
      List<JCTree> normalizeDefs(List<JCTree> defs, ClassSymbol c) {
          ListBuffer<JCStatement> initCode = new ListBuffer<>();
+         // only used for value classes
+         ListBuffer<JCStatement> initBlocks = new ListBuffer<>();
          ListBuffer<Attribute.TypeCompound> initTAs = new ListBuffer<>();
          ListBuffer<JCStatement> clinitCode = new ListBuffer<>();
          ListBuffer<Attribute.TypeCompound> clinitTAs = new ListBuffer<>();
          ListBuffer<JCTree> methodDefs = new ListBuffer<>();
          // Sort definitions into three listbuffers:

*** 437,12 ***
              switch (def.getTag()) {
              case BLOCK:
                  JCBlock block = (JCBlock)def;
                  if ((block.flags & STATIC) != 0)
                      clinitCode.append(block);
!                 else if ((block.flags & SYNTHETIC) == 0)
!                     initCode.append(block);
                  break;
              case METHODDEF:
                  methodDefs.append(def);
                  break;
              case VARDEF:
--- 444,17 ---
              switch (def.getTag()) {
              case BLOCK:
                  JCBlock block = (JCBlock)def;
                  if ((block.flags & STATIC) != 0)
                      clinitCode.append(block);
!                 else if ((block.flags & SYNTHETIC) == 0) {
!                     if (c.isValueClass() || c.hasStrict()) {
+                         initBlocks.append(block);
+                     } else {
+                         initCode.append(block);
+                     }
+                 }
                  break;
              case METHODDEF:
                  methodDefs.append(def);
                  break;
              case VARDEF:

*** 477,16 ***
              default:
                  Assert.error();
              }
          }
          // Insert any instance initializers into all constructors.
!         if (initCode.length() != 0) {
-             List<JCStatement> inits = initCode.toList();
              initTAs.addAll(c.getInitTypeAttributes());
              List<Attribute.TypeCompound> initTAlist = initTAs.toList();
              for (JCTree t : methodDefs) {
!                 normalizeMethod((JCMethodDecl)t, inits, initTAlist);
              }
          }
          // If there are class initializers, create a <clinit> method
          // that contains them as its body.
          if (clinitCode.length() != 0) {
--- 489,15 ---
              default:
                  Assert.error();
              }
          }
          // Insert any instance initializers into all constructors.
!         if (initCode.length() != 0 || initBlocks.length() != 0) {
              initTAs.addAll(c.getInitTypeAttributes());
              List<Attribute.TypeCompound> initTAlist = initTAs.toList();
              for (JCTree t : methodDefs) {
!                 normalizeMethod((JCMethodDecl)t, initCode.toList(), initBlocks.toList(), initTAlist);
              }
          }
          // If there are class initializers, create a <clinit> method
          // that contains them as its body.
          if (clinitCode.length() != 0) {

*** 545,23 ***
       *  @param md        The tree potentially representing a
       *                   constructor's definition.
       *  @param initCode  The list of instance initializer statements.
       *  @param initTAs  Type annotations from the initializer expression.
       */
!     void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode, List<TypeCompound> initTAs) {
          if (TreeInfo.isConstructor(md) && TreeInfo.hasConstructorCall(md, names._super)) {
              // We are seeing a constructor that has a super() call.
              // Find the super() invocation and append the given initializer code.
!             TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.prepend(supercall)));
  
              if (md.body.endpos == Position.NOPOS)
                  md.body.endpos = TreeInfo.endPos(md.body.stats.last());
  
              md.sym.appendUniqueTypeAttributes(initTAs);
          }
      }
  
  /* ************************************************************************
   * Traversal methods
   *************************************************************************/
  
      /** Visitor argument: The current environment.
--- 556,63 ---
       *  @param md        The tree potentially representing a
       *                   constructor's definition.
       *  @param initCode  The list of instance initializer statements.
       *  @param initTAs  Type annotations from the initializer expression.
       */
!     void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode, List<JCStatement> initBlocks,  List<TypeCompound> initTAs) {
          if (TreeInfo.isConstructor(md) && TreeInfo.hasConstructorCall(md, names._super)) {
              // We are seeing a constructor that has a super() call.
              // Find the super() invocation and append the given initializer code.
!             if (md.sym.owner.isValueClass() || md.sym.owner.hasStrict()) {
+                 rewriteInitializersIfNeeded(md, initCode);
+                 md.body.stats = initCode.appendList(md.body.stats);
+                 TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initBlocks.prepend(supercall)));
+             } else {
+                 TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.prepend(supercall)));
+             }
  
              if (md.body.endpos == Position.NOPOS)
                  md.body.endpos = TreeInfo.endPos(md.body.stats.last());
  
              md.sym.appendUniqueTypeAttributes(initTAs);
          }
      }
  
+     void rewriteInitializersIfNeeded(JCMethodDecl md, List<JCStatement> initCode) {
+         if (lower.initializerOuterThis.containsKey(md.sym.owner)) {
+             InitializerVisitor initializerVisitor = new InitializerVisitor(md, lower.initializerOuterThis.get(md.sym.owner));
+             for (JCStatement init : initCode) {
+                 initializerVisitor.scan(init);
+             }
+         }
+     }
+ 
+     class InitializerVisitor extends TreeScanner {
+         JCMethodDecl md;
+         Set<JCExpression> exprSet;
+ 
+         InitializerVisitor(JCMethodDecl md, Set<JCExpression> exprSet) {
+             this.md = md;
+             this.exprSet = exprSet;
+         }
+ 
+         @Override
+         public void visitTree(JCTree tree) {}
+ 
+         @Override
+         public void visitIdent(JCIdent tree) {
+             if (exprSet.contains(tree)) {
+                 for (JCVariableDecl param: md.params) {
+                     if (param.name == tree.name &&
+                             ((param.sym.flags_field & (MANDATED | NOOUTERTHIS)) == (MANDATED | NOOUTERTHIS))) {
+                         tree.sym = param.sym;
+                     }
+                 }
+             }
+         }
+     }
+ 
  /* ************************************************************************
   * Traversal methods
   *************************************************************************/
  
      /** Visitor argument: The current environment.

*** 942,17 ***
--- 993,23 ---
              }
  
              else if (tree.body != null) {
                  // Create a new code structure and initialize it.
                  int startpcCrt = initCode(tree, env, fatcode);
+                 Set<VarSymbol> prevUnsetFields = code.currentUnsetFields;
+                 if (meth.isConstructor()) {
+                     code.currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body);
+                 }
  
                  try {
                      genStat(tree.body, env);
                  } catch (CodeSizeOverflow e) {
                      // Failed due to code limit, try again with jsr/ret
                      startpcCrt = initCode(tree, env, fatcode);
                      genStat(tree.body, env);
+                 } finally {
+                     code.currentUnsetFields = prevUnsetFields;
                  }
  
                  if (code.state.stacksize != 0) {
                      log.error(tree.body.pos(), Errors.StackSimError(tree.sym));
                      throw new AssertionError();

*** 1017,11 ***
                                          debugCode,
                                          genCrt ? new CRTable(tree, env.toplevel.endPositions)
                                                 : null,
                                          syms,
                                          types,
!                                         poolWriter);
              items = new Items(poolWriter, code, syms, types);
              if (code.debugCode) {
                  System.err.println(meth + " for body " + tree);
              }
  
--- 1074,12 ---
                                          debugCode,
                                          genCrt ? new CRTable(tree, env.toplevel.endPositions)
                                                 : null,
                                          syms,
                                          types,
!                                         poolWriter,
+                                         generateAssertUnsetFieldsFrame);
              items = new Items(poolWriter, code, syms, types);
              if (code.debugCode) {
                  System.err.println(meth + " for body " + tree);
              }
  

*** 1153,10 ***
--- 1211,23 ---
          private void genLoop(JCStatement loop,
                               JCStatement body,
                               JCExpression cond,
                               List<JCExpressionStatement> step,
                               boolean testFirst) {
+             Set<VarSymbol> prevCodeUnsetFields = code.currentUnsetFields;
+             try {
+                 genLoopHelper(loop, body, cond, step, testFirst);
+             } finally {
+                 code.currentUnsetFields = prevCodeUnsetFields;
+             }
+         }
+ 
+         private void genLoopHelper(JCStatement loop,
+                              JCStatement body,
+                              JCExpression cond,
+                              List<JCExpressionStatement> step,
+                              boolean testFirst) {
              Env<GenContext> loopEnv = env.dup(loop, new GenContext());
              int startpc = code.entryPoint();
              if (testFirst) { //while or for loop
                  CondItem c;
                  if (cond != null) {

*** 1219,15 ***
--- 1290,17 ---
  
      @Override
      public void visitSwitchExpression(JCSwitchExpression tree) {
          code.resolvePending();
          boolean prevInCondSwitchExpression = inCondSwitchExpression;
+         Set<VarSymbol> prevCodeUnsetFields = code.currentUnsetFields;
          try {
              inCondSwitchExpression = false;
              doHandleSwitchExpression(tree);
          } finally {
              inCondSwitchExpression = prevInCondSwitchExpression;
+             code.currentUnsetFields = prevCodeUnsetFields;
          }
          result = items.makeStackItem(pt);
      }
  
      private void doHandleSwitchExpression(JCSwitchExpression tree) {

*** 1299,10 ***
--- 1372,20 ---
              return hasTryScanner.hasTry;
          }
  
      private void handleSwitch(JCTree swtch, JCExpression selector, List<JCCase> cases,
                                boolean patternSwitch) {
+         Set<VarSymbol> prevCodeUnsetFields = code.currentUnsetFields;
+         try {
+             handleSwitchHelper(swtch, selector, cases, patternSwitch);
+         } finally {
+             code.currentUnsetFields = prevCodeUnsetFields;
+         }
+     }
+ 
+     void handleSwitchHelper(JCTree swtch, JCExpression selector, List<JCCase> cases,
+                       boolean patternSwitch) {
          int limit = code.nextreg;
          Assert.check(!selector.type.hasTag(CLASS));
          int switchStart = patternSwitch ? code.entryPoint() : -1;
          int startpcCrt = genCrt ? code.curCP() : 0;
          Assert.check(code.isStatementStart());

*** 1310,17 ***
          if (cases.isEmpty()) {
              // We are seeing:  switch <sel> {}
              sel.load().drop();
              if (genCrt)
                  code.crt.put(TreeInfo.skipParens(selector),
!                              CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
          } else {
              // We are seeing a nonempty switch.
              sel.load();
              if (genCrt)
                  code.crt.put(TreeInfo.skipParens(selector),
!                              CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
              Env<GenContext> switchEnv = env.dup(swtch, new GenContext());
              switchEnv.info.isSwitch = true;
  
              // Compute number of labels and minimum and maximum label values.
              // For each case, store its label in an array.
--- 1393,17 ---
          if (cases.isEmpty()) {
              // We are seeing:  switch <sel> {}
              sel.load().drop();
              if (genCrt)
                  code.crt.put(TreeInfo.skipParens(selector),
!                         CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
          } else {
              // We are seeing a nonempty switch.
              sel.load();
              if (genCrt)
                  code.crt.put(TreeInfo.skipParens(selector),
!                         CRT_FLOW_CONTROLLER, startpcCrt, code.curCP());
              Env<GenContext> switchEnv = env.dup(swtch, new GenContext());
              switchEnv.info.isSwitch = true;
  
              // Compute number of labels and minimum and maximum label values.
              // For each case, store its label in an array.

*** 1352,15 ***
              long table_space_cost = 4 + ((long) hi - lo + 1); // words
              long table_time_cost = 3; // comparisons
              long lookup_space_cost = 3 + 2 * (long) nlabels;
              long lookup_time_cost = nlabels;
              int opcode =
!                 nlabels > 0 &&
!                 table_space_cost + 3 * table_time_cost <=
!                 lookup_space_cost + 3 * lookup_time_cost
!                 ?
!                 tableswitch : lookupswitch;
  
              int startpc = code.curCP();    // the position of the selector operation
              code.emitop0(opcode);
              code.align(4);
              int tableBase = code.curCP();  // the start of the jump table
--- 1435,15 ---
              long table_space_cost = 4 + ((long) hi - lo + 1); // words
              long table_time_cost = 3; // comparisons
              long lookup_space_cost = 3 + 2 * (long) nlabels;
              long lookup_time_cost = nlabels;
              int opcode =
!                     nlabels > 0 &&
!                             table_space_cost + 3 * table_time_cost <=
!                                     lookup_space_cost + 3 * lookup_time_cost
!                             ?
!                             tableswitch : lookupswitch;
  
              int startpc = code.curCP();    // the position of the selector operation
              code.emitop0(opcode);
              code.align(4);
              int tableBase = code.curCP();  // the start of the jump table

*** 1392,12 ***
                  // Insert offset directly into code or else into the
                  // offsets table.
                  if (i != defaultIndex) {
                      if (opcode == tableswitch) {
                          code.put4(
!                             tableBase + 4 * (labels[i] - lo + 3),
!                             pc - startpc);
                      } else {
                          offsets[i] = pc - startpc;
                      }
                  } else {
                      code.put4(tableBase, pc - startpc);
--- 1475,12 ---
                  // Insert offset directly into code or else into the
                  // offsets table.
                  if (i != defaultIndex) {
                      if (opcode == tableswitch) {
                          code.put4(
!                                 tableBase + 4 * (labels[i] - lo + 3),
!                                 pc - startpc);
                      } else {
                          offsets[i] = pc - startpc;
                      }
                  } else {
                      code.put4(tableBase, pc - startpc);

*** 1447,12 ***
                      code.put4(caseidx + 4, offsets[i]);
                  }
              }
  
              if (swtch instanceof JCSwitchExpression) {
!                  // Emit line position for the end of a switch expression
!                  code.statBegin(TreeInfo.endPos(swtch));
              }
          }
          code.endScopes(limit);
      }
  //where
--- 1530,12 ---
                      code.put4(caseidx + 4, offsets[i]);
                  }
              }
  
              if (swtch instanceof JCSwitchExpression) {
!                 // Emit line position for the end of a switch expression
!                 code.statBegin(TreeInfo.endPos(swtch));
              }
          }
          code.endScopes(limit);
      }
  //where

*** 1551,10 ***
--- 1634,19 ---
           *  @param body      The body of the try or synchronized statement.
           *  @param catchers  The list of catch clauses.
           *  @param env       The current environment of the body.
           */
          void genTry(JCTree body, List<JCCatch> catchers, Env<GenContext> env) {
+             Set<VarSymbol> prevCodeUnsetFields = code.currentUnsetFields;
+             try {
+                 genTryHelper(body, catchers, env);
+             } finally {
+                 code.currentUnsetFields = prevCodeUnsetFields;
+             }
+         }
+ 
+         void genTryHelper(JCTree body, List<JCCatch> catchers, Env<GenContext> env) {
              int limit = code.nextreg;
              int startpc = code.curCP();
              Code.State stateTry = code.state.dup();
              genStat(body, env, CRT_BLOCK);
              int endpc = code.curCP();

*** 1570,21 ***
                  exitChain = code.branch(goto_);
              }
              endFinalizerGap(env);
              env.info.finalize.afterBody();
              boolean hasFinalizer =
!                 env.info.finalize != null &&
!                 env.info.finalize.hasFinalizer();
              if (startpc != endpc) for (List<JCCatch> l = catchers; l.nonEmpty(); l = l.tail) {
                  // start off with exception on stack
                  code.entryPoint(stateTry, l.head.param.sym.type);
                  genCatch(l.head, env, startpc, endpc, gaps);
                  genFinalizer(env);
                  if (hasFinalizer || l.tail.nonEmpty()) {
                      code.statBegin(TreeInfo.endPos(env.tree));
                      exitChain = Code.mergeChains(exitChain,
!                                                  code.branch(goto_));
                  }
                  endFinalizerGap(env);
              }
              if (hasFinalizer && (startpc != endpc || !actualTry)) {
                  // Create a new register segment to avoid allocating
--- 1662,21 ---
                  exitChain = code.branch(goto_);
              }
              endFinalizerGap(env);
              env.info.finalize.afterBody();
              boolean hasFinalizer =
!                     env.info.finalize != null &&
!                             env.info.finalize.hasFinalizer();
              if (startpc != endpc) for (List<JCCatch> l = catchers; l.nonEmpty(); l = l.tail) {
                  // start off with exception on stack
                  code.entryPoint(stateTry, l.head.param.sym.type);
                  genCatch(l.head, env, startpc, endpc, gaps);
                  genFinalizer(env);
                  if (hasFinalizer || l.tail.nonEmpty()) {
                      code.statBegin(TreeInfo.endPos(env.tree));
                      exitChain = Code.mergeChains(exitChain,
!                             code.branch(goto_));
                  }
                  endFinalizerGap(env);
              }
              if (hasFinalizer && (startpc != endpc || !actualTry)) {
                  // Create a new register segment to avoid allocating

*** 1603,11 ***
                  // environment's GenContext.
                  int startseg = startpc;
                  while (env.info.gaps.nonEmpty()) {
                      int endseg = env.info.gaps.next().intValue();
                      registerCatch(body.pos(), startseg, endseg,
!                                   catchallpc, 0);
                      startseg = env.info.gaps.next().intValue();
                  }
                  code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS));
                  code.markStatBegin();
  
--- 1695,11 ---
                  // environment's GenContext.
                  int startseg = startpc;
                  while (env.info.gaps.nonEmpty()) {
                      int endseg = env.info.gaps.next().intValue();
                      registerCatch(body.pos(), startseg, endseg,
!                             catchallpc, 0);
                      startseg = env.info.gaps.next().intValue();
                  }
                  code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS));
                  code.markStatBegin();
  

*** 1618,12 ***
                  code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.END_POS));
                  code.markStatBegin();
  
                  excVar.load();
                  registerCatch(body.pos(), startseg,
!                               env.info.gaps.next().intValue(),
!                               catchallpc, 0);
                  code.emitop0(athrow);
                  code.markDead();
  
                  // If there are jsr's to this finalizer, ...
                  if (env.info.cont != null) {
--- 1710,12 ---
                  code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.END_POS));
                  code.markStatBegin();
  
                  excVar.load();
                  registerCatch(body.pos(), startseg,
!                         env.info.gaps.next().intValue(),
!                         catchallpc, 0);
                  code.emitop0(athrow);
                  code.markDead();
  
                  // If there are jsr's to this finalizer, ...
                  if (env.info.cont != null) {

*** 1762,15 ***
                  nerrs++;
              }
          }
  
      public void visitIf(JCIf tree) {
          int limit = code.nextreg;
          Chain thenExit = null;
          Assert.check(code.isStatementStart());
          CondItem c = genCond(TreeInfo.skipParens(tree.cond),
!                              CRT_FLOW_CONTROLLER);
          Chain elseChain = c.jumpFalse();
          Assert.check(code.isStatementStart());
          if (!c.isFalse()) {
              code.resolve(c.trueJumps);
              genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET);
--- 1854,24 ---
                  nerrs++;
              }
          }
  
      public void visitIf(JCIf tree) {
+         Set<VarSymbol> prevCodeUnsetFields = code.currentUnsetFields;
+         try {
+             visitIfHelper(tree);
+         } finally {
+             code.currentUnsetFields = prevCodeUnsetFields;
+         }
+     }
+ 
+     public void visitIfHelper(JCIf tree) {
          int limit = code.nextreg;
          Chain thenExit = null;
          Assert.check(code.isStatementStart());
          CondItem c = genCond(TreeInfo.skipParens(tree.cond),
!                 CRT_FLOW_CONTROLLER);
          Chain elseChain = c.jumpFalse();
          Assert.check(code.isStatementStart());
          if (!c.isFalse()) {
              code.resolve(c.trueJumps);
              genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET);

*** 2093,10 ***
--- 2194,12 ---
      }
  
      public void visitAssign(JCAssign tree) {
          Item l = genExpr(tree.lhs, tree.lhs.type);
          genExpr(tree.rhs, tree.lhs.type).load();
+         Set<VarSymbol> tmpUnsetSymbols = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree);
+         code.currentUnsetFields = tmpUnsetSymbols != null ? tmpUnsetSymbols : code.currentUnsetFields;
          if (tree.rhs.type.hasTag(BOT)) {
              /* This is just a case of widening reference conversion that per 5.1.5 simply calls
                 for "regarding a reference as having some other type in a manner that can be proved
                 correct at compile time."
              */

*** 2371,11 ***
  
          if (tree.name == names._class) {
              code.emitLdc((LoadableConstant)checkDimension(tree.pos(), tree.selected.type));
              result = items.makeStackItem(pt);
              return;
!        }
  
          Symbol ssym = TreeInfo.symbol(tree.selected);
  
          // Are we selecting via super?
          boolean selectSuper =
--- 2474,11 ---
  
          if (tree.name == names._class) {
              code.emitLdc((LoadableConstant)checkDimension(tree.pos(), tree.selected.type));
              result = items.makeStackItem(pt);
              return;
!         }
  
          Symbol ssym = TreeInfo.symbol(tree.selected);
  
          // Are we selecting via super?
          boolean selectSuper =
< prev index next >