< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
Print this page
/*
! * 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
/*
! * 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
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);
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;
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);
}
}
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);
}
}
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;
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;
}
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;
}
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;
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) {
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) {
/** 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.
/** 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.
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);
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);
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:
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:
/**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
/**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
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) {
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 >