< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.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

*** 104,10 ***
--- 104,11 ---
      private final PkgInfo pkginfoOpt;
      private final boolean optimizeOuterThis;
      private final boolean nullCheckOuterThis;
      private final boolean useMatchException;
      private final HashMap<TypePairs, String> typePairToName;
+     private final boolean allowValueClasses;
      private int variableIndex = 0;
  
      @SuppressWarnings("this-escape")
      protected Lower(Context context) {
          context.put(lowerKey, this);

*** 139,10 ***
--- 140,11 ---
          Source source = Source.instance(context);
          Preview preview = Preview.instance(context);
          useMatchException = Feature.PATTERN_SWITCH.allowedInSource(source) &&
                              (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH));
          typePairToName = TypePairs.initialize(syms);
+         this.allowValueClasses = preview.isEnabled() && Feature.VALUE_CLASSES.allowedInSource(source);
      }
  
      /** The currently enclosing class.
       */
      ClassSymbol currentClass;

*** 769,11 ***
          for (List<ClassSymbol> l = accessConstrTags; l.nonEmpty(); l = l.tail) {
              ClassSymbol c = l.head;
              if (isTranslatedClassAvailable(c))
                  continue;
              // Create class definition tree.
!             JCClassDecl cdec = makeEmptyClass(STATIC | SYNTHETIC,
                      c.outermostClass(), c.flatname, false);
              swapAccessConstructorTag(c, cdec.sym);
              translated.append(cdec);
          }
      }
--- 771,12 ---
          for (List<ClassSymbol> l = accessConstrTags; l.nonEmpty(); l = l.tail) {
              ClassSymbol c = l.head;
              if (isTranslatedClassAvailable(c))
                  continue;
              // Create class definition tree.
!             // IDENTITY_TYPE will be interpreted as ACC_SUPER for older class files so we are fine
+             JCClassDecl cdec = makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE,
                      c.outermostClass(), c.flatname, false);
              swapAccessConstructorTag(c, cdec.sym);
              translated.append(cdec);
          }
      }

*** 1241,11 ***
              Name flatname = names.fromString("" + topClass.getQualifiedName() +
                                              target.syntheticNameChar() +
                                              i);
              ClassSymbol ctag = chk.getCompiled(topModle, flatname);
              if (ctag == null)
!                 ctag = makeEmptyClass(STATIC | SYNTHETIC, topClass).sym;
              else if (!ctag.isAnonymous())
                  continue;
              // keep a record of all tags, to verify that all are generated as required
              accessConstrTags = accessConstrTags.prepend(ctag);
              return ctag;
--- 1244,12 ---
              Name flatname = names.fromString("" + topClass.getQualifiedName() +
                                              target.syntheticNameChar() +
                                              i);
              ClassSymbol ctag = chk.getCompiled(topModle, flatname);
              if (ctag == null)
!                 // IDENTITY_TYPE will be interpreted as ACC_SUPER for older class files so we are fine
+                 ctag = makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE, topClass).sym;
              else if (!ctag.isAnonymous())
                  continue;
              // keep a record of all tags, to verify that all are generated as required
              accessConstrTags = accessConstrTags.prepend(ctag);
              return ctag;

*** 1397,16 ***
          }
          return proxyName;
      }
  
      /** Proxy definitions for all free variables in given list, in reverse order.
!      *  @param pos        The source code position of the definition.
!      *  @param freevars   The free variables.
!      *  @param owner      The class in which the definitions go.
       */
      List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner) {
!         return freevarDefs(pos, freevars, owner, LOCAL_CAPTURE_FIELD);
      }
  
      List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner,
              long additionalFlags) {
          long flags = FINAL | SYNTHETIC | additionalFlags;
--- 1401,17 ---
          }
          return proxyName;
      }
  
      /** Proxy definitions for all free variables in given list, in reverse order.
!      *  @param pos               The source code position of the definition.
!      *  @param freevars          The free variables.
!      *  @param owner             The class in which the definitions go.
       */
      List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner) {
!         long strict = (allowValueClasses && owner.isValueClass()) ? STRICT : 0;
+         return freevarDefs(pos, freevars, owner, LOCAL_CAPTURE_FIELD | strict);
      }
  
      List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner,
              long additionalFlags) {
          long flags = FINAL | SYNTHETIC | additionalFlags;

*** 1450,16 ***
          return result;
      }
  
      private VarSymbol makeOuterThisVarSymbol(Symbol owner, long flags) {
          Type target = owner.innermostAccessibleEnclosingClass().erasure(types);
!         // Set NOOUTERTHIS for all synthetic outer instance variables, and unset
!         // it when the variable is accessed. If the variable is never accessed,
!         // we skip creating an outer instance field and saving the constructor
!         // parameter to it.
!         VarSymbol outerThis =
!             new VarSymbol(flags | NOOUTERTHIS, outerThisName(target, owner), target, owner);
          outerThisStack = outerThisStack.prepend(outerThis);
          return outerThis;
      }
  
      private JCVariableDecl makeOuterThisVarDecl(int pos, VarSymbol sym) {
--- 1455,18 ---
          return result;
      }
  
      private VarSymbol makeOuterThisVarSymbol(Symbol owner, long flags) {
          Type target = owner.innermostAccessibleEnclosingClass().erasure(types);
!         if (owner.kind == TYP) {
!             // Set NOOUTERTHIS for all synthetic outer instance variables, and unset
!             // it when the variable is accessed. If the variable is never accessed,
!             // we skip creating an outer instance field and saving the constructor
!             // parameter to it.
!             flags = flags | NOOUTERTHIS | OUTER_THIS_FIELD;
+         }
+         VarSymbol outerThis = new VarSymbol(flags, outerThisName(target, owner), target, owner);
          outerThisStack = outerThisStack.prepend(outerThis);
          return outerThis;
      }
  
      private JCVariableDecl makeOuterThisVarDecl(int pos, VarSymbol sym) {

*** 1490,11 ***
      /** Definition for this$n field.
       *  @param pos        The source code position of the definition.
       *  @param owner      The class in which the definition goes.
       */
      JCVariableDecl outerThisDef(int pos, ClassSymbol owner) {
!         VarSymbol outerThis = makeOuterThisVarSymbol(owner, FINAL | SYNTHETIC);
          return makeOuterThisVarDecl(pos, outerThis);
      }
  
      /** Return a list of trees that load the free variables in given list,
       *  in reverse order.
--- 1497,12 ---
      /** Definition for this$n field.
       *  @param pos        The source code position of the definition.
       *  @param owner      The class in which the definition goes.
       */
      JCVariableDecl outerThisDef(int pos, ClassSymbol owner) {
!         long strict = (allowValueClasses && owner.isValueClass()) ? STRICT : 0;
+         VarSymbol outerThis = makeOuterThisVarSymbol(owner, FINAL | SYNTHETIC | strict);
          return makeOuterThisVarDecl(pos, outerThis);
      }
  
      /** Return a list of trees that load the free variables in given list,
       *  in reverse order.

*** 1830,11 ***
          Scope s = clazz.members();
          for (Symbol sym : s.getSymbols(NON_RECURSIVE))
              if (sym.kind == TYP &&
                  sym.name == names.empty &&
                  (sym.flags() & INTERFACE) == 0) return (ClassSymbol) sym;
!         return makeEmptyClass(STATIC | SYNTHETIC, clazz).sym;
      }
  
      /** Create an attributed tree of the form left.name(). */
      private JCMethodInvocation makeCall(JCExpression left, Name name, List<JCExpression> args) {
          Assert.checkNonNull(left.type);
--- 1838,12 ---
          Scope s = clazz.members();
          for (Symbol sym : s.getSymbols(NON_RECURSIVE))
              if (sym.kind == TYP &&
                  sym.name == names.empty &&
                  (sym.flags() & INTERFACE) == 0) return (ClassSymbol) sym;
!         // IDENTITY_TYPE will be interpreted as ACC_SUPER for older class files so we are fine
+         return makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE, clazz).sym;
      }
  
      /** Create an attributed tree of the form left.name(). */
      private JCMethodInvocation makeCall(JCExpression left, Name name, List<JCExpression> args) {
          Assert.checkNonNull(left.type);

*** 1856,11 ***
          case DOUBLE: case BOOLEAN: case VOID:
              // replace with <BoxedClass>.TYPE
              ClassSymbol c = types.boxedClass(type);
              Symbol typeSym =
                  rs.accessBase(
!                     rs.findIdentInType(pos, attrEnv, c.type, names.TYPE, KindSelector.VAR),
                      pos, c.type, names.TYPE, true);
              if (typeSym.kind == VAR)
                  ((VarSymbol)typeSym).getConstValue(); // ensure initializer is evaluated
              return make.QualIdent(typeSym);
          case CLASS: case ARRAY:
--- 1865,11 ---
          case DOUBLE: case BOOLEAN: case VOID:
              // replace with <BoxedClass>.TYPE
              ClassSymbol c = types.boxedClass(type);
              Symbol typeSym =
                  rs.accessBase(
!                     rs.findIdentInType(pos, attrEnv, c.type, names.TYPE, KindSelector.VAR, null),
                      pos, c.type, names.TYPE, true);
              if (typeSym.kind == VAR)
                  ((VarSymbol)typeSym).getConstValue(); // ensure initializer is evaluated
              return make.QualIdent(typeSym);
          case CLASS: case ARRAY:

*** 1882,11 ***
      /**Used to create an auxiliary class to hold $assertionsDisabled for interfaces.
       */
      private ClassSymbol assertionsDisabledClass() {
          if (assertionsDisabledClassCache != null) return assertionsDisabledClassCache;
  
!         assertionsDisabledClassCache = makeEmptyClass(STATIC | SYNTHETIC, outermostClassDef.sym).sym;
  
          return assertionsDisabledClassCache;
      }
  
      // This code is not particularly robust if the user has
--- 1891,12 ---
      /**Used to create an auxiliary class to hold $assertionsDisabled for interfaces.
       */
      private ClassSymbol assertionsDisabledClass() {
          if (assertionsDisabledClassCache != null) return assertionsDisabledClassCache;
  
!         // IDENTITY_TYPE will be interpreted as ACC_SUPER for older class files so we are fine
+         assertionsDisabledClassCache = makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE, outermostClassDef.sym).sym;
  
          return assertionsDisabledClassCache;
      }
  
      // This code is not particularly robust if the user has

*** 2751,23 ***
              ListBuffer<VarSymbol> fields = new ListBuffer<>();
              for (Symbol sym : currentClass.getEnclosedElements()) {
                  if (sym.kind == Kinds.Kind.VAR && ((sym.flags() & RECORD) != 0))
                      fields.append((VarSymbol) sym);
              }
              for (VarSymbol field: fields) {
                  if ((field.flags_field & Flags.UNINITIALIZED_FIELD) != 0) {
                      VarSymbol param = tree.params.stream().filter(p -> p.name == field.name).findFirst().get().sym;
                      make.at(tree.pos);
!                     tree.body.stats = tree.body.stats.append(
!                             make.Exec(
!                                     make.Assign(
!                                             make.Select(make.This(field.owner.erasure(types)), field),
-                                             make.Ident(param)).setType(field.erasure(types))));
-                     // we don't need the flag at the field anymore
                      field.flags_field &= ~Flags.UNINITIALIZED_FIELD;
                  }
              }
          }
          result = tree;
      }
  
      public void visitTypeCast(JCTypeCast tree) {
--- 2761,29 ---
              ListBuffer<VarSymbol> fields = new ListBuffer<>();
              for (Symbol sym : currentClass.getEnclosedElements()) {
                  if (sym.kind == Kinds.Kind.VAR && ((sym.flags() & RECORD) != 0))
                      fields.append((VarSymbol) sym);
              }
+             ListBuffer<JCStatement> initializers = new ListBuffer<>();
              for (VarSymbol field: fields) {
                  if ((field.flags_field & Flags.UNINITIALIZED_FIELD) != 0) {
                      VarSymbol param = tree.params.stream().filter(p -> p.name == field.name).findFirst().get().sym;
                      make.at(tree.pos);
!                     initializers.add(make.Exec(
!                             make.Assign(
!                                     make.Select(make.This(field.owner.erasure(types)), field),
!                                     make.Ident(param)).setType(field.erasure(types))));
                      field.flags_field &= ~Flags.UNINITIALIZED_FIELD;
                  }
              }
+             if (initializers.nonEmpty()) {
+                 if (allowValueClasses && (tree.sym.owner.isValueClass() || ((ClassSymbol)tree.sym.owner).isRecord())) {
+                     TreeInfo.mapSuperCalls(tree.body, supercall -> make.Block(0, initializers.toList().append(supercall)));
+                 } else {
+                     tree.body.stats = tree.body.stats.appendList(initializers);
+                 }
+             }
          }
          result = tree;
      }
  
      public void visitTypeCast(JCTypeCast tree) {
< prev index next >