diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java index 451abea8a8c..ca0ecf5b20a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LocalProxyVarsGen.java @@ -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> fieldsReadInPrologue = new HashMap<>(); + private final Map> 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 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 thisMethodRollback = rollback.remove(tree); + + if (thisMethodRollback == null) { + return ; + } + + new TreeTranslator() { + @Override + @SuppressWarnings("unchecked") + public 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 rollback = new HashMap<>(); JCMethodDecl md; Map 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; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index 42a8d857d8f..6921400d3cf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -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 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 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. diff --git a/test/langtools/tools/javac/valhalla/value-objects/LocalProxyVariablesRuntime.java b/test/langtools/tools/javac/valhalla/value-objects/LocalProxyVariablesRuntime.java new file mode 100644 index 00000000000..42e33c7e445 --- /dev/null +++ b/test/langtools/tools/javac/valhalla/value-objects/LocalProxyVariablesRuntime.java @@ -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) {} + } +}