< prev index next >

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

Print this page
*** 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
--- 1,7 ---
  /*
!  * Copyright (c) 1999, 2026, 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

*** 75,10 ***
--- 75,11 ---
      private final String accessDollar;
      private final Types types;
      private final Lower lower;
      private final Annotate annotate;
      private final StringConcat concat;
+     private final LocalProxyVarsGen localProxyVarsGen;
  
      /** Format of stackmap tables to be generated. */
      private final Code.StackMapFormat stackMap;
  
      /** A type that serves as the expected type for all method expressions.

*** 107,10 ***
--- 108,11 ---
          rs = Resolve.instance(context);
          make = TreeMaker.instance(context);
          target = Target.instance(context);
          types = Types.instance(context);
          concat = StringConcat.instance(context);
+         localProxyVarsGen = LocalProxyVarsGen.instance(context);
  
          methodType = new MethodType(null, null, null, syms.methodClass);
          accessDollar = "access" + target.syntheticNameChar();
          lower = Lower.instance(context);
  

*** 129,19 ***
--- 131,23 ---
  
          // ignore cldc because we cannot have both stackmap formats
          this.stackMap = StackMapFormat.JSR202;
          annotate = Annotate.instance(context);
          qualifiedSymbolCache = new HashMap<>();
+         Preview preview = Preview.instance(context);
+         Source source = Source.instance(context);
+         allowValueClasses = preview.isEnabled() && Source.Feature.VALUE_CLASSES.allowedInSource(source);
      }
  
      /** Switches
       */
      private final boolean lineDebugInfo;
      private final boolean varDebugInfo;
      private final boolean genCrt;
      private final boolean debugCode;
      private boolean disableVirtualizedPrivateInvoke;
+     private final boolean allowValueClasses;
  
      /** Code buffer, set by genMethod.
       */
      private Code code;
  

*** 411,32 ***
   * Normalizing class-members.
   *************************************************************************/
  
      /** Distribute member initializer code into constructors and {@code <clinit>}
       *  method.
!      *  @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<>();
          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:
          //  - initCode for instance initializers
          //  - clinitCode for class initializers
          //  - methodDefs for method definitions
!         for (List<JCTree> l = defs; l.nonEmpty(); l = l.tail) {
              JCTree def = l.head;
              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:
--- 417,38 ---
   * Normalizing class-members.
   *************************************************************************/
  
      /** Distribute member initializer code into constructors and {@code <clinit>}
       *  method.
!      *  @param classDecl         The class declaration to normalize.
       */
!     List<JCTree> normalizeDefs(JCClassDecl classDecl) {
          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:
          //  - initCode for instance initializers
          //  - clinitCode for class initializers
          //  - methodDefs for method definitions
!         for (List<JCTree> l = classDecl.defs; l.nonEmpty(); l = l.tail) {
              JCTree def = l.head;
              switch (def.getTag()) {
              case BLOCK:
                  JCBlock block = (JCBlock)def;
                  if ((block.flags & STATIC) != 0)
                      clinitCode.append(block);
!                 else if ((block.flags & SYNTHETIC) == 0) {
!                     if (classDecl.sym.isValueClass()) {
+                         initBlocks.append(block);
+                     } else {
+                         initCode.append(block);
+                     }
+                 }
                  break;
              case METHODDEF:
                  methodDefs.append(def);
                  break;
              case VARDEF:

*** 471,38 ***
              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) {
              MethodSymbol clinit = new MethodSymbol(
!                 STATIC | (c.flags() & STRICTFP),
                  names.clinit,
                  new MethodType(
                      List.nil(), syms.voidType,
                      List.nil(), syms.methodClass),
!                 c);
!             c.members().enter(clinit);
              List<JCStatement> clinitStats = clinitCode.toList();
              JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats);
              block.bracePos = TreeInfo.endPos(clinitStats.last());
              methodDefs.append(make.MethodDef(clinit, block));
  
              if (!clinitTAs.isEmpty())
                  clinit.appendUniqueTypeAttributes(clinitTAs.toList());
!             if (!c.getClassInitTypeAttributes().isEmpty())
!                 clinit.appendUniqueTypeAttributes(c.getClassInitTypeAttributes());
          }
          // Return all method definitions.
          return methodDefs.toList();
      }
  
--- 483,39 ---
              default:
                  Assert.error();
              }
          }
          // Insert any instance initializers into all constructors.
!         List<TypeCompound> initTAlist = List.nil();
!         if (initCode.nonEmpty() || initBlocks.nonEmpty()) {
!             initTAs.addAll(classDecl.sym.getInitTypeAttributes());
!             initTAlist = initTAs.toList();
          }
+         for (JCTree t : methodDefs) {
+             normalizeMethod((JCMethodDecl)t, initCode.toList(), initBlocks.toList(), initTAlist);
+         }
+         localProxyVarsGen.allFieldNormalized(classDecl.sym);
          // If there are class initializers, create a <clinit> method
          // that contains them as its body.
          if (clinitCode.length() != 0) {
              MethodSymbol clinit = new MethodSymbol(
!                 STATIC | (classDecl.sym.flags() & STRICTFP),
                  names.clinit,
                  new MethodType(
                      List.nil(), syms.voidType,
                      List.nil(), syms.methodClass),
!                     classDecl.sym);
!             classDecl.sym.members().enter(clinit);
              List<JCStatement> clinitStats = clinitCode.toList();
              JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats);
              block.bracePos = TreeInfo.endPos(clinitStats.last());
              methodDefs.append(make.MethodDef(clinit, block));
  
              if (!clinitTAs.isEmpty())
                  clinit.appendUniqueTypeAttributes(clinitTAs.toList());
!             if (!classDecl.sym.getClassInitTypeAttributes().isEmpty())
!                 clinit.appendUniqueTypeAttributes(classDecl.sym.getClassInitTypeAttributes());
          }
          // Return all method definitions.
          return methodDefs.toList();
      }
  

*** 539,20 ***
       *  @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.bracePos == Position.NOPOS)
                  md.body.bracePos = TreeInfo.endPos(md.body.stats.last());
  
!             md.sym.appendUniqueTypeAttributes(initTAs);
          }
      }
  
  /* ************************************************************************
   * Traversal methods
--- 552,58 ---
       *  @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 (initCode.nonEmpty() || initBlocks.nonEmpty()) {
+                 if (allowValueClasses &&
+                         (md.sym.owner.isValueClass() || ((md.sym.owner.flags_field & RECORD) != 0))) {
+                     rewriteEarlyInitializersIfNeeded(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)));
+                 }
+                 md.sym.appendUniqueTypeAttributes(initTAs);
+             }
+ 
+             localProxyVarsGen.patchConstructor(md, make);
  
              if (md.body.bracePos == Position.NOPOS)
                  md.body.bracePos = TreeInfo.endPos(md.body.stats.last());
+         }
+     }
  
!     /**
+      * Some early field initializer might contain references to synthetic Lower symbols,
+      * such as 'this$0' or local var proxies. Since these are effectively "early reads",
+      * we need to replace such reference with a reference to the corresponding
+      * (synthetic) constructor parameter.
+      */
+     void rewriteEarlyInitializersIfNeeded(JCMethodDecl md, List<JCStatement> initCode) {
+         class EarlyInitializerVisitor extends TreeScanner {
+             @Override
+             public void visitIdent(JCIdent tree) {
+                 if ((tree.sym.flags() & OUTER_THIS_FIELD) != 0) {
+                     tree.sym = md.sym.extraParams.head;
+                 } else if ((tree.sym.flags() & LOCAL_CAPTURE_FIELD) != 0) {
+                     Symbol capturedSym = tree.sym.baseSymbol();
+                     tree.sym = md.sym.capturedLocals.stream()
+                             .filter(l -> l.baseSymbol() == capturedSym)
+                             .findAny().orElseThrow();
+                 }
+             }
+         }
+         if (md.sym.capturedLocals.nonEmpty() || md.sym.extraParams.nonEmpty()) {
+             EarlyInitializerVisitor initializerVisitor = new EarlyInitializerVisitor();
+             for (JCStatement init : initCode) {
+                 initializerVisitor.scan(init);
+             }
          }
      }
  
  /* ************************************************************************
   * Traversal methods

*** 1022,11 ***
                                          stackMap,
                                          debugCode,
                                          genCrt ? new CRTable(tree) : null,
                                          syms,
                                          types,
!                                         poolWriter);
              items = new Items(poolWriter, code, syms, types);
              if (code.debugCode) {
                  System.err.println(meth + " for body " + tree);
              }
  
--- 1073,12 ---
                                          stackMap,
                                          debugCode,
                                          genCrt ? new CRTable(tree) : null,
                                          syms,
                                          types,
!                                         poolWriter,
+                                         allowValueClasses);
              items = new Items(poolWriter, code, syms, types);
              if (code.debugCode) {
                  System.err.println(meth + " for body " + tree);
              }
  

*** 1046,10 ***
--- 1098,14 ---
              for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
                  checkDimension(l.head.pos(), l.head.sym.type);
                  code.setDefined(code.newLocal(l.head.sym));
              }
  
+             if (allowValueClasses && meth.isConstructor()) {
+                 code.initUnsetStrictFields(env.enclClass.sym);
+             }
+ 
              // Get ready to generate code for method body.
              int startpcCrt = genCrt ? code.curCP() : 0;
              code.entryPoint();
  
              // Suppress initial stackmap

*** 1158,10 ***
--- 1214,18 ---
          private void genLoop(JCStatement loop,
                               JCStatement body,
                               JCExpression cond,
                               List<JCExpressionStatement> step,
                               boolean testFirst) {
+             genLoopHelper(loop, body, cond, step, testFirst);
+         }
+ 
+         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) {

*** 1296,10 ***
--- 1360,15 ---
              return hasTryScanner.hasTry;
          }
  
      private void handleSwitch(JCTree swtch, JCExpression selector, List<JCCase> cases,
                                boolean patternSwitch) {
+         handleSwitchHelper(swtch, selector, cases, patternSwitch);
+     }
+ 
+     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());

*** 1544,10 ***
--- 1613,14 ---
           *  @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) {
+             genTryHelper(body, catchers, env);
+         }
+ 
+         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();

*** 1755,10 ***
--- 1828,14 ---
                  nerrs++;
              }
          }
  
      public void visitIf(JCIf tree) {
+         visitIfHelper(tree);
+     }
+ 
+     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);

*** 2471,11 ***
              attrEnv = env;
              ClassSymbol c = cdef.sym;
              this.toplevel = env.toplevel;
              /* method normalizeDefs() can add references to external classes into the constant pool
               */
!             cdef.defs = normalizeDefs(cdef.defs, c);
              generateReferencesToPrunedTree(c);
              Env<GenContext> localEnv = new Env<>(cdef, new GenContext());
              localEnv.toplevel = env.toplevel;
              localEnv.enclClass = cdef;
  
--- 2548,11 ---
              attrEnv = env;
              ClassSymbol c = cdef.sym;
              this.toplevel = env.toplevel;
              /* method normalizeDefs() can add references to external classes into the constant pool
               */
!             cdef.defs = normalizeDefs(cdef);
              generateReferencesToPrunedTree(c);
              Env<GenContext> localEnv = new Env<>(cdef, new GenContext());
              localEnv.toplevel = env.toplevel;
              localEnv.enclClass = cdef;
  
< prev index next >