< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.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 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.
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);
// 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;
* 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:
* 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:
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();
}
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();
}
* @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
* @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
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);
}
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);
}
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
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) {
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());
* @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();
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);
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;
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 >