8388338: javac shares proxy initializer trees across constructors

Reviewed-by: liach, mcimadamore
This commit is contained in:
Jan Lahoda 2026-08-05 07:11:53 +00:00
parent d5336eb3f9
commit a935581a4d
3 changed files with 129 additions and 6 deletions

View File

@ -26,18 +26,17 @@
package com.sun.tools.javac.comp;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
@ -91,6 +90,7 @@ public class LocalProxyVarsGen {
private final Target target;
private TreeMaker make;
private final Map<Symbol, Set<Symbol>> fieldsReadInPrologue = new HashMap<>();
private final Map<JCMethodDecl, Map<JCTree, JCTree>> rollback = new HashMap<>();
private final boolean noLocalProxyVars;
@ -136,7 +136,7 @@ public class LocalProxyVarsGen {
}
}
public void allFieldNormalized(Symbol.ClassSymbol csym) {
public void classGenerated(ClassSymbol csym) {
fieldsReadInPrologue.remove(csym);
}
@ -164,6 +164,7 @@ public class LocalProxyVarsGen {
for (JCStatement st : constructor.body.stats) {
newBody = newBody.append(fieldRewriter.translate(st));
}
rollback.put(constructor, fieldRewriter.rollback);
localDeclarations.addAll(newBody);
ListBuffer<JCStatement> assigmentsBeforeSuper = new ListBuffer<>();
for (Symbol vsym : fieldToLocalMap.keySet()) {
@ -207,7 +208,27 @@ public class LocalProxyVarsGen {
return names.fromString("local" + target.syntheticNameChar() + name);
}
public void unpatchConstructor(JCMethodDecl tree, TreeMaker make) {
Map<JCTree, JCTree> thisMethodRollback = rollback.remove(tree);
if (thisMethodRollback == null) {
return ;
}
new TreeTranslator() {
@Override
@SuppressWarnings("unchecked")
public <T extends JCTree> T translate(T tree) {
if (tree != null && thisMethodRollback.containsKey(tree)) {
return (T) thisMethodRollback.get(tree);
}
return super.translate(tree);
}
}.translate(tree.body);
}
class FieldRewriter extends TreeTranslator {
Map<JCTree, JCTree> rollback = new HashMap<>();
JCMethodDecl md;
Map<Symbol, Symbol> fieldToLocalMap;
boolean ctorPrologue = true;
@ -221,6 +242,7 @@ public class LocalProxyVarsGen {
public void visitIdent(JCTree.JCIdent tree) {
if (ctorPrologue && fieldToLocalMap.get(tree.sym) != null) {
result = make.at(md).Ident(fieldToLocalMap.get(tree.sym));
rollback.put(result, tree);
} else {
result = tree;
}
@ -231,6 +253,7 @@ public class LocalProxyVarsGen {
super.visitSelect(tree);
if (ctorPrologue && fieldToLocalMap.get(tree.sym) != null) {
result = make.at(md).Ident(fieldToLocalMap.get(tree.sym));
rollback.put(result, tree);
} else {
result = tree;
}

View File

@ -493,7 +493,6 @@ public class Gen extends JCTree.Visitor {
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) {
@ -570,8 +569,6 @@ public class Gen extends JCTree.Visitor {
md.sym.appendUniqueTypeAttributes(initTAs);
}
localProxyVarsGen.patchConstructor(md, make);
if (md.body.bracePos == Position.NOPOS)
md.body.bracePos = TreeInfo.endPos(md.body.stats.last());
}
@ -981,6 +978,7 @@ public class Gen extends JCTree.Visitor {
int extras = 0;
// Count up extra parameters
if (meth.isConstructor()) {
localProxyVarsGen.patchConstructor(tree, make);
extras++;
if (meth.enclClass().isInner() &&
!meth.enclClass().isStatic()) {
@ -1052,6 +1050,9 @@ public class Gen extends JCTree.Visitor {
// Fill in type annotation positions for exception parameters
code.fillExceptionParameterPositions();
}
if (meth.isConstructor()) {
localProxyVarsGen.unpatchConstructor(tree, make);
}
}
private int initCode(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) {
@ -2567,6 +2568,7 @@ public class Gen extends JCTree.Visitor {
}
}
cdef.defs = List.nil(); // discard trees
localProxyVarsGen.classGenerated(c);
return nerrs == 0;
} finally {
// note: this method does NOT support recursion.

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 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.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @enablePreview
* @compile LocalProxyVariablesRuntime.java
* @run main LocalProxyVariablesRuntime
*/
public class LocalProxyVariablesRuntime {
public static void main(String... args) {
new Value1();
new Value1(0);
new Value2();
new Value2(0);
new Value3();
new Value3(0);
new Value4();
new Value4(0);
}
private static value class Value1 {
int i = 0;
int j = i + 1;
public Value1() {}
public Value1(int x) {}
}
private static value class Value2 {
Object f1 = new String("");
String f2 = f1 instanceof String s ? s : "";
public Value2() {}
public Value2(int x) {}
}
private static value class Value3 {
Object f1 = new String("");
String f2 = !switch (f1) {
case String s -> true;
default -> false;
} ? "a" : "b";
public Value3() {}
public Value3(int x) {}
}
private static value class Value4 {
Object f1 = new String("");
String f2 = switch (0) {
default -> {
boolean r;
switch (f1) {
case String s:
r = true;
break;
default:
r = false;
break;
}
IF: if (true) break IF;
for (int i = 0; i < 10; i++) {
if (i < 5) continue;
System.err.println(i);
}
yield r;
}
} ? "a" : "b";
public Value4() {}
public Value4(int x) {}
}
}