Skip to content

Commit 66ee27a

Browse files
author
Vicente Romero
committed
8334484: [lworld] new translation strategy for instance field initializers
Reviewed-by: mcimadamore
1 parent daf7b09 commit 66ee27a

File tree

12 files changed

+394
-26
lines changed

12 files changed

+394
-26
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,10 @@ public boolean isStrict() {
407407
return (flags() & STRICT) != 0;
408408
}
409409

410+
public boolean isStrictInstance() {
411+
return (flags() & STRICT) != 0 && (flags() & STATIC) == 0;
412+
}
413+
410414
public boolean hasStrict() {
411415
return (flags() & HAS_STRICT) != 0;
412416
}

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ public enum CompileState {
6363
TRANSPATTERNS(8),
6464
LOWER(9),
6565
UNLAMBDA(10),
66-
GENERATE(11);
66+
STRICT_FIELDS_PROXIES(11),
67+
GENERATE(12);
6768

6869
CompileState(int value) {
6970
this.value = value;
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package com.sun.tools.javac.comp;
27+
28+
import java.util.ArrayList;
29+
import java.util.HashMap;
30+
import java.util.HashSet;
31+
import java.util.LinkedHashMap;
32+
import java.util.Map;
33+
import java.util.Set;
34+
35+
import com.sun.tools.javac.code.Symbol;
36+
import com.sun.tools.javac.code.Symbol.ClassSymbol;
37+
import com.sun.tools.javac.code.Symbol.VarSymbol;
38+
import com.sun.tools.javac.code.Types;
39+
import com.sun.tools.javac.tree.JCTree.JCAssign;
40+
import com.sun.tools.javac.tree.JCTree.JCExpression;
41+
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
42+
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
43+
import com.sun.tools.javac.tree.TreeMaker;
44+
import com.sun.tools.javac.tree.TreeTranslator;
45+
import com.sun.tools.javac.util.Context;
46+
import com.sun.tools.javac.util.ListBuffer;
47+
import com.sun.tools.javac.util.Name;
48+
import com.sun.tools.javac.util.Names;
49+
50+
import static com.sun.tools.javac.code.Flags.FINAL;
51+
import static com.sun.tools.javac.code.Flags.SYNTHETIC;
52+
import static com.sun.tools.javac.tree.JCTree.Tag.VARDEF;
53+
54+
import com.sun.tools.javac.jvm.Target;
55+
import com.sun.tools.javac.tree.JCTree;
56+
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
57+
import com.sun.tools.javac.tree.JCTree.JCStatement;
58+
import com.sun.tools.javac.tree.TreeInfo;
59+
import com.sun.tools.javac.util.List;
60+
import com.sun.tools.javac.util.Options;
61+
62+
/** This phase adds local variable proxies for strict fields that are read during the
63+
* early construction phase (prologue)
64+
*
65+
* Assignments to the affected instance fields will be rewritten as assignments to a
66+
* local proxy variable. Fields will be assigned to with its corresponding local variable
67+
* proxy just before the super invocation and after its arguments, if any, have been evaluated.
68+
*
69+
* <p><b>This is NOT part of any supported API.
70+
* If you write code that depends on this, you do so at your own risk.
71+
* This code and its internal interfaces are subject to change or
72+
* deletion without notice.</b>
73+
*/
74+
public class LocalProxyVarsGen extends TreeTranslator {
75+
76+
protected static final Context.Key<LocalProxyVarsGen> valueInitializersKey = new Context.Key<>();
77+
78+
public static LocalProxyVarsGen instance(Context context) {
79+
LocalProxyVarsGen instance = context.get(valueInitializersKey);
80+
if (instance == null)
81+
instance = new LocalProxyVarsGen(context);
82+
return instance;
83+
}
84+
85+
private final Types types;
86+
private final Names names;
87+
private final Target target;
88+
private TreeMaker make;
89+
private final UnsetFieldsInfo unsetFieldsInfo;
90+
private ClassSymbol currentClass = null;
91+
private java.util.List<JCVariableDecl> strictInstanceFields;
92+
private Map<JCMethodDecl, Set<Symbol>> strictFieldsReadInPrologue = new HashMap<>();
93+
94+
private final boolean noLocalProxyVars;
95+
96+
@SuppressWarnings("this-escape")
97+
protected LocalProxyVarsGen(Context context) {
98+
context.put(valueInitializersKey, this);
99+
make = TreeMaker.instance(context);
100+
types = Types.instance(context);
101+
names = Names.instance(context);
102+
target = Target.instance(context);
103+
unsetFieldsInfo = UnsetFieldsInfo.instance(context);
104+
Options options = Options.instance(context);
105+
noLocalProxyVars = options.isSet("noLocalProxyVars");
106+
}
107+
108+
public void addStrictFieldReadInPrologue(JCMethodDecl constructor, Symbol sym) {
109+
Set<Symbol> fieldSet = strictFieldsReadInPrologue.getOrDefault(constructor, new HashSet<>());
110+
fieldSet.add(sym);
111+
strictFieldsReadInPrologue.put(constructor, fieldSet);
112+
}
113+
114+
public JCTree translateTopLevelClass(JCTree cdef, TreeMaker make) {
115+
if (!noLocalProxyVars) {
116+
try {
117+
this.make = make;
118+
return translate(cdef);
119+
} finally {
120+
// note that recursive invocations of this method fail hard
121+
this.make = null;
122+
}
123+
} else {
124+
return cdef;
125+
}
126+
}
127+
128+
@Override
129+
public void visitClassDef(JCClassDecl tree) {
130+
ClassSymbol prevCurrentClass = currentClass;
131+
java.util.List<JCVariableDecl> prevStrictInstanceFields = strictInstanceFields;
132+
try {
133+
currentClass = tree.sym;
134+
strictInstanceFields = tree.defs.stream()
135+
.filter(t -> t.hasTag(VARDEF))
136+
.map(t -> (JCVariableDecl)t)
137+
.filter(vd -> vd.sym.isStrict() && !vd.sym.isStatic())
138+
.collect(List.collector());
139+
super.visitClassDef(tree);
140+
} finally {
141+
currentClass = prevCurrentClass;
142+
strictInstanceFields = prevStrictInstanceFields;
143+
}
144+
}
145+
146+
public void visitMethodDef(JCMethodDecl tree) {
147+
if (strictFieldsReadInPrologue.get(tree) != null) {
148+
Set<Symbol> fieldSet = strictFieldsReadInPrologue.get(tree);
149+
java.util.List<JCVariableDecl> strictFieldsRead = new ArrayList<>();
150+
for (JCVariableDecl sfield : strictInstanceFields) {
151+
if (fieldSet.contains(sfield.sym)) {
152+
strictFieldsRead.add(sfield);
153+
}
154+
}
155+
addLocalProxiesFor(tree, strictFieldsRead);
156+
strictFieldsReadInPrologue.remove(tree);
157+
}
158+
super.visitMethodDef(tree);
159+
}
160+
161+
void addLocalProxiesFor(JCMethodDecl constructor, java.util.List<JCVariableDecl> multiAssignedStrictFields) {
162+
ListBuffer<JCStatement> localDeclarations = new ListBuffer<>();
163+
Map<Symbol, Symbol> fieldToLocalMap = new LinkedHashMap<>();
164+
165+
for (JCVariableDecl fieldDecl : multiAssignedStrictFields) {
166+
long flags = SYNTHETIC;
167+
VarSymbol proxy = new VarSymbol(flags, newLocalName(fieldDecl.name.toString()), fieldDecl.sym.erasure(types), constructor.sym);
168+
fieldToLocalMap.put(fieldDecl.sym, proxy);
169+
JCVariableDecl localDecl = make.at(constructor.pos).VarDef(proxy, fieldDecl.init);
170+
localDecl.vartype = fieldDecl.vartype;
171+
localDeclarations = localDeclarations.append(localDecl);
172+
}
173+
174+
FieldRewriter fieldRewriter = new FieldRewriter(constructor, fieldToLocalMap);
175+
ListBuffer<JCStatement> newBody = new ListBuffer<>();
176+
for (JCStatement st : constructor.body.stats) {
177+
newBody = newBody.append(fieldRewriter.translate(st));
178+
}
179+
localDeclarations.addAll(newBody);
180+
ListBuffer<JCStatement> assigmentsBeforeSuper = new ListBuffer<>();
181+
for (Symbol vsym : fieldToLocalMap.keySet()) {
182+
Symbol local = fieldToLocalMap.get(vsym);
183+
assigmentsBeforeSuper.append(make.at(constructor.pos()).Assignment(vsym, make.at(constructor.pos()).Ident(local)));
184+
}
185+
constructor.body.stats = localDeclarations.toList();
186+
JCTree.JCMethodInvocation constructorCall = TreeInfo.findConstructorCall(constructor);
187+
if (constructorCall.args.isEmpty()) {
188+
// this is just a super invocation with no arguments we can set the fields just before the invocation
189+
// and let Gen do the rest
190+
TreeInfo.mapSuperCalls(constructor.body, supercall -> make.Block(0, assigmentsBeforeSuper.toList().append(supercall)));
191+
} else {
192+
// we need to generate fresh local variables to catch the values of the arguments, then
193+
// assign the proxy locals to the fields and finally invoke the super with the fresh local variables
194+
int argPosition = 0;
195+
ListBuffer<JCStatement> superArgsProxies = new ListBuffer<>();
196+
for (JCExpression arg : constructorCall.args) {
197+
long flags = SYNTHETIC | FINAL;
198+
VarSymbol proxyForArgSym = new VarSymbol(flags, newLocalName("" + argPosition), types.erasure(arg.type), constructor.sym);
199+
JCVariableDecl proxyForArgDecl = make.at(constructor.pos).VarDef(proxyForArgSym, arg);
200+
superArgsProxies = superArgsProxies.append(proxyForArgDecl);
201+
argPosition++;
202+
}
203+
List<JCStatement> superArgsProxiesList = superArgsProxies.toList();
204+
ListBuffer<JCExpression> newArgs = new ListBuffer<>();
205+
for (JCStatement argProxy : superArgsProxies) {
206+
newArgs.add(make.at(argProxy.pos).Ident((JCVariableDecl) argProxy));
207+
}
208+
constructorCall.args = newArgs.toList();
209+
TreeInfo.mapSuperCalls(constructor.body,
210+
supercall -> make.Block(0, superArgsProxiesList.appendList(assigmentsBeforeSuper.toList()).append(supercall)));
211+
}
212+
}
213+
214+
Name newLocalName(String name) {
215+
return names.fromString("local" + target.syntheticNameChar() + name);
216+
}
217+
218+
class FieldRewriter extends TreeTranslator {
219+
JCMethodDecl md;
220+
Map<Symbol, Symbol> fieldToLocalMap;
221+
boolean ctorPrologue = true;
222+
223+
public FieldRewriter(JCMethodDecl md, Map<Symbol, Symbol> fieldToLocalMap) {
224+
this.md = md;
225+
this.fieldToLocalMap = fieldToLocalMap;
226+
}
227+
228+
@Override
229+
public void visitIdent(JCTree.JCIdent tree) {
230+
if (ctorPrologue && fieldToLocalMap.get(tree.sym) != null) {
231+
result = make.at(md).Ident(fieldToLocalMap.get(tree.sym));
232+
} else {
233+
result = tree;
234+
}
235+
}
236+
237+
@Override
238+
public void visitSelect(JCTree.JCFieldAccess tree) {
239+
super.visitSelect(tree);
240+
if (ctorPrologue && fieldToLocalMap.get(tree.sym) != null) {
241+
result = make.at(md).Ident(fieldToLocalMap.get(tree.sym));
242+
} else {
243+
result = tree;
244+
}
245+
}
246+
247+
@Override
248+
public void visitAssign(JCAssign tree) {
249+
JCExpression previousLHS = tree.lhs;
250+
super.visitAssign(tree);
251+
if (ctorPrologue && previousLHS != tree.lhs) {
252+
unsetFieldsInfo.removeUnsetFieldInfo(currentClass, tree);
253+
}
254+
}
255+
256+
@Override
257+
public void visitApply(JCTree.JCMethodInvocation tree) {
258+
Name methName = TreeInfo.name(tree.meth);
259+
boolean isConstructorCall = methName == names._this || methName == names._super;
260+
super.visitApply(tree);
261+
if (isConstructorCall) {
262+
ctorPrologue = false;
263+
}
264+
}
265+
}
266+
}

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public class Resolve {
115115
final EnumSet<VerboseResolutionMode> verboseResolutionMode;
116116
final boolean dumpMethodReferenceSearchResults;
117117
final boolean dumpStacktraceOnError;
118+
private final LocalProxyVarsGen localProxyVarsGen;
118119

119120
WriteableScope polymorphicSignatureScope;
120121

@@ -154,6 +155,7 @@ protected Resolve(Context context) {
154155
allowRecords = Feature.RECORDS.allowedInSource(source);
155156
dumpMethodReferenceSearchResults = options.isSet("debug.dumpMethodReferenceSearchResults");
156157
dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE);
158+
localProxyVarsGen = LocalProxyVarsGen.instance(context);
157159
}
158160

159161
/** error symbols, which are returned when resolution fails
@@ -1537,7 +1539,12 @@ Symbol findVar(DiagnosticPosition pos, Env<AttrContext> env, Name name) {
15371539
return new StaticError(sym);
15381540
if (env1.info.ctorPrologue && !isAllowedEarlyReference(pos, env1, (VarSymbol)sym)) {
15391541
if (!env.tree.hasTag(ASSIGN) || !TreeInfo.isIdentOrThisDotIdent(((JCAssign)env.tree).lhs)) {
1540-
return new RefBeforeCtorCalledError(sym);
1542+
if (!sym.isStrictInstance()) {
1543+
return new RefBeforeCtorCalledError(sym);
1544+
} else {
1545+
localProxyVarsGen.addStrictFieldReadInPrologue(env.enclMethod, sym);
1546+
return sym;
1547+
}
15411548
}
15421549
}
15431550
}

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,11 @@ public Set<VarSymbol> getUnsetFields(ClassSymbol csym, JCTree tree) {
8888
}
8989
return null;
9090
}
91+
92+
public void removeUnsetFieldInfo(ClassSymbol csym, JCTree tree) {
93+
Map<JCTree, Set<VarSymbol>> treeToFieldsMap = unsetFieldsMap.get(csym);
94+
if (treeToFieldsMap != null) {
95+
treeToFieldsMap.remove(tree);
96+
}
97+
}
9198
}

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ public boolean checkLimits(DiagnosticPosition pos, Log log) {
190190

191191
private Map<Integer, Set<VarSymbol>> cpToUnsetFieldsMap = new HashMap<>();
192192

193+
public Set<VarSymbol> initialUnsetFields;
194+
193195
public Set<VarSymbol> currentUnsetFields;
194196

195197
boolean generateAssertUnsetFieldsFrame;
@@ -1355,7 +1357,7 @@ void emitStackMapFrame(int pc, int localsSize) {
13551357
}
13561358

13571359
Set<VarSymbol> unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc);
1358-
boolean generateAssertUnsetFieldsEntry = unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame;
1360+
boolean generateAssertUnsetFieldsEntry = unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame && !lastFrame.unsetFields.equals(unsetFieldsAtPC) ;
13591361

13601362
if (stackMapTableBuffer == null) {
13611363
stackMapTableBuffer = new StackMapTableEntry[20];
@@ -1366,10 +1368,10 @@ void emitStackMapFrame(int pc, int localsSize) {
13661368
}
13671369

13681370
if (generateAssertUnsetFieldsEntry) {
1369-
if (lastFrame.unsetFields == null || !lastFrame.unsetFields.equals(unsetFieldsAtPC)) {
1370-
stackMapTableBuffer[stackMapBufferSize++] = new StackMapTableEntry.AssertUnsetFields(pc, unsetFieldsAtPC);
1371-
frame.unsetFields = unsetFieldsAtPC;
1372-
}
1371+
stackMapTableBuffer[stackMapBufferSize++] = new StackMapTableEntry.AssertUnsetFields(pc, unsetFieldsAtPC);
1372+
frame.unsetFields = unsetFieldsAtPC;
1373+
} else {
1374+
frame.unsetFields = lastFrame.unsetFields;
13731375
}
13741376
stackMapTableBuffer[stackMapBufferSize++] =
13751377
StackMapTableEntry.getInstance(frame, lastFrame, types, pc);
@@ -1403,6 +1405,7 @@ StackMapFrame getInitialFrame() {
14031405
}
14041406
frame.pc = -1;
14051407
frame.stack = null;
1408+
frame.unsetFields = initialUnsetFields;
14061409
return frame;
14071410
}
14081411

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,11 +586,11 @@ void rewriteInitializersIfNeeded(JCMethodDecl md, List<JCStatement> initCode) {
586586
}
587587
}
588588

589-
class InitializerVisitor extends TreeScanner {
589+
public static class InitializerVisitor extends TreeScanner {
590590
JCMethodDecl md;
591591
Set<JCExpression> exprSet;
592592

593-
InitializerVisitor(JCMethodDecl md, Set<JCExpression> exprSet) {
593+
public InitializerVisitor(JCMethodDecl md, Set<JCExpression> exprSet) {
594594
this.md = md;
595595
this.exprSet = exprSet;
596596
}
@@ -998,6 +998,7 @@ else if (tree.body != null) {
998998
Set<VarSymbol> prevUnsetFields = code.currentUnsetFields;
999999
if (meth.isConstructor()) {
10001000
code.currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body);
1001+
code.initialUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body);
10011002
}
10021003

10031004
try {

0 commit comments

Comments
 (0)