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