From 0cdbccabba344526c21be36b900664c9017d1e00 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 8 Nov 2024 10:48:46 -0500 Subject: [PATCH 01/28] 8343846: [lworld] implement spec changes to stack map tables --- .../jdk/internal/classfile/impl/CodeImpl.java | 8 +- .../com/sun/tools/javac/code/Symbol.java | 4 + .../com/sun/tools/javac/comp/Flow.java | 17 +- .../sun/tools/javac/comp/UnsetFieldsInfo.java | 96 +++++++++++ .../com/sun/tools/javac/jvm/ClassWriter.java | 64 +++++--- .../classes/com/sun/tools/javac/jvm/Code.java | 62 +++++-- .../classes/com/sun/tools/javac/jvm/Gen.java | 13 +- .../com/sun/tools/javac/tree/TreeInfo.java | 16 ++ .../com/sun/tools/classfile/ClassWriter.java | 19 ++- .../classfile/StackMapTable_attribute.java | 151 ++++++++---------- .../tools/classfile/StackMap_attribute.java | 14 +- .../com/sun/tools/javap/AttributeWriter.java | 6 +- .../tools/javap/stackmap/StackmapTest.java | 6 +- 13 files changed, 334 insertions(+), 142 deletions(-) create mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index aa9603b1508..2f6c22d05f3 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -290,8 +290,13 @@ else if (frameType < 128) { offsetDelta = frameType & 0x3f; p = adjustForObjectOrUninitialized(p + 1); } - else + else { + System.err.println("stop here"); switch (frameType) { + /*case 246 -> { + offsetDelta = 0; + p = adjustForObjectOrUninitialized(p + 3); + }*/ case 247 -> { offsetDelta = classReader.readU2(p + 1); p = adjustForObjectOrUninitialized(p + 3); @@ -324,6 +329,7 @@ else if (frameType < 128) { } default -> throw new IllegalArgumentException("Bad frame type: " + frameType); } + } bci += offsetDelta + 1; inflateLabel(bci); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index 3c08800421f..785e255f011 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -447,6 +447,10 @@ public boolean isImplicit() { return (flags_field & IMPLICIT_CLASS) != 0; } + public boolean isStrict() { + return (flags_field & STRICT) != 0; + } + /** Is this symbol declared (directly or indirectly) local * to a method or variable initializer? * Also includes fields of inner classes which are in diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 29ab8435ada..a1a86fd0798 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -27,6 +27,7 @@ package com.sun.tools.javac.comp; +import java.util.ArrayList; import java.util.Map; import java.util.Map.Entry; import java.util.HashMap; @@ -218,6 +219,7 @@ public class Flow { private Env attrEnv; private Lint lint; private final Infer infer; + private final UnsetFieldsInfo unsetFieldsInfo; public static Flow instance(Context context) { Flow instance = context.get(flowKey); @@ -344,7 +346,7 @@ protected Flow(Context context) { infer = Infer.instance(context); rs = Resolve.instance(context); diags = JCDiagnostic.Factory.instance(context); - Source source = Source.instance(context); + unsetFieldsInfo = UnsetFieldsInfo.instance(context); } /** @@ -2283,6 +2285,19 @@ void letInit(JCTree tree) { Symbol sym = TreeInfo.symbol(tree); if (sym.kind == VAR) { letInit(tree.pos(), (VarSymbol)sym); + if (isConstructor && sym.isStrict()) { + /* we are initializing a strict field inside of a constructor, we now need to find which fields + * haven't been initialized yet + */ + java.util.List unsetFields = new ArrayList<>(); + for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) { + JCVariableDecl variableDecl = vardecls[i]; + if (variableDecl.sym.isStrict()) { + unsetFields.add(variableDecl.sym); + } + } + unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree, unsetFields); + } } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java new file mode 100644 index 00000000000..765971e6118 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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 + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package com.sun.tools.javac.comp; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import com.sun.tools.javac.code.Symbol.ClassSymbol; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.util.Context; + +/** + * A Context class, that can keep useful information about unset fields. + * This information will be produced during flow analysis and used during + * code generation. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class UnsetFieldsInfo { + protected static final Context.Key unsetFieldsInfoKey = new Context.Key<>(); + + public static UnsetFieldsInfo instance(Context context) { + UnsetFieldsInfo instance = context.get(unsetFieldsInfoKey); + if (instance == null) + instance = new UnsetFieldsInfo(context); + return instance; + } + + @SuppressWarnings("this-escape") + protected UnsetFieldsInfo(Context context) { + context.put(unsetFieldsInfoKey, this); + } + + private WeakHashMap>> unsetFieldsMap = new WeakHashMap<>(); + + public void addUnsetFieldsInfo(ClassSymbol csym, JCTree tree, List unsetFields) { + Map> treeToFieldsMap = unsetFieldsMap.get(csym); + if (treeToFieldsMap == null) { + treeToFieldsMap = new HashMap<>(); + treeToFieldsMap.put(tree, unsetFields); + unsetFieldsMap.put(csym, treeToFieldsMap); + } else { + if (!treeToFieldsMap.containsKey(tree)) { + // only add if there is no info for the given tree + treeToFieldsMap.put(tree, unsetFields); + } + } + } + + public List getUnsetFields(ClassSymbol csym, JCTree tree) { + Map> treeToFieldsMap = unsetFieldsMap.get(csym); + if (treeToFieldsMap != null) { + List result = treeToFieldsMap.get(tree); + if (result != null) { + return result; + } + // it could be that we have stored a subtree of the tree the user has a reference to + for (JCTree subTree : treeToFieldsMap.keySet()) { + if (TreeInfo.contains(tree, subTree)) { + return treeToFieldsMap.get(subTree); + } + } + } + return null; + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 99ee7cef8b5..309443ddabf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -146,6 +146,7 @@ public class ClassWriter extends ClassFile { /** The tags and constants used in compressed stackmap. */ static final int SAME_FRAME_SIZE = 64; + static final int ASSERT_UNSET_FIELDS = 246; static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247; static final int SAME_FRAME_EXTENDED = 251; static final int FULL_FRAME = 255; @@ -1255,7 +1256,7 @@ void writeStackMap(Code code) { Assert.checkNull(code.stackMapBuffer); for (int i=0; i 0 && unsetFields != null && unsetFields.size() == 0) { + stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFields); + } + } + StackMapFrame getInitialFrame() { StackMapFrame frame = new StackMapFrame(); List arg_types = ((MethodType)meth.externalType(types)).argtypes; @@ -1633,6 +1653,14 @@ public void statBegin(int pos) { } } + /** Keep stat tree + */ + public JCTree statTree(JCTree statTree) { + JCTree prevStatTree = pendingStatTree; + pendingStatTree = statTree; + return prevStatTree; + } + /** Force stat begin eagerly */ public void markStatBegin() { 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 ff8facdb6af..60077c3f5fe 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 @@ -97,6 +97,8 @@ public static Gen instance(Context context) { */ final PoolWriter poolWriter; + private final UnsetFieldsInfo unsetFieldsInfo; + @SuppressWarnings("this-escape") protected Gen(Context context) { context.put(genKey, this); @@ -127,6 +129,7 @@ protected Gen(Context context) { 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; @@ -665,7 +668,12 @@ public void genStat(JCTree tree, Env env, int crtFlags) { public void genStat(JCTree tree, Env env) { if (code.isAlive()) { code.statBegin(tree.pos); - genDef(tree, env); + JCTree prevStatTree = code.statTree(tree); + try { + genDef(tree, env); + } finally { + code.statTree(prevStatTree); + } } else if (env.info.isSwitch && tree.hasTag(VARDEF)) { // variables whose declarations are in a switch // can be used even if the decl is unreachable. @@ -1064,7 +1072,8 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { : null, syms, types, - poolWriter); + poolWriter, + unsetFieldsInfo); items = new Items(poolWriter, code, syms, types); if (code.debugCode) { System.err.println(meth + " for body " + tree); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 5e3b043fb11..642787aef52 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -889,6 +889,22 @@ public static JCTree declarationFor(final Symbol sym, final JCTree tree) { return s.result; } + /** Find if a tree contains a subtree. */ + public static boolean contains(final JCTree tree, final JCTree subTree) { + var scanner = new TreeScanner() { + boolean result = false; + @Override + public void scan(JCTree tree) { + if (tree == subTree) { + result = true; + } + super.scan(tree); + } + }; + scanner.scan(tree); + return scanner.result; + } + /** Return the statement referenced by a label. * If the label refers to a loop or switch, return that switch * otherwise return the labelled statement itself diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java index d755e622612..9ab90614693 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java @@ -733,7 +733,7 @@ public Void visitStackMap(StackMap_attribute attr, ClassOutputStream out) { stackMapWriter = new StackMapTableWriter(); out.writeShort(attr.entries.length); - for (stack_map_frame f: attr.entries) + for (stack_map_entry f: attr.entries) stackMapWriter.write(f, out); return null; } @@ -744,7 +744,7 @@ public Void visitStackMapTable(StackMapTable_attribute attr, ClassOutputStream o stackMapWriter = new StackMapTableWriter(); out.writeShort(attr.entries.length); - for (stack_map_frame f: attr.entries) + for (stack_map_entry f: attr.entries) stackMapWriter.write(f, out); return null; } @@ -773,10 +773,10 @@ protected void writeAccessFlags(AccessFlags flags, ClassOutputStream out) { * Writer for the frames of StackMap and StackMapTable attributes. */ protected static class StackMapTableWriter - implements stack_map_frame.Visitor { + implements stack_map_entry.Visitor { - public void write(stack_map_frame frame, ClassOutputStream out) { - out.write(frame.frame_type); + public void write(stack_map_entry frame, ClassOutputStream out) { + out.write(frame.entry_type); frame.accept(this, out); } @@ -830,6 +830,15 @@ public Void visit_full_frame(full_frame frame, ClassOutputStream out) { return null; } + @Override + public Void visit_assert_unset_fields(assert_unset_fields frame, ClassOutputStream out) { + out.writeShort(frame.number_of_unset_fields); + for (int uf: frame.unset_fields) { + out.writeShort(uf); + } + return null; + } + protected void writeVerificationTypeInfo(verification_type_info info, ClassOutputStream out) { out.write(info.tag); diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java index a0f94f1bbd5..81f6bbf9117 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java @@ -47,17 +47,17 @@ static class InvalidStackMap extends AttributeException { throws IOException, InvalidStackMap { super(name_index, length); number_of_entries = cr.readUnsignedShort(); - entries = new stack_map_frame[number_of_entries]; + entries = new stack_map_entry[number_of_entries]; for (int i = 0; i < number_of_entries; i++) - entries[i] = stack_map_frame.read(cr); + entries[i] = stack_map_entry.read(cr); } - public StackMapTable_attribute(ConstantPool constant_pool, stack_map_frame[] entries) + public StackMapTable_attribute(ConstantPool constant_pool, stack_map_entry[] entries) throws ConstantPoolException { this(constant_pool.getUTF8Index(Attribute.StackMapTable), entries); } - public StackMapTable_attribute(int name_index, stack_map_frame[] entries) { + public StackMapTable_attribute(int name_index, stack_map_entry[] entries) { super(name_index, length(entries)); this.number_of_entries = entries.length; this.entries = entries; @@ -67,53 +67,51 @@ public R accept(Visitor visitor, D data) { return visitor.visitStackMapTable(this, data); } - static int length(stack_map_frame[] entries) { + static int length(stack_map_entry[] entries) { int n = 2; - for (stack_map_frame entry: entries) + for (stack_map_entry entry: entries) n += entry.length(); return n; } public final int number_of_entries; - public final stack_map_frame entries[]; + public final stack_map_entry entries[]; - public abstract static class stack_map_frame { - static stack_map_frame read(ClassReader cr) + public abstract static class stack_map_entry { + static stack_map_entry read(ClassReader cr) throws IOException, InvalidStackMap { - int frame_type = cr.readUnsignedByte(); - if (frame_type <= 63) - return new same_frame(frame_type); - else if (frame_type <= 127) - return new same_locals_1_stack_item_frame(frame_type, cr); - else if (frame_type <= 246) - throw new Error("unknown frame_type " + frame_type); - else if (frame_type == 247) - return new same_locals_1_stack_item_frame_extended(frame_type, cr); - else if (frame_type <= 250) - return new chop_frame(frame_type, cr); - else if (frame_type == 251) - return new same_frame_extended(frame_type, cr); - else if (frame_type <= 254) - return new append_frame(frame_type, cr); + int entry_type = cr.readUnsignedByte(); + if (entry_type <= 63) + return new same_frame(entry_type); + else if (entry_type <= 127) + return new same_locals_1_stack_item_frame(entry_type, cr); + else if (entry_type <= 245) + throw new Error("unknown frame_type " + entry_type); + else if (entry_type == 247) + return new same_locals_1_stack_item_frame_extended(entry_type, cr); + else if (entry_type <= 250) + return new chop_frame(entry_type, cr); + else if (entry_type == 251) + return new same_frame_extended(entry_type, cr); + else if (entry_type <= 254) + return new append_frame(entry_type, cr); else - return new full_frame(frame_type, cr); + return new full_frame(entry_type, cr); } - protected stack_map_frame(int frame_type) { - this.frame_type = frame_type; + protected stack_map_entry(int entry_type) { + this.entry_type = entry_type; } public int length() { return 1; } - public abstract int getOffsetDelta(); - public abstract R accept(Visitor visitor, D data); - public final int frame_type; + public final int entry_type; - public static interface Visitor { + public interface Visitor { R visit_same_frame(same_frame frame, P p); R visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame frame, P p); R visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended frame, P p); @@ -121,27 +119,24 @@ public static interface Visitor { R visit_same_frame_extended(same_frame_extended frame, P p); R visit_append_frame(append_frame frame, P p); R visit_full_frame(full_frame frame, P p); + R visit_assert_unset_fields(assert_unset_fields frame, P p); } } - public static class same_frame extends stack_map_frame { - same_frame(int frame_type) { - super(frame_type); + public static class same_frame extends stack_map_entry { + same_frame(int entry_type) { + super(entry_type); } public R accept(Visitor visitor, D data) { return visitor.visit_same_frame(this, data); } - - public int getOffsetDelta() { - return frame_type; - } } - public static class same_locals_1_stack_item_frame extends stack_map_frame { - same_locals_1_stack_item_frame(int frame_type, ClassReader cr) + public static class same_locals_1_stack_item_frame extends stack_map_entry { + same_locals_1_stack_item_frame(int entry_type, ClassReader cr) throws IOException, InvalidStackMap { - super(frame_type); + super(entry_type); stack = new verification_type_info[1]; stack[0] = verification_type_info.read(cr); } @@ -155,17 +150,13 @@ public R accept(Visitor visitor, D data) { return visitor.visit_same_locals_1_stack_item_frame(this, data); } - public int getOffsetDelta() { - return frame_type - 64; - } - public final verification_type_info[] stack; } - public static class same_locals_1_stack_item_frame_extended extends stack_map_frame { - same_locals_1_stack_item_frame_extended(int frame_type, ClassReader cr) + public static class same_locals_1_stack_item_frame_extended extends stack_map_entry { + same_locals_1_stack_item_frame_extended(int entry_type, ClassReader cr) throws IOException, InvalidStackMap { - super(frame_type); + super(entry_type); offset_delta = cr.readUnsignedShort(); stack = new verification_type_info[1]; stack[0] = verification_type_info.read(cr); @@ -180,17 +171,13 @@ public R accept(Visitor visitor, D data) { return visitor.visit_same_locals_1_stack_item_frame_extended(this, data); } - public int getOffsetDelta() { - return offset_delta; - } - public final int offset_delta; public final verification_type_info[] stack; } - public static class chop_frame extends stack_map_frame { - chop_frame(int frame_type, ClassReader cr) throws IOException { - super(frame_type); + public static class chop_frame extends stack_map_entry { + chop_frame(int entry_type, ClassReader cr) throws IOException { + super(entry_type); offset_delta = cr.readUnsignedShort(); } @@ -203,16 +190,12 @@ public R accept(Visitor visitor, D data) { return visitor.visit_chop_frame(this, data); } - public int getOffsetDelta() { - return offset_delta; - } - public final int offset_delta; } - public static class same_frame_extended extends stack_map_frame { - same_frame_extended(int frame_type, ClassReader cr) throws IOException { - super(frame_type); + public static class same_frame_extended extends stack_map_entry { + same_frame_extended(int entry_type, ClassReader cr) throws IOException { + super(entry_type); offset_delta = cr.readUnsignedShort(); } @@ -225,19 +208,15 @@ public R accept(Visitor visitor, D data) { return visitor.visit_same_frame_extended(this, data); } - public int getOffsetDelta() { - return offset_delta; - } - public final int offset_delta; } - public static class append_frame extends stack_map_frame { - append_frame(int frame_type, ClassReader cr) + public static class append_frame extends stack_map_entry { + append_frame(int entry_type, ClassReader cr) throws IOException, InvalidStackMap { - super(frame_type); + super(entry_type); offset_delta = cr.readUnsignedShort(); - locals = new verification_type_info[frame_type - 251]; + locals = new verification_type_info[entry_type - 251]; for (int i = 0; i < locals.length; i++) locals[i] = verification_type_info.read(cr); } @@ -254,18 +233,14 @@ public R accept(Visitor visitor, D data) { return visitor.visit_append_frame(this, data); } - public int getOffsetDelta() { - return offset_delta; - } - public final int offset_delta; public final verification_type_info[] locals; } - public static class full_frame extends stack_map_frame { - full_frame(int frame_type, ClassReader cr) + public static class full_frame extends stack_map_entry { + full_frame(int entry_type, ClassReader cr) throws IOException, InvalidStackMap { - super(frame_type); + super(entry_type); offset_delta = cr.readUnsignedShort(); number_of_locals = cr.readUnsignedShort(); locals = new verification_type_info[number_of_locals]; @@ -292,10 +267,6 @@ public R accept(Visitor visitor, D data) { return visitor.visit_full_frame(this, data); } - public int getOffsetDelta() { - return offset_delta; - } - public final int offset_delta; public final int number_of_locals; public final verification_type_info[] locals; @@ -303,6 +274,24 @@ public int getOffsetDelta() { public final verification_type_info[] stack; } + public static class assert_unset_fields extends stack_map_entry { + assert_unset_fields(int entry_type, ClassReader cr) throws IOException { + super(entry_type); + number_of_unset_fields = cr.readUnsignedShort(); + unset_fields = new int[number_of_unset_fields]; + for (int i = 0; i < number_of_unset_fields; i++) { + unset_fields[i] = cr.readUnsignedShort(); + } + } + + public R accept(Visitor visitor, D data) { + return visitor.visit_assert_unset_fields(this, data); + } + + public final int number_of_unset_fields; + public final int[] unset_fields; + } + public static class verification_type_info { public static final int ITEM_Top = 0; public static final int ITEM_Integer = 1; diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMap_attribute.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMap_attribute.java index e57a24bfa98..401b2e222b6 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMap_attribute.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMap_attribute.java @@ -38,17 +38,17 @@ public class StackMap_attribute extends Attribute { throws IOException, StackMapTable_attribute.InvalidStackMap { super(name_index, length); number_of_entries = cr.readUnsignedShort(); - entries = new stack_map_frame[number_of_entries]; + entries = new stack_map_entry[number_of_entries]; for (int i = 0; i < number_of_entries; i++) - entries[i] = new stack_map_frame(cr); + entries[i] = new stack_map_entry(cr); } - public StackMap_attribute(ConstantPool constant_pool, stack_map_frame[] entries) + public StackMap_attribute(ConstantPool constant_pool, stack_map_entry[] entries) throws ConstantPoolException { this(constant_pool.getUTF8Index(Attribute.StackMap), entries); } - public StackMap_attribute(int name_index, stack_map_frame[] entries) { + public StackMap_attribute(int name_index, stack_map_entry[] entries) { super(name_index, StackMapTable_attribute.length(entries)); this.number_of_entries = entries.length; this.entries = entries; @@ -59,10 +59,10 @@ public R accept(Visitor visitor, D data) { } public final int number_of_entries; - public final stack_map_frame entries[]; + public final stack_map_entry entries[]; - public static class stack_map_frame extends StackMapTable_attribute.full_frame { - stack_map_frame(ClassReader cr) + public static class stack_map_entry extends StackMapTable_attribute.full_frame { + stack_map_entry(ClassReader cr) throws IOException, StackMapTable_attribute.InvalidStackMap { super(255, cr); } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java index 266f9e12a5e..089a9b920e1 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java @@ -572,7 +572,7 @@ public void write(Attribute a, CodeAttribute lr) { int offsetDelta = lr.labelToBci(frame.target()) - lastOffset - 1; switch (frameType) { case 247 -> { - printHeader(frameType, "/* same_locals_1_stack_item_frame_extended */"); + printHeader(frameType, "/* same_locals_1_stack_item_entry_extended */"); indent(+1); println("offset_delta = " + offsetDelta); printMap("stack", frame.stack(), lr); @@ -585,7 +585,7 @@ public void write(Attribute a, CodeAttribute lr) { indent(-1); } case 251 -> { - printHeader(frameType, "/* same_frame_extended */"); + printHeader(frameType, "/* same_entry_extended */"); indent(+1); println("offset_delta = " + offsetDelta); indent(-1); @@ -600,7 +600,7 @@ public void write(Attribute a, CodeAttribute lr) { indent(-1); } case 255 -> { - printHeader(frameType, "/* full_frame */"); + printHeader(frameType, "/* full_entry */"); indent(+1); println("offset_delta = " + offsetDelta); printMap("locals", frame.locals(), lr); diff --git a/test/langtools/tools/javap/stackmap/StackmapTest.java b/test/langtools/tools/javap/stackmap/StackmapTest.java index 7fdaed746ef..d315845804f 100644 --- a/test/langtools/tools/javap/stackmap/StackmapTest.java +++ b/test/langtools/tools/javap/stackmap/StackmapTest.java @@ -66,10 +66,10 @@ public class StackmapTest { "}\n"; private static final String goldenOut = - " frame_type = 255 /* full_frame */\n" + - " frame_type = 255 /* full_frame */\n" + + " frame_type = 255 /* full_entry */\n" + + " frame_type = 255 /* full_entry */\n" + " frame_type = 73 /* same_locals_1_stack_item */\n" + - " frame_type = 255 /* full_frame */\n" + + " frame_type = 255 /* full_entry */\n" + " offset_delta = 19\n" + " offset_delta = 0\n" + " offset_delta = 2\n" + From 00a22e37a69086af44c4a36196b785c01a3f2d55 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 14 Nov 2024 18:24:19 -0500 Subject: [PATCH 02/28] additional changes --- .../share/classes/com/sun/tools/javac/util/Bits.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Bits.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Bits.java index fe795245610..3d242b67e02 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Bits.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Bits.java @@ -88,7 +88,10 @@ static BitsState getState(int[] someBits, boolean reset) { private static final int wordshift = 5; private static final int wordmask = wordlen - 1; - public int[] bits = null; + /* every int in the bits array is used to represent 32 bits, so the bits array will have + * length == 1 until we need to represent the 33rd bit and so on. + */ + private int[] bits = null; // This field will store last version of bits after every change. private static final int[] unassignedBits = new int[0]; From 87fd327ad00b237c788767a565b7ae95b45ad2ad Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 21 Jan 2025 16:51:17 -0500 Subject: [PATCH 03/28] removing duplicate method after merge --- .../share/classes/com/sun/tools/javac/code/Symbol.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index 7c6f1e1f725..645b20df9eb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -451,10 +451,6 @@ public boolean isImplicit() { return (flags_field & IMPLICIT_CLASS) != 0; } - public boolean isStrict() { - return (flags_field & STRICT) != 0; - } - /** Is this symbol declared (directly or indirectly) local * to a method or variable initializer? * Also includes fields of inner classes which are in From 3b4175f6bd023ec8ea95e983b8835d2e0f69a20d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 27 Jan 2025 12:08:17 -0500 Subject: [PATCH 04/28] experiments on classfile API side --- .../classfile/attribute/StackMapFrameInfo.java | 11 +++++++++-- .../java/lang/reflect/ProxyGenerator.java | 9 +++++---- .../jdk/internal/classfile/impl/CodeImpl.java | 6 +++--- .../classfile/impl/StackMapDecoder.java | 18 +++++++++++++++--- .../com/sun/tools/javac/jvm/ClassWriter.java | 7 ++++++- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java index d041a73c58a..99e74dad06e 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java @@ -63,17 +63,24 @@ public sealed interface StackMapFrameInfo */ List stack(); + /** + * {@return the expanded unset fields} + */ + List unSetFields(); + /** * {@return a new stack map frame} * @param target the location of the frame * @param locals the complete list of frame locals * @param stack the complete frame stack + * @param unSetFields the complete list of unset fields */ public static StackMapFrameInfo of(Label target, List locals, - List stack) { + List stack, + List unSetFields) { - return new StackMapDecoder.StackMapFrameImpl(255, target, locals, stack); + return new StackMapDecoder.StackMapFrameImpl(255, target, locals, stack, unSetFields); } /** diff --git a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java index 3807938100c..81e0b70ce5d 100644 --- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java +++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java @@ -611,8 +611,8 @@ private void generateStaticInitializer(ClassBuilder clb) { .invokespecial(cp.methodRefEntry(ncdfError, exInit)) .athrow(); cob.with(StackMapTableAttribute.of(List.of( - StackMapFrameInfo.of(c1, classLoaderLocal, throwableStack), - StackMapFrameInfo.of(c2, classLoaderLocal, throwableStack)))); + StackMapFrameInfo.of(c1, classLoaderLocal, throwableStack, List.of()), + StackMapFrameInfo.of(c2, classLoaderLocal, throwableStack, List.of())))); }); } @@ -650,6 +650,7 @@ private void generateLookupAccessor(ClassBuilder clb) { .with(StackMapTableAttribute.of(List.of( StackMapFrameInfo.of(failLabel, List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(mhl)), + List.of(), List.of())))); })); } @@ -748,8 +749,8 @@ private void generateMethod(ClassBuilder clb) { .invokespecial(uteInit) .athrow() .with(StackMapTableAttribute.of(List.of( - StackMapFrameInfo.of(c1, List.of(), throwableStack), - StackMapFrameInfo.of(c2, List.of(), throwableStack)))); + StackMapFrameInfo.of(c1, List.of(), throwableStack, List.of()), + StackMapFrameInfo.of(c2, List.of(), throwableStack, List.of())))); } })); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 68d53d0dd10..2b5c9f88754 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -292,10 +292,10 @@ else if (frameType < 128) { else { System.err.println("stop here"); switch (frameType) { - /*case 246 -> { - offsetDelta = 0; + case 246 -> { + offsetDelta = classReader.readU2(p + 1); p = adjustForObjectOrUninitialized(p + 3); - }*/ + } case 247 -> { offsetDelta = classReader.readU2(p + 1); p = adjustForObjectOrUninitialized(p + 3); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index f8e58ed2242..62884fe6272 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -50,6 +50,7 @@ public class StackMapDecoder { private static final int + ASSERT_UNSET_FIELDS = 246, SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251; private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {}; @@ -184,6 +185,7 @@ private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) { List entries() { p = pos; List locals = initFrameLocals, stack = List.of(); + List unSetFields = List.of(); int bci = -1; var entries = new StackMapFrameInfo[u2()]; for (int ei = 0; ei < entries.length; ei++) { @@ -195,11 +197,18 @@ List entries() { bci += frameType - 63; stack = List.of(readVerificationTypeInfo()); } else { - if (frameType < SAME_LOCALS_1_STACK_ITEM_EXTENDED) + if (frameType < ASSERT_UNSET_FIELDS) throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType); bci += u2() + 1; if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { stack = List.of(readVerificationTypeInfo()); + } else if (frameType == ASSERT_UNSET_FIELDS) { + stack = List.of(); + locals = List.of(); + var newUnsetFields = new Integer[u2()]; + for (int i=0; i entries() { entries[ei] = new StackMapFrameImpl(frameType, ctx.getLabel(bci), locals, - stack); + stack, + unSetFields); } return List.of(entries); } @@ -299,12 +309,14 @@ private int u2() { public static record StackMapFrameImpl(int frameType, Label target, List locals, - List stack) + List stack, + List unSetFields) implements StackMapFrameInfo { public StackMapFrameImpl { requireNonNull(target); locals = List.copyOf(locals); stack = List.copyOf(stack); + unSetFields = List.copyOf(unSetFields); } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 309443ddabf..326cfc7fe35 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1466,10 +1466,15 @@ static class AssertUnsetFields extends StackMapTablEntry { @Override void write(ClassWriter writer) { super.write(writer); + System.out.println("Writing unset field entry"); writer.databuf.appendChar(unsetFields.size()); for (VarSymbol vsym : unsetFields) { - writer.databuf.appendChar(writer.poolWriter.putNameAndType(vsym)); + //writer.databuf.appendChar(writer.poolWriter.putNameAndType(vsym)); + int index = writer.poolWriter.putNameAndType(vsym); + writer.databuf.appendChar(index); + System.out.println("Writing unset field: " + index + ", # of fields: " + unsetFields.size()); } + System.out.println("# of fields: " + unsetFields.size()); } } From dbcca52c1980e7dc931a4b9440bedbad20ea5770 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 27 Jan 2025 15:37:30 -0500 Subject: [PATCH 05/28] fixing bugs --- .../jdk/internal/classfile/impl/CodeImpl.java | 1 - .../com/sun/tools/javac/comp/Flow.java | 29 +++++-- .../sun/tools/javac/comp/UnsetFieldsInfo.java | 9 +-- .../com/sun/tools/javac/jvm/ClassWriter.java | 12 +-- .../classes/com/sun/tools/javac/jvm/Code.java | 79 +++++++++++++------ .../classes/com/sun/tools/javac/jvm/Gen.java | 66 ++++++++++------ 6 files changed, 127 insertions(+), 69 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 2b5c9f88754..913aa3ca5e2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -290,7 +290,6 @@ else if (frameType < 128) { p = adjustForObjectOrUninitialized(p + 1); } else { - System.err.println("stop here"); switch (frameType) { case 246 -> { offsetDelta = classReader.readU2(p + 1); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 9dbbb9a1352..3479bb4f9d3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -27,7 +27,6 @@ package com.sun.tools.javac.comp; -import java.util.ArrayList; import java.util.Map; import java.util.Map.Entry; import java.util.HashMap; @@ -35,7 +34,6 @@ import java.util.Set; import java.util.function.Consumer; -import com.sun.source.tree.CaseTree; import com.sun.source.tree.LambdaExpressionTree.BodyKind; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Scope.WriteableScope; @@ -61,8 +59,6 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*; import com.sun.tools.javac.util.JCDiagnostic.Fragment; import java.util.Arrays; -import java.util.Collections; -import java.util.IdentityHashMap; import java.util.Iterator; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -2300,6 +2296,10 @@ void uninit(VarSymbol sym) { * record an initialization of the variable. */ void letInit(JCTree tree) { + letInit(tree, (JCAssign) null); + } + + void letInit(JCTree tree, JCAssign assign) { tree = TreeInfo.skipParens(tree); if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { Symbol sym = TreeInfo.symbol(tree); @@ -2309,14 +2309,14 @@ void letInit(JCTree tree) { /* we are initializing a strict field inside of a constructor, we now need to find which fields * haven't been initialized yet */ - java.util.List unsetFields = new ArrayList<>(); + ListBuffer unsetFields = new ListBuffer<>(); for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) { JCVariableDecl variableDecl = vardecls[i]; if (variableDecl.sym.isStrict()) { - unsetFields.add(variableDecl.sym); + unsetFields.append(variableDecl.sym); } } - unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree, unsetFields); + unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, assign != null ? assign : tree, unsetFields.toList()); } } } @@ -2549,6 +2549,19 @@ public void visitMethodDef(JCMethodDecl tree) { */ initParam(def); } + if (isConstructor) { + ListBuffer unsetFields = new ListBuffer<>(); + for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) { + JCVariableDecl variableDecl = vardecls[i]; + if (variableDecl.sym.isStrict()) { + unsetFields.append(variableDecl.sym); + } + } + if (unsetFields != null && !unsetFields.isEmpty()) { + unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree.body, unsetFields.toList()); + } + } + // else we are in an instance initializer block; // leave caught unchanged. scan(tree.body); @@ -3168,7 +3181,7 @@ public void visitAssign(JCAssign tree) { if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs)) scanExpr(tree.lhs); scanExpr(tree.rhs); - letInit(tree.lhs); + letInit(tree.lhs, tree); } // check fields accessed through this. are definitely diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java index 765971e6118..43f180781ad 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java @@ -26,10 +26,11 @@ package com.sun.tools.javac.comp; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.WeakHashMap; +import com.sun.tools.javac.util.List; + import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.tree.JCTree; @@ -84,12 +85,6 @@ public List getUnsetFields(ClassSymbol csym, JCTree tree) { if (result != null) { return result; } - // it could be that we have stored a subtree of the tree the user has a reference to - for (JCTree subTree : treeToFieldsMap.keySet()) { - if (TreeInfo.contains(tree, subTree)) { - return treeToFieldsMap.get(subTree); - } - } } return null; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 326cfc7fe35..08f74dbc157 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1328,7 +1328,7 @@ abstract static class StackMapTablEntry { void write(ClassWriter writer) { int entryType = getEntryType(); writer.databuf.appendByte(entryType); - if (writer.debugstackmap) System.out.print(" frame_type=" + entryType); + if (writer.debugstackmap) System.out.println(" frame_type=" + entryType); } static class SameFrame extends StackMapTablEntry { @@ -1466,15 +1466,17 @@ static class AssertUnsetFields extends StackMapTablEntry { @Override void write(ClassWriter writer) { super.write(writer); - System.out.println("Writing unset field entry"); writer.databuf.appendChar(unsetFields.size()); + if (writer.debugstackmap) { + System.out.println(" # writing: AssertUnsetFields stackmap entry with " + unsetFields.size() + " fields"); + } for (VarSymbol vsym : unsetFields) { - //writer.databuf.appendChar(writer.poolWriter.putNameAndType(vsym)); int index = writer.poolWriter.putNameAndType(vsym); writer.databuf.appendChar(index); - System.out.println("Writing unset field: " + index + ", # of fields: " + unsetFields.size()); + if (writer.debugstackmap) { + System.out.println(" #writing unset field: " + index + ", with name: " + vsym.name.toString()); + } } - System.out.println("# of fields: " + unsetFields.size()); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index 3d1b5362cd1..f34ea65a2ae 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -30,6 +30,7 @@ import com.sun.tools.javac.comp.UnsetFieldsInfo; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -43,6 +44,8 @@ import static com.sun.tools.javac.jvm.UninitializedType.*; import static com.sun.tools.javac.jvm.ClassWriter.StackMapTablEntry; import java.util.Arrays; +import java.util.Map; +import java.util.HashMap; /** An internal structure that corresponds to the code attribute of * methods in a classfile. The class also provides some utility operations to @@ -165,8 +168,6 @@ public boolean checkLimits(DiagnosticPosition pos, Log log) { */ int pendingStatPos = Position.NOPOS; - JCTree pendingStatTree; - java.util.List unsetFields = null; /** Set true when a stackMap is needed at the current PC. */ @@ -193,6 +194,10 @@ public boolean checkLimits(DiagnosticPosition pos, Log log) { private UnsetFieldsInfo unsetFieldsInfo; + private Map> cpToUnsetFieldsMap = new HashMap<>(); + + public List currentUnsetFields; + /** Construct a code object, given the settings of the fatcode, * debugging info switches and the CharacterRangeTable. */ @@ -1010,9 +1015,6 @@ public void emitop2(int op, int od) { } public void emitop2(int op, int od, PoolConstant data) { - if (op == putfield) { - //System.err.println("stop here"); - } emitop(op); if (!alive) return; emit2(od); @@ -1061,10 +1063,15 @@ public void emitop2(int op, int od, PoolConstant data) { * here using the saved tree we should find if that tree is included in the map of trees with info of * unset fields and extract that info in case a stack map is generated after this instruction */ - unsetFields = unsetFieldsInfo.getUnsetFields((ClassSymbol) meth.owner, pendingStatTree); - if (unsetFields != null && unsetFields.size() == 0) { - emitClosingAssertUnsetFields(); - } + /*AssignScanner assignScanner = new AssignScanner(); + assignScanner.scan(pendingStatTree); + List assignList = assignScanner.assignList.toList(); + for (JCTree.JCAssign assign : assignList) { + unsetFields = unsetFieldsInfo.getUnsetFields((ClassSymbol) meth.owner, assign); + //if (unsetFields != null && unsetFields.size() == 0) { + // emitClosingAssertUnsetFields(); + //} + }*/ break; case getfield: state.pop(1); // object ref @@ -1090,7 +1097,16 @@ public void emitop2(int op, int od, PoolConstant data) { } // postop(); } - +/* + class AssignScanner extends TreeScanner { + ListBuffer assignList = new ListBuffer<>(); + @Override + public void visitAssign(JCTree.JCAssign tree) { + assignList.add(tree); + super.visitAssign(tree); + } + } +*/ /** Emit an opcode with a four-byte operand field. */ public void emitop4(int op, int od) { @@ -1237,6 +1253,7 @@ static class StackMapFrame { int pc; Type[] locals; Type[] stack; + List unsetFields; } /** A buffer of cldc stack map entries. */ @@ -1371,9 +1388,17 @@ void emitStackMapFrame(int pc, int localsSize) { stackMapTableBuffer, stackMapBufferSize); } - if (unsetFields != null) { - stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFields); - unsetFields = null; + List unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc); + if (unsetFieldsAtPC != null) { + if (lastFrame.unsetFields != null) { + if (!lastFrame.unsetFields.diff(unsetFieldsAtPC).isEmpty() || !unsetFieldsAtPC.diff(lastFrame.unsetFields).isEmpty()) { + stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFieldsAtPC); + frame.unsetFields = unsetFieldsAtPC; + } + } else { + stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFieldsAtPC); + frame.unsetFields = unsetFieldsAtPC; + } } stackMapTableBuffer[stackMapBufferSize++] = StackMapTablEntry.getInstance(frame, lastFrame.pc, lastFrame.locals, types); @@ -1382,6 +1407,10 @@ void emitStackMapFrame(int pc, int localsSize) { lastFrame = frame; } + public void addUnsetFieldsAtCP(int cp, List unsetFields) { + cpToUnsetFieldsMap.put(cp, unsetFields); + } + void emitClosingAssertUnsetFields() { // if we have emitted at least one frame if (stackMapBufferSize > 0 && unsetFields != null && unsetFields.size() == 0) { @@ -1488,6 +1517,9 @@ public Chain branch(int opcode) { result = new Chain(emitJump(opcode), result, state.dup()); + if (currentUnsetFields != null) { + addUnsetFieldsAtCP(result.pc, currentUnsetFields); + } fixedPc = fatcode; if (opcode == goto_) alive = false; } @@ -1499,6 +1531,7 @@ public Chain branch(int opcode) { public void resolve(Chain chain, int target) { boolean changed = false; State newState = state; + int originalTarget = target; for (; chain != null; chain = chain.next) { Assert.check(state != chain.state && (target > chain.pc || isStatementStart())); @@ -1525,13 +1558,21 @@ public void resolve(Chain chain, int target) { break; } } else { - if (fatcode) + if (fatcode) { put4(chain.pc + 1, target - chain.pc); + if (cpToUnsetFieldsMap.get(chain.pc) != null) { + addUnsetFieldsAtCP(originalTarget, cpToUnsetFieldsMap.get(chain.pc)); + } + } else if (target - chain.pc < Short.MIN_VALUE || target - chain.pc > Short.MAX_VALUE) fatcode = true; - else + else { put2(chain.pc + 1, target - chain.pc); + if (cpToUnsetFieldsMap.get(chain.pc) != null) { + addUnsetFieldsAtCP(originalTarget, cpToUnsetFieldsMap.get(chain.pc)); + } + } Assert.check(!alive || chain.state.stacksize == newState.stacksize && chain.state.nlocks == newState.nlocks); @@ -1653,14 +1694,6 @@ public void statBegin(int pos) { } } - /** Keep stat tree - */ - public JCTree statTree(JCTree statTree) { - JCTree prevStatTree = pendingStatTree; - pendingStatTree = statTree; - return prevStatTree; - } - /** Force stat begin eagerly */ public void markStatBegin() { 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 60077c3f5fe..5b93c1c9782 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 @@ -99,6 +99,8 @@ public static Gen instance(Context context) { private final UnsetFieldsInfo unsetFieldsInfo; + private List currentUnsetFields = List.nil(); + @SuppressWarnings("this-escape") protected Gen(Context context) { context.put(genKey, this); @@ -668,12 +670,7 @@ public void genStat(JCTree tree, Env env, int crtFlags) { public void genStat(JCTree tree, Env env) { if (code.isAlive()) { code.statBegin(tree.pos); - JCTree prevStatTree = code.statTree(tree); - try { - genDef(tree, env); - } finally { - code.statTree(prevStatTree); - } + genDef(tree, env); } else if (env.info.isSwitch && tree.hasTag(VARDEF)) { // variables whose declarations are in a switch // can be used even if the decl is unreachable. @@ -997,6 +994,11 @@ void genMethod(JCMethodDecl tree, Env env, boolean fatcode) { else if (tree.body != null) { // Create a new code structure and initialize it. int startpcCrt = initCode(tree, env, fatcode); + List prevUnsetFields = currentUnsetFields; + if (meth.isConstructor()) { + currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body); + code.currentUnsetFields = currentUnsetFields; + } try { genStat(tree.body, env); @@ -1004,6 +1006,9 @@ else if (tree.body != null) { // Failed due to code limit, try again with jsr/ret startpcCrt = initCode(tree, env, fatcode); genStat(tree.body, env); + } finally { + currentUnsetFields = prevUnsetFields; + code.currentUnsetFields = prevUnsetFields; } if (code.state.stacksize != 0) { @@ -1818,27 +1823,35 @@ void registerCatch(DiagnosticPosition pos, } 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); - thenExit = code.branch(goto_); - } - if (elseChain != null) { - code.resolve(elseChain); - if (tree.elsepart != null) { - genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET); + List prevUnsetFields = currentUnsetFields; + List prevCodeUnsetFields = code.currentUnsetFields; + try { + code.currentUnsetFields = currentUnsetFields; + 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); + thenExit = code.branch(goto_); + } + if (elseChain != null) { + code.resolve(elseChain); + if (tree.elsepart != null) { + genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET); + } } + code.resolve(thenExit); + code.endScopes(limit); + Assert.check(code.isStatementStart()); + } finally { + currentUnsetFields = prevUnsetFields; + code.currentUnsetFields = prevCodeUnsetFields; } - code.resolve(thenExit); - code.endScopes(limit); - Assert.check(code.isStatementStart()); } public void visitExec(JCExpressionStatement tree) { @@ -2148,6 +2161,9 @@ public void visitParens(JCParens tree) { public void visitAssign(JCAssign tree) { Item l = genExpr(tree.lhs, tree.lhs.type); + List tmpUnsetSymbols = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree); + currentUnsetFields = tmpUnsetSymbols != null ? tmpUnsetSymbols : currentUnsetFields; + code.currentUnsetFields = currentUnsetFields; genExpr(tree.rhs, tree.lhs.type).load(); if (tree.rhs.type.hasTag(BOT)) { /* This is just a case of widening reference conversion that per 5.1.5 simply calls From 25a5838f36c3d7afd27bcf88a123e9aebd059477 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 29 Jan 2025 15:04:35 -0500 Subject: [PATCH 06/28] additional cleanup --- .../classes/com/sun/tools/javac/jvm/Code.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index f34ea65a2ae..75da5e79ea7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -1059,19 +1059,6 @@ public void emitop2(int op, int od, PoolConstant data) { case putfield: state.pop(((Symbol)data).erasure(types)); state.pop(1); // object ref - /* - * here using the saved tree we should find if that tree is included in the map of trees with info of - * unset fields and extract that info in case a stack map is generated after this instruction - */ - /*AssignScanner assignScanner = new AssignScanner(); - assignScanner.scan(pendingStatTree); - List assignList = assignScanner.assignList.toList(); - for (JCTree.JCAssign assign : assignList) { - unsetFields = unsetFieldsInfo.getUnsetFields((ClassSymbol) meth.owner, assign); - //if (unsetFields != null && unsetFields.size() == 0) { - // emitClosingAssertUnsetFields(); - //} - }*/ break; case getfield: state.pop(1); // object ref @@ -1097,16 +1084,6 @@ public void emitop2(int op, int od, PoolConstant data) { } // postop(); } -/* - class AssignScanner extends TreeScanner { - ListBuffer assignList = new ListBuffer<>(); - @Override - public void visitAssign(JCTree.JCAssign tree) { - assignList.add(tree); - super.visitAssign(tree); - } - } -*/ /** Emit an opcode with a four-byte operand field. */ public void emitop4(int op, int od) { From 07ddeaba8b3dc82c956558535a243f756f36a4b4 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 29 Jan 2025 15:04:35 -0500 Subject: [PATCH 07/28] additional cleanup --- .../classes/com/sun/tools/javac/jvm/Code.java | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index f34ea65a2ae..8fc68496c34 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -1059,19 +1059,6 @@ public void emitop2(int op, int od, PoolConstant data) { case putfield: state.pop(((Symbol)data).erasure(types)); state.pop(1); // object ref - /* - * here using the saved tree we should find if that tree is included in the map of trees with info of - * unset fields and extract that info in case a stack map is generated after this instruction - */ - /*AssignScanner assignScanner = new AssignScanner(); - assignScanner.scan(pendingStatTree); - List assignList = assignScanner.assignList.toList(); - for (JCTree.JCAssign assign : assignList) { - unsetFields = unsetFieldsInfo.getUnsetFields((ClassSymbol) meth.owner, assign); - //if (unsetFields != null && unsetFields.size() == 0) { - // emitClosingAssertUnsetFields(); - //} - }*/ break; case getfield: state.pop(1); // object ref @@ -1097,16 +1084,6 @@ public void emitop2(int op, int od, PoolConstant data) { } // postop(); } -/* - class AssignScanner extends TreeScanner { - ListBuffer assignList = new ListBuffer<>(); - @Override - public void visitAssign(JCTree.JCAssign tree) { - assignList.add(tree); - super.visitAssign(tree); - } - } -*/ /** Emit an opcode with a four-byte operand field. */ public void emitop4(int op, int od) { @@ -1411,13 +1388,6 @@ public void addUnsetFieldsAtCP(int cp, List unsetFields) { cpToUnsetFieldsMap.put(cp, unsetFields); } - void emitClosingAssertUnsetFields() { - // if we have emitted at least one frame - if (stackMapBufferSize > 0 && unsetFields != null && unsetFields.size() == 0) { - stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFields); - } - } - StackMapFrame getInitialFrame() { StackMapFrame frame = new StackMapFrame(); List arg_types = ((MethodType)meth.externalType(types)).argtypes; From fb7775c90e21c857e86b1e2d39c75500e5812063 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 29 Jan 2025 15:56:26 -0500 Subject: [PATCH 08/28] support for loops, try, switch --- .../classes/com/sun/tools/javac/jvm/Gen.java | 531 +++++++++--------- 1 file changed, 280 insertions(+), 251 deletions(-) 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 5b93c1c9782..029ba6c7afe 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 @@ -1216,28 +1216,11 @@ private void genLoop(JCStatement loop, boolean testFirst) { Env loopEnv = env.dup(loop, new GenContext()); int startpc = code.entryPoint(); - if (testFirst) { //while or for loop - CondItem c; - if (cond != null) { - code.statBegin(cond.pos); - Assert.check(code.isStatementStart()); - c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); - } else { - c = items.makeCondItem(goto_); - } - Chain loopDone = c.jumpFalse(); - code.resolve(c.trueJumps); - Assert.check(code.isStatementStart()); - genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); - code.resolve(loopEnv.info.cont); - genStats(step, loopEnv); - code.resolve(code.branch(goto_), startpc); - code.resolve(loopDone); - } else { - genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); - code.resolve(loopEnv.info.cont); - genStats(step, loopEnv); - if (code.isAlive()) { + List prevUnsetFields = currentUnsetFields; + List prevCodeUnsetFields = code.currentUnsetFields; + try { + code.currentUnsetFields = currentUnsetFields; + if (testFirst) { //while or for loop CondItem c; if (cond != null) { code.statBegin(cond.pos); @@ -1246,15 +1229,40 @@ private void genLoop(JCStatement loop, } else { c = items.makeCondItem(goto_); } - code.resolve(c.jumpTrue(), startpc); + Chain loopDone = c.jumpFalse(); + code.resolve(c.trueJumps); Assert.check(code.isStatementStart()); - code.resolve(c.falseJumps); + genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); + code.resolve(loopEnv.info.cont); + genStats(step, loopEnv); + code.resolve(code.branch(goto_), startpc); + code.resolve(loopDone); + } else { + genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); + code.resolve(loopEnv.info.cont); + genStats(step, loopEnv); + if (code.isAlive()) { + CondItem c; + if (cond != null) { + code.statBegin(cond.pos); + Assert.check(code.isStatementStart()); + c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); + } else { + c = items.makeCondItem(goto_); + } + code.resolve(c.jumpTrue(), startpc); + Assert.check(code.isStatementStart()); + code.resolve(c.falseJumps); + } } - } - Chain exit = loopEnv.info.exit; - if (exit != null) { - code.resolve(exit); - exit.state.defined.excludeFrom(code.nextreg); + Chain exit = loopEnv.info.exit; + if (exit != null) { + code.resolve(exit); + exit.state.defined.excludeFrom(code.nextreg); + } + } finally { + currentUnsetFields = prevUnsetFields; + code.currentUnsetFields = prevCodeUnsetFields; } } @@ -1280,11 +1288,16 @@ public void visitSwitch(JCSwitch tree) { public void visitSwitchExpression(JCSwitchExpression tree) { code.resolvePending(); boolean prevInCondSwitchExpression = inCondSwitchExpression; + List prevUnsetFields = currentUnsetFields; + List prevCodeUnsetFields = code.currentUnsetFields; try { + code.currentUnsetFields = currentUnsetFields; inCondSwitchExpression = false; doHandleSwitchExpression(tree); } finally { inCondSwitchExpression = prevInCondSwitchExpression; + currentUnsetFields = prevUnsetFields; + code.currentUnsetFields = prevCodeUnsetFields; } result = items.makeStackItem(pt); } @@ -1360,159 +1373,167 @@ public void visitLambda(JCLambda tree) { private void handleSwitch(JCTree swtch, JCExpression selector, List 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()); - Item sel = genExpr(selector, syms.intType); - if (cases.isEmpty()) { - // We are seeing: switch {} - 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 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. - int lo = Integer.MAX_VALUE; // minimum label. - int hi = Integer.MIN_VALUE; // maximum label. - int nlabels = 0; // number of labels. - - int[] labels = new int[cases.length()]; // the label array. - int defaultIndex = -1; // the index of the default clause. - - List l = cases; - for (int i = 0; i < labels.length; i++) { - if (l.head.labels.head instanceof JCConstantCaseLabel constLabel) { - Assert.check(l.head.labels.size() == 1); - int val = ((Number) constLabel.expr.type.constValue()).intValue(); - labels[i] = val; - if (val < lo) lo = val; - if (hi < val) hi = val; - nlabels++; - } else { - Assert.check(defaultIndex == -1); - defaultIndex = i; - } - l = l.tail; - } - - // Determine whether to issue a tableswitch or a lookupswitch - // instruction. - 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 - int[] offsets = null; // a table of offsets for a lookupswitch - code.emit4(-1); // leave space for default offset - if (opcode == tableswitch) { - code.emit4(lo); // minimum label - code.emit4(hi); // maximum label - for (long i = lo; i <= hi; i++) { // leave space for jump table - code.emit4(-1); - } + List prevUnsetFields = currentUnsetFields; + List prevCodeUnsetFields = code.currentUnsetFields; + try { + code.currentUnsetFields = currentUnsetFields; + 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()); + Item sel = genExpr(selector, syms.intType); + if (cases.isEmpty()) { + // We are seeing: switch {} + sel.load().drop(); + if (genCrt) + code.crt.put(TreeInfo.skipParens(selector), + CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); } else { - code.emit4(nlabels); // number of labels - for (int i = 0; i < nlabels; i++) { - code.emit4(-1); code.emit4(-1); // leave space for lookup table - } - offsets = new int[labels.length]; - } - Code.State stateSwitch = code.state.dup(); - code.markDead(); - - // For each case do: - l = cases; - for (int i = 0; i < labels.length; i++) { - JCCase c = l.head; - l = l.tail; - - int pc = code.entryPoint(stateSwitch); - // 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); + // We are seeing a nonempty switch. + sel.load(); + if (genCrt) + code.crt.put(TreeInfo.skipParens(selector), + CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); + Env 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. + int lo = Integer.MAX_VALUE; // minimum label. + int hi = Integer.MIN_VALUE; // maximum label. + int nlabels = 0; // number of labels. + + int[] labels = new int[cases.length()]; // the label array. + int defaultIndex = -1; // the index of the default clause. + + List l = cases; + for (int i = 0; i < labels.length; i++) { + if (l.head.labels.head instanceof JCConstantCaseLabel constLabel) { + Assert.check(l.head.labels.size() == 1); + int val = ((Number) constLabel.expr.type.constValue()).intValue(); + labels[i] = val; + if (val < lo) lo = val; + if (hi < val) hi = val; + nlabels++; } else { - offsets[i] = pc - startpc; + Assert.check(defaultIndex == -1); + defaultIndex = i; + } + l = l.tail; + } + + // Determine whether to issue a tableswitch or a lookupswitch + // instruction. + 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 + int[] offsets = null; // a table of offsets for a lookupswitch + code.emit4(-1); // leave space for default offset + if (opcode == tableswitch) { + code.emit4(lo); // minimum label + code.emit4(hi); // maximum label + for (long i = lo; i <= hi; i++) { // leave space for jump table + code.emit4(-1); } } else { - code.put4(tableBase, pc - startpc); + code.emit4(nlabels); // number of labels + for (int i = 0; i < nlabels; i++) { + code.emit4(-1); code.emit4(-1); // leave space for lookup table + } + offsets = new int[labels.length]; } + Code.State stateSwitch = code.state.dup(); + code.markDead(); - // Generate code for the statements in this case. - genStats(c.stats, switchEnv, CRT_FLOW_TARGET); - } + // For each case do: + l = cases; + for (int i = 0; i < labels.length; i++) { + JCCase c = l.head; + l = l.tail; + + int pc = code.entryPoint(stateSwitch); + // 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); + } - if (switchEnv.info.cont != null) { - Assert.check(patternSwitch); - code.resolve(switchEnv.info.cont, switchStart); - } + // Generate code for the statements in this case. + genStats(c.stats, switchEnv, CRT_FLOW_TARGET); + } - // Resolve all breaks. - Chain exit = switchEnv.info.exit; - if (exit != null) { - code.resolve(exit); - exit.state.defined.excludeFrom(limit); - } + if (switchEnv.info.cont != null) { + Assert.check(patternSwitch); + code.resolve(switchEnv.info.cont, switchStart); + } - // If we have not set the default offset, we do so now. - if (code.get4(tableBase) == -1) { - code.put4(tableBase, code.entryPoint(stateSwitch) - startpc); - } + // Resolve all breaks. + Chain exit = switchEnv.info.exit; + if (exit != null) { + code.resolve(exit); + exit.state.defined.excludeFrom(limit); + } - if (opcode == tableswitch) { - // Let any unfilled slots point to the default case. - int defaultOffset = code.get4(tableBase); - for (long i = lo; i <= hi; i++) { - int t = (int)(tableBase + 4 * (i - lo + 3)); - if (code.get4(t) == -1) - code.put4(t, defaultOffset); + // If we have not set the default offset, we do so now. + if (code.get4(tableBase) == -1) { + code.put4(tableBase, code.entryPoint(stateSwitch) - startpc); } - } else { - // Sort non-default offsets and copy into lookup table. - if (defaultIndex >= 0) - for (int i = defaultIndex; i < labels.length - 1; i++) { - labels[i] = labels[i+1]; - offsets[i] = offsets[i+1]; + + if (opcode == tableswitch) { + // Let any unfilled slots point to the default case. + int defaultOffset = code.get4(tableBase); + for (long i = lo; i <= hi; i++) { + int t = (int)(tableBase + 4 * (i - lo + 3)); + if (code.get4(t) == -1) + code.put4(t, defaultOffset); + } + } else { + // Sort non-default offsets and copy into lookup table. + if (defaultIndex >= 0) + for (int i = defaultIndex; i < labels.length - 1; i++) { + labels[i] = labels[i+1]; + offsets[i] = offsets[i+1]; + } + if (nlabels > 0) + qsort2(labels, offsets, 0, nlabels - 1); + for (int i = 0; i < nlabels; i++) { + int caseidx = tableBase + 8 * (i + 1); + code.put4(caseidx, labels[i]); + code.put4(caseidx + 4, offsets[i]); } - if (nlabels > 0) - qsort2(labels, offsets, 0, nlabels - 1); - for (int i = 0; i < nlabels; i++) { - int caseidx = tableBase + 8 * (i + 1); - code.put4(caseidx, labels[i]); - 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)); + if (swtch instanceof JCSwitchExpression) { + // Emit line position for the end of a switch expression + code.statBegin(TreeInfo.endPos(swtch)); + } } + code.endScopes(limit); + } finally { + currentUnsetFields = prevUnsetFields; + code.currentUnsetFields = prevCodeUnsetFields; } - code.endScopes(limit); } //where /** Sort (int) arrays of keys and values @@ -1612,103 +1633,111 @@ void afterBody() { * @param env The current environment of the body. */ void genTry(JCTree body, List catchers, Env env) { - int limit = code.nextreg; - int startpc = code.curCP(); - Code.State stateTry = code.state.dup(); - genStat(body, env, CRT_BLOCK); - int endpc = code.curCP(); - List gaps = env.info.gaps.toList(); - code.statBegin(TreeInfo.endPos(body)); - genFinalizer(env); - code.statBegin(TreeInfo.endPos(env.tree)); - Chain exitChain; - boolean actualTry = env.tree.hasTag(TRY); - if (startpc == endpc && actualTry) { - exitChain = code.branch(dontgoto); - } else { - 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 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); + List prevUnsetFields = currentUnsetFields; + List prevCodeUnsetFields = code.currentUnsetFields; + try { + code.currentUnsetFields = currentUnsetFields; + int limit = code.nextreg; + int startpc = code.curCP(); + Code.State stateTry = code.state.dup(); + genStat(body, env, CRT_BLOCK); + int endpc = code.curCP(); + List gaps = env.info.gaps.toList(); + code.statBegin(TreeInfo.endPos(body)); genFinalizer(env); - if (hasFinalizer || l.tail.nonEmpty()) { - code.statBegin(TreeInfo.endPos(env.tree)); - exitChain = Code.mergeChains(exitChain, - code.branch(goto_)); + code.statBegin(TreeInfo.endPos(env.tree)); + Chain exitChain; + boolean actualTry = env.tree.hasTag(TRY); + if (startpc == endpc && actualTry) { + exitChain = code.branch(dontgoto); + } else { + exitChain = code.branch(goto_); } endFinalizerGap(env); - } - if (hasFinalizer && (startpc != endpc || !actualTry)) { - // Create a new register segment to avoid allocating - // the same variables in finalizers and other statements. - code.newRegSegment(); - - // Add a catch-all clause. - - // start off with exception on stack - int catchallpc = code.entryPoint(stateTry, syms.throwableType); - - // Register all exception ranges for catch all clause. - // The range of the catch all clause is from the beginning - // of the try or synchronized block until the present - // code pointer excluding all gaps in the current - // 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(); + env.info.finalize.afterBody(); + boolean hasFinalizer = + env.info.finalize != null && + env.info.finalize.hasFinalizer(); + if (startpc != endpc) for (List 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); } - code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); - code.markStatBegin(); + if (hasFinalizer && (startpc != endpc || !actualTry)) { + // Create a new register segment to avoid allocating + // the same variables in finalizers and other statements. + code.newRegSegment(); + + // Add a catch-all clause. + + // start off with exception on stack + int catchallpc = code.entryPoint(stateTry, syms.throwableType); + + // Register all exception ranges for catch all clause. + // The range of the catch all clause is from the beginning + // of the try or synchronized block until the present + // code pointer excluding all gaps in the current + // 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(); - Item excVar = makeTemp(syms.throwableType); - excVar.store(); - genFinalizer(env); - code.resolvePending(); - 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(); + Item excVar = makeTemp(syms.throwableType); + excVar.store(); + genFinalizer(env); + code.resolvePending(); + code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.END_POS)); + code.markStatBegin(); - // If there are jsr's to this finalizer, ... - if (env.info.cont != null) { - // Resolve all jsr's. - code.resolve(env.info.cont); + excVar.load(); + registerCatch(body.pos(), startseg, + env.info.gaps.next().intValue(), + catchallpc, 0); + code.emitop0(athrow); + code.markDead(); - // Mark statement line number - code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); - code.markStatBegin(); + // If there are jsr's to this finalizer, ... + if (env.info.cont != null) { + // Resolve all jsr's. + code.resolve(env.info.cont); - // Save return address. - LocalItem retVar = makeTemp(syms.throwableType); - retVar.store(); + // Mark statement line number + code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); + code.markStatBegin(); - // Generate finalizer code. - env.info.finalize.genLast(); + // Save return address. + LocalItem retVar = makeTemp(syms.throwableType); + retVar.store(); - // Return. - code.emitop1w(ret, retVar.reg); - code.markDead(); + // Generate finalizer code. + env.info.finalize.genLast(); + + // Return. + code.emitop1w(ret, retVar.reg); + code.markDead(); + } } - } - // Resolve all breaks. - code.resolve(exitChain); + // Resolve all breaks. + code.resolve(exitChain); - code.endScopes(limit); + code.endScopes(limit); + } finally { + currentUnsetFields = prevUnsetFields; + code.currentUnsetFields = prevCodeUnsetFields; + } } /** Generate code for a catch clause. From 739b111796a8560a0e76707354eced617c26893b Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 30 Jan 2025 13:34:42 -0500 Subject: [PATCH 09/28] fixing bug, array out of bounds --- .../share/classes/com/sun/tools/javac/jvm/Code.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index 8fc68496c34..cde92536c70 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -1371,10 +1371,16 @@ void emitStackMapFrame(int pc, int localsSize) { if (!lastFrame.unsetFields.diff(unsetFieldsAtPC).isEmpty() || !unsetFieldsAtPC.diff(lastFrame.unsetFields).isEmpty()) { stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFieldsAtPC); frame.unsetFields = unsetFieldsAtPC; + stackMapTableBuffer = ArrayUtils.ensureCapacity( + stackMapTableBuffer, + stackMapBufferSize); } } else { stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFieldsAtPC); frame.unsetFields = unsetFieldsAtPC; + stackMapTableBuffer = ArrayUtils.ensureCapacity( + stackMapTableBuffer, + stackMapBufferSize); } } stackMapTableBuffer[stackMapBufferSize++] = From 9a30940952178eb5845b5befa0280cccfaba4a72 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 30 Jan 2025 14:07:33 -0500 Subject: [PATCH 10/28] small verification changes --- src/hotspot/share/classfile/stackMapTable.cpp | 2 +- src/hotspot/share/classfile/stackMapTable.hpp | 1 + .../internal/classfile/impl/verifier/VerificationTable.java | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/classfile/stackMapTable.cpp b/src/hotspot/share/classfile/stackMapTable.cpp index fab7bcc7779..8593cc3dfaf 100644 --- a/src/hotspot/share/classfile/stackMapTable.cpp +++ b/src/hotspot/share/classfile/stackMapTable.cpp @@ -273,7 +273,7 @@ StackMapFrame* StackMapReader::next( u2 offset_delta = _stream->get_u2(CHECK_NULL); - if (frame_type < SAME_LOCALS_1_STACK_ITEM_EXTENDED) { + if (frame_type < ASSERT_UNSET_FIELDS) { // reserved frame types _stream->stackmap_format_error( "reserved frame type", CHECK_VERIFY_(_verifier, nullptr)); diff --git a/src/hotspot/share/classfile/stackMapTable.hpp b/src/hotspot/share/classfile/stackMapTable.hpp index a8ec3fc45db..534889293db 100644 --- a/src/hotspot/share/classfile/stackMapTable.hpp +++ b/src/hotspot/share/classfile/stackMapTable.hpp @@ -134,6 +134,7 @@ class StackMapReader : StackObj { } enum { + ASSERT_UNSET_FIELDS = 246, SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251, FULL = 255 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java index af009297405..4741f677df9 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java @@ -159,6 +159,7 @@ void check_verification_type_array_size(int size, int max_size) { } private static final int + ASSERT_UNSET_FIELDS = 246, SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251, FULL = 255; @@ -278,7 +279,7 @@ public VerificationFrame next(VerificationFrame pre_frame, boolean first, int ma return frame; } int offset_delta = _stream.get_u2(); - if (frame_type < SAME_LOCALS_1_STACK_ITEM_EXTENDED) { + if (frame_type < ASSERT_UNSET_FIELDS) { _verifier.classError("reserved frame type"); } if (frame_type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { From cf906da559787b21b6f4d014ab440aeab59b2dc7 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 30 Jan 2025 14:58:28 -0500 Subject: [PATCH 11/28] add option to generate new stackmap table frame --- .../share/classes/com/sun/tools/javac/jvm/Code.java | 8 ++++++-- .../share/classes/com/sun/tools/javac/jvm/Gen.java | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index cde92536c70..16ee1bc4075 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -198,6 +198,8 @@ public boolean checkLimits(DiagnosticPosition pos, Log log) { public List currentUnsetFields; + boolean generateAssertUnsetFieldsFrame; + /** Construct a code object, given the settings of the fatcode, * debugging info switches and the CharacterRangeTable. */ @@ -211,7 +213,8 @@ public Code(MethodSymbol meth, Symtab syms, Types types, PoolWriter poolWriter, - UnsetFieldsInfo unsetFieldsInfo) { + UnsetFieldsInfo unsetFieldsInfo, + boolean generateAssertUnsetFieldsFrame) { this.meth = meth; this.fatcode = fatcode; this.lineMap = lineMap; @@ -234,6 +237,7 @@ public Code(MethodSymbol meth, state = new State(); lvar = new LocalVar[20]; this.unsetFieldsInfo = unsetFieldsInfo; + this.generateAssertUnsetFieldsFrame = generateAssertUnsetFieldsFrame; } @@ -1366,7 +1370,7 @@ void emitStackMapFrame(int pc, int localsSize) { stackMapBufferSize); } List unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc); - if (unsetFieldsAtPC != null) { + if (unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame) { if (lastFrame.unsetFields != null) { if (!lastFrame.unsetFields.diff(unsetFieldsAtPC).isEmpty() || !unsetFieldsAtPC.diff(lastFrame.unsetFields).isEmpty()) { stackMapTableBuffer[stackMapBufferSize++] = new StackMapTablEntry.AssertUnsetFields(unsetFieldsAtPC); 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 029ba6c7afe..f601235812e 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 @@ -137,6 +137,7 @@ protected Gen(Context context) { this.stackMap = StackMapFormat.JSR202; annotate = Annotate.instance(context); qualifiedSymbolCache = new HashMap<>(); + generateAssertUnsetFieldsFrame = options.isSet("generateAssertUnsetFieldsFrame"); } /** Switches @@ -146,6 +147,7 @@ protected Gen(Context context) { private final boolean genCrt; private final boolean debugCode; private boolean disableVirtualizedPrivateInvoke; + private boolean generateAssertUnsetFieldsFrame; /** Code buffer, set by genMethod. */ @@ -1067,6 +1069,9 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { MethodSymbol meth = tree.sym; // Create a new code structure. + if (generateAssertUnsetFieldsFrame) { + System.err.println("will generate generateAssertUnsetFieldsFrame"); + } meth.code = code = new Code(meth, fatcode, lineDebugInfo ? toplevel.lineMap : null, @@ -1078,7 +1083,8 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { syms, types, poolWriter, - unsetFieldsInfo); + unsetFieldsInfo, + generateAssertUnsetFieldsFrame); items = new Items(poolWriter, code, syms, types); if (code.debugCode) { System.err.println(meth + " for body " + tree); From 64876e2b4ad00b90e536f2244da50ffe210b00a2 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 30 Jan 2025 15:16:21 -0500 Subject: [PATCH 12/28] removing debug code --- .../share/classes/com/sun/tools/javac/jvm/Gen.java | 3 --- 1 file changed, 3 deletions(-) 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 f601235812e..ab6843d0c28 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 @@ -1069,9 +1069,6 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { MethodSymbol meth = tree.sym; // Create a new code structure. - if (generateAssertUnsetFieldsFrame) { - System.err.println("will generate generateAssertUnsetFieldsFrame"); - } meth.code = code = new Code(meth, fatcode, lineDebugInfo ? toplevel.lineMap : null, From 0d2b44a89a7de89e597aff8790880d2c46464955 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 31 Jan 2025 14:31:08 -0500 Subject: [PATCH 13/28] minor changes to classfile API --- .../classes/jdk/internal/classfile/impl/CodeImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 913aa3ca5e2..f2466b75249 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -280,7 +280,7 @@ private void inflateJumpTargets() { int p = stackMapPos + 2; for (int i = 0; i < nEntries; ++i) { int frameType = classReader.readU1(p); - int offsetDelta; + int offsetDelta = -1; if (frameType < 64) { offsetDelta = frameType; ++p; @@ -292,8 +292,10 @@ else if (frameType < 128) { else { switch (frameType) { case 246 -> { - offsetDelta = classReader.readU2(p + 1); - p = adjustForObjectOrUninitialized(p + 3); + int numberOfUnsetFields = classReader.readU2(p + 1); + offsetDelta = offsetDelta == -1 ? 0 : offsetDelta; + p += 3; + p += 2 * numberOfUnsetFields; //adjustForObjectOrUninitialized(p + 3); } case 247 -> { offsetDelta = classReader.readU2(p + 1); From dec0a31aeee8ef96d9586c38a5a16c01837a7f1f Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 3 Feb 2025 11:03:20 -0500 Subject: [PATCH 14/28] fixing minor bug in Gen::visitAssign --- src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ab6843d0c28..36e4e889199 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 @@ -2193,10 +2193,10 @@ public void visitParens(JCParens tree) { public void visitAssign(JCAssign tree) { Item l = genExpr(tree.lhs, tree.lhs.type); + genExpr(tree.rhs, tree.lhs.type).load(); List tmpUnsetSymbols = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree); currentUnsetFields = tmpUnsetSymbols != null ? tmpUnsetSymbols : currentUnsetFields; code.currentUnsetFields = currentUnsetFields; - genExpr(tree.rhs, tree.lhs.type).load(); 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 From 437610aae2a232b3f65869ac3bb79e9d67c3e7b7 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 3 Feb 2025 14:54:36 -0500 Subject: [PATCH 15/28] clean-ups and refactorings --- .../com/sun/tools/javac/comp/Flow.java | 30 +++++++++---------- .../com/sun/tools/javac/jvm/ClassWriter.java | 24 +++++++-------- .../classes/com/sun/tools/javac/jvm/Code.java | 14 ++++----- .../com/sun/tools/javac/tree/TreeInfo.java | 16 ---------- 4 files changed, 32 insertions(+), 52 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index d62236593e1..2526094c665 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -2309,14 +2309,7 @@ void letInit(JCTree tree, JCAssign assign) { /* we are initializing a strict field inside of a constructor, we now need to find which fields * haven't been initialized yet */ - ListBuffer unsetFields = new ListBuffer<>(); - for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) { - JCVariableDecl variableDecl = vardecls[i]; - if (variableDecl.sym.isStrict()) { - unsetFields.append(variableDecl.sym); - } - } - unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, assign != null ? assign : tree, unsetFields.toList()); + unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, assign != null ? assign : tree, findUninitStrictFields()); } } } @@ -2550,15 +2543,9 @@ public void visitMethodDef(JCMethodDecl tree) { initParam(def); } if (isConstructor) { - ListBuffer unsetFields = new ListBuffer<>(); - for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) { - JCVariableDecl variableDecl = vardecls[i]; - if (variableDecl.sym.isStrict()) { - unsetFields.append(variableDecl.sym); - } - } + List unsetFields = findUninitStrictFields(); if (unsetFields != null && !unsetFields.isEmpty()) { - unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree.body, unsetFields.toList()); + unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree.body, unsetFields); } } @@ -2617,6 +2604,17 @@ public void visitMethodDef(JCMethodDecl tree) { } } + List findUninitStrictFields() { + ListBuffer unsetFields = new ListBuffer<>(); + for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) { + JCVariableDecl variableDecl = vardecls[i]; + if (variableDecl.sym.isStrict()) { + unsetFields.append(variableDecl.sym); + } + } + return unsetFields.toList(); + } + private void clearPendingExits(boolean inMethod) { List exits = pendingExits.toList(); pendingExits = new ListBuffer<>(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 64e1744d232..204e87b2540 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1264,7 +1264,7 @@ void writeStackMap(Code code) { Assert.checkNull(code.stackMapBuffer); for (int i=0; i unsetFields; - AssertUnsetFields(java.util.List unsetFields) { + AssertUnsetFields(int pc, java.util.List unsetFields) { + super(pc); this.unsetFields = unsetFields; } @@ -1493,36 +1504,37 @@ void write(ClassWriter writer) { static StackMapTableEntry getInstance(Code.StackMapFrame this_frame, int prev_pc, Type[] prev_locals, - Types types) { + Types types, + int pc) { Type[] locals = this_frame.locals; Type[] stack = this_frame.stack; int offset_delta = this_frame.pc - prev_pc - 1; if (stack.length == 1) { if (locals.length == prev_locals.length && compare(prev_locals, locals, types) == 0) { - return new SameLocals1StackItemFrame(offset_delta, stack[0]); + return new SameLocals1StackItemFrame(pc, offset_delta, stack[0]); } } else if (stack.length == 0) { int diff_length = compare(prev_locals, locals, types); if (diff_length == 0) { - return new SameFrame(offset_delta); + return new SameFrame(pc, offset_delta); } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) { // APPEND Type[] local_diff = new Type[-diff_length]; for (int i=prev_locals.length, j=0; i Date: Tue, 4 Feb 2025 15:33:45 -0500 Subject: [PATCH 17/28] minor refactoring --- .../com/sun/tools/javac/jvm/ClassWriter.java | 13 ++++++------- .../share/classes/com/sun/tools/javac/jvm/Code.java | 6 +----- .../share/classes/com/sun/tools/javac/jvm/Gen.java | 1 - 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 262e47a9c7a..adcec2097ae 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1502,26 +1502,25 @@ void write(ClassWriter writer) { /** Compare this frame with the previous frame and produce * an entry of compressed stack map frame. */ static StackMapTableEntry getInstance(Code.StackMapFrame this_frame, - int prev_pc, - Type[] prev_locals, + Code.StackMapFrame prevFrame, Types types, int pc) { Type[] locals = this_frame.locals; Type[] stack = this_frame.stack; - int offset_delta = this_frame.pc - prev_pc - 1; + int offset_delta = this_frame.pc - prevFrame.pc - 1; if (stack.length == 1) { - if (locals.length == prev_locals.length - && compare(prev_locals, locals, types) == 0) { + if (locals.length == prevFrame.locals.length + && compare(prevFrame.locals, locals, types) == 0) { return new SameLocals1StackItemFrame(pc, offset_delta, stack[0]); } } else if (stack.length == 0) { - int diff_length = compare(prev_locals, locals, types); + int diff_length = compare(prevFrame.locals, locals, types); if (diff_length == 0) { return new SameFrame(pc, offset_delta); } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) { // APPEND Type[] local_diff = new Type[-diff_length]; - for (int i=prev_locals.length, j=0; i> cpToUnsetFieldsMap = new HashMap<>(); public List currentUnsetFields; @@ -211,7 +209,6 @@ public Code(MethodSymbol meth, Symtab syms, Types types, PoolWriter poolWriter, - UnsetFieldsInfo unsetFieldsInfo, boolean generateAssertUnsetFieldsFrame) { this.meth = meth; this.fatcode = fatcode; @@ -234,7 +231,6 @@ public Code(MethodSymbol meth, } state = new State(); lvar = new LocalVar[20]; - this.unsetFieldsInfo = unsetFieldsInfo; this.generateAssertUnsetFieldsFrame = generateAssertUnsetFieldsFrame; } @@ -1386,7 +1382,7 @@ void emitStackMapFrame(int pc, int localsSize) { } } stackMapTableBuffer[stackMapBufferSize++] = - StackMapTableEntry.getInstance(frame, lastFrame.pc, lastFrame.locals, types, pc); + StackMapTableEntry.getInstance(frame, lastFrame, types, pc); frameBeforeLast = lastFrame; lastFrame = frame; 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 36e4e889199..44da5ffbdf5 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 @@ -1080,7 +1080,6 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { syms, types, poolWriter, - unsetFieldsInfo, generateAssertUnsetFieldsFrame); items = new Items(poolWriter, code, syms, types); if (code.debugCode) { From 30f9df3a8e79ffa6dd900c4b7f47a2a4fdb323c0 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 4 Feb 2025 18:22:25 -0500 Subject: [PATCH 18/28] improve javap support --- .../jdk/internal/classfile/impl/CodeImpl.java | 2 +- .../internal/classfile/impl/StackMapDecoder.java | 5 ++++- .../com/sun/tools/javap/AttributeWriter.java | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index f2466b75249..a55f3acae03 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -293,7 +293,7 @@ else if (frameType < 128) { switch (frameType) { case 246 -> { int numberOfUnsetFields = classReader.readU2(p + 1); - offsetDelta = offsetDelta == -1 ? 0 : offsetDelta; + offsetDelta = 0; //offsetDelta == -1 ? 0 : offsetDelta; p += 3; p += 2 * numberOfUnsetFields; //adjustForObjectOrUninitialized(p + 3); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 62884fe6272..724b1ddfe0d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -199,10 +199,13 @@ List entries() { } else { if (frameType < ASSERT_UNSET_FIELDS) throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType); - bci += u2() + 1; + if (frameType != ASSERT_UNSET_FIELDS) { + bci += 1; + } if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { stack = List.of(readVerificationTypeInfo()); } else if (frameType == ASSERT_UNSET_FIELDS) { + bci = bci == -1 ? 0 : bci; stack = List.of(); locals = List.of(); var newUnsetFields = new Integer[u2()]; diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java index 9264ed0a5ec..ce33bd0d499 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java @@ -569,8 +569,21 @@ public void write(Attribute a, CodeAttribute lr) { printMap("stack", frame.stack(), lr); indent(-1); } else { - int offsetDelta = lr.labelToBci(frame.target()) - lastOffset - 1; + int offsetDelta = frameType != 246 ? lr.labelToBci(frame.target()) - lastOffset - 1 : 0; switch (frameType) { + case 246 -> { + printHeader(frameType, "/* assert_unset_fields */"); + indent(+1); + println("number of unset_fields = " + frame.unSetFields().size()); + indent(+1); + for (Integer fieldIndex : frame.unSetFields()) { + print("unset_field = #"); + constantWriter.write(fieldIndex); + println(); + } + indent(-1); + indent(-1); + } case 247 -> { printHeader(frameType, "/* same_locals_1_stack_item_entry_extended */"); indent(+1); From 26b1130263254f957df4f08219a988c576e40aa5 Mon Sep 17 00:00:00 2001 From: liach Date: Tue, 4 Feb 2025 18:21:34 -0600 Subject: [PATCH 19/28] Some stack maps classfile work, need testing --- .../attribute/StackMapFrameInfo.java | 11 +++-- .../jdk/internal/classfile/impl/CodeImpl.java | 2 +- .../classfile/impl/StackMapDecoder.java | 49 ++++++++++++------- .../com/sun/tools/javap/AttributeWriter.java | 8 +-- .../com/sun/tools/javap/StackMapWriter.java | 22 ++++++++- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java index 1f04ef47241..634a0d6ce6b 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java @@ -27,6 +27,7 @@ import java.lang.classfile.Label; import java.lang.classfile.constantpool.ClassEntry; +import java.lang.classfile.constantpool.NameAndTypeEntry; import java.lang.constant.ClassDesc; import java.util.List; @@ -63,22 +64,24 @@ public sealed interface StackMapFrameInfo /** * {@return the expanded unset fields} + * + * @see Specs */ - List unSetFields(); + List unsetFields(); /** * {@return a new stack map frame} * @param target the location of the frame * @param locals the complete list of frame locals * @param stack the complete frame stack - * @param unSetFields the complete list of unset fields + * @param unsetFields the complete list of unset fields */ public static StackMapFrameInfo of(Label target, List locals, List stack, - List unSetFields) { + List unsetFields) { - return new StackMapDecoder.StackMapFrameImpl(255, target, locals, stack, unSetFields); + return new StackMapDecoder.StackMapFrameImpl(255, target, locals, stack, unsetFields); } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index a55f3acae03..ce2fb270ce1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -293,9 +293,9 @@ else if (frameType < 128) { switch (frameType) { case 246 -> { int numberOfUnsetFields = classReader.readU2(p + 1); - offsetDelta = 0; //offsetDelta == -1 ? 0 : offsetDelta; p += 3; p += 2 * numberOfUnsetFields; //adjustForObjectOrUninitialized(p + 3); + continue; // do not move bci/create label } case 247 -> { offsetDelta = classReader.readU2(p + 1); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 724b1ddfe0d..5ff1d74817c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -35,14 +35,19 @@ import java.lang.classfile.attribute.StackMapFrameInfo.UninitializedVerificationTypeInfo; import java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo; import java.lang.classfile.constantpool.ClassEntry; +import java.lang.classfile.constantpool.NameAndTypeEntry; +import java.lang.classfile.constantpool.PoolEntry; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; import java.lang.reflect.AccessFlag; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Objects; +import jdk.internal.access.SharedSecrets; + import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*; import static java.util.Objects.requireNonNull; @@ -182,13 +187,26 @@ private static void writeTypeInfo(BufWriterImpl bw, VerificationTypeInfo vti) { } } + // Copied from BoundAttribute + List readEntryList(int p, Class type) { + int cnt = classReader.readU2(p); + p += 2; + var entries = new Object[cnt]; + int end = p + (cnt * 2); + for (int i = 0; p < end; i++, p += 2) { + entries[i] = classReader.readEntry(p, type); + } + return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries); + } + List entries() { p = pos; List locals = initFrameLocals, stack = List.of(); - List unSetFields = List.of(); + List unsetFields = List.of(); int bci = -1; - var entries = new StackMapFrameInfo[u2()]; - for (int ei = 0; ei < entries.length; ei++) { + int len = u2(); + var entries = new ArrayList(len); + for (int ei = 0; ei < len; ei++) { int frameType = classReader.readU1(p++); if (frameType < 64) { bci += frameType + 1; @@ -199,19 +217,14 @@ List entries() { } else { if (frameType < ASSERT_UNSET_FIELDS) throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType); - if (frameType != ASSERT_UNSET_FIELDS) { - bci += 1; + if (frameType == ASSERT_UNSET_FIELDS) { + unsetFields = readEntryList(p, NameAndTypeEntry.class); + p += 2 + unsetFields.size() * 2; + continue; // no new frame entry, no change to bci or other things } + bci += u2() + 1; if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { stack = List.of(readVerificationTypeInfo()); - } else if (frameType == ASSERT_UNSET_FIELDS) { - bci = bci == -1 ? 0 : bci; - stack = List.of(); - locals = List.of(); - var newUnsetFields = new Integer[u2()]; - for (int i=0; i entries() { stack = List.of(newStack); } } - entries[ei] = new StackMapFrameImpl(frameType, + entries.add(new StackMapFrameImpl(frameType, ctx.getLabel(bci), locals, stack, - unSetFields); + unsetFields)); } - return List.of(entries); + return List.copyOf(entries); } private VerificationTypeInfo readVerificationTypeInfo() { @@ -313,13 +326,13 @@ public static record StackMapFrameImpl(int frameType, Label target, List locals, List stack, - List unSetFields) + List unsetFields) implements StackMapFrameInfo { public StackMapFrameImpl { requireNonNull(target); locals = List.copyOf(locals); stack = List.copyOf(stack); - unSetFields = List.copyOf(unSetFields); + unsetFields = List.copyOf(unsetFields); } } } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java index ce33bd0d499..d69931d459e 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -574,11 +574,11 @@ public void write(Attribute a, CodeAttribute lr) { case 246 -> { printHeader(frameType, "/* assert_unset_fields */"); indent(+1); - println("number of unset_fields = " + frame.unSetFields().size()); + println("number of unset_fields = " + frame.unsetFields().size()); indent(+1); - for (Integer fieldIndex : frame.unSetFields()) { + for (NameAndTypeEntry field : frame.unsetFields()) { print("unset_field = #"); - constantWriter.write(fieldIndex); + constantWriter.write(field.index()); println(); } indent(-1); diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java index d899569724e..e1a361bd3c4 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -25,6 +25,7 @@ package com.sun.tools.javap; +import java.lang.classfile.constantpool.NameAndTypeEntry; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -98,10 +99,29 @@ private void writeDetails(int pc) { if (m != null) { print("StackMap locals: ", m.locals(), true); print("StackMap stack: ", m.stack(), false); + if (!m.unsetFields().isEmpty()) { + printFields("StackMap unset fields: ", m.unsetFields()); + } } } + void printFields(String label, List entries) { + print(label); + boolean first = true; + for (var e : entries) { + if (!first) { + print(", "); + } else { + first = false; + } + print(e::name); + print(":"); + print(e::type); + } + println(); + } + void print(String label, List entries, boolean firstThis) { print(label); From 546de3b475c0b9ebaaa21e0b615da7178a7f6db5 Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 5 Feb 2025 10:18:48 -0600 Subject: [PATCH 20/28] Restore stack map entries for asserts --- .../jdk/internal/classfile/impl/CodeImpl.java | 2 +- .../internal/classfile/impl/StackMapDecoder.java | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index ce2fb270ce1..7ccf75e70c6 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -294,7 +294,7 @@ else if (frameType < 128) { case 246 -> { int numberOfUnsetFields = classReader.readU2(p + 1); p += 3; - p += 2 * numberOfUnsetFields; //adjustForObjectOrUninitialized(p + 3); + p += 2 * numberOfUnsetFields; continue; // do not move bci/create label } case 247 -> { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 5ff1d74817c..bb9493d07ac 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -206,7 +206,10 @@ List entries() { int bci = -1; int len = u2(); var entries = new ArrayList(len); + List> deferredUnsetFields = new ArrayList<>(); for (int ei = 0; ei < len; ei++) { + var oldLocals = locals; + var oldStack = stack; int frameType = classReader.readU1(p++); if (frameType < 64) { bci += frameType + 1; @@ -220,7 +223,8 @@ List entries() { if (frameType == ASSERT_UNSET_FIELDS) { unsetFields = readEntryList(p, NameAndTypeEntry.class); p += 2 + unsetFields.size() * 2; - continue; // no new frame entry, no change to bci or other things + deferredUnsetFields.add(unsetFields); + continue; // defer entry until we can get the bci } bci += u2() + 1; if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { @@ -248,8 +252,15 @@ List entries() { stack = List.of(newStack); } } + Label label = ctx.getLabel(bci); + if (!deferredUnsetFields.isEmpty()) { + for (var deferredList : deferredUnsetFields) { + entries.add(new StackMapFrameImpl(ASSERT_UNSET_FIELDS, + label, oldLocals, oldStack, deferredList)); + } + } entries.add(new StackMapFrameImpl(frameType, - ctx.getLabel(bci), + label, locals, stack, unsetFields)); From 6b543761204ad74db8cbba3a1799efbf2034616b Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 5 Feb 2025 12:17:10 -0600 Subject: [PATCH 21/28] Missing clear --- .../classes/jdk/internal/classfile/impl/StackMapDecoder.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index bb9493d07ac..6f99b6ecaa0 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -254,10 +254,13 @@ List entries() { } Label label = ctx.getLabel(bci); if (!deferredUnsetFields.isEmpty()) { + // technically we only have one assert at once, just in case + // of duplicate asserts... for (var deferredList : deferredUnsetFields) { entries.add(new StackMapFrameImpl(ASSERT_UNSET_FIELDS, label, oldLocals, oldStack, deferredList)); } + deferredUnsetFields.clear(); } entries.add(new StackMapFrameImpl(frameType, label, From 630e2415a958effa8ec102160519e82402880c20 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 5 Feb 2025 16:19:39 -0500 Subject: [PATCH 22/28] adding some test cases --- .../classfile/StackMapTable_attribute.java | 2 + .../ValueObjectCompilationTests.java | 103 ++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java index 81f6bbf9117..152163b75f3 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java @@ -87,6 +87,8 @@ else if (entry_type <= 127) return new same_locals_1_stack_item_frame(entry_type, cr); else if (entry_type <= 245) throw new Error("unknown frame_type " + entry_type); + else if (entry_type == 246) + return new assert_unset_fields(entry_type, cr); else if (entry_type == 247) return new same_locals_1_stack_item_frame_extended(entry_type, cr); else if (entry_type <= 250) diff --git a/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java index c01d6a86e93..bca3c8528e4 100644 --- a/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java +++ b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java @@ -57,6 +57,7 @@ import com.sun.tools.classfile.Field; import com.sun.tools.classfile.Instruction; import com.sun.tools.classfile.Method; +import com.sun.tools.classfile.StackMapTable_attribute; import com.sun.tools.javac.code.Flags; @@ -1285,6 +1286,108 @@ class NumberSubClass extends Number { } } + @Test + void testAssertUnsetFieldsSMEntry() throws Exception { + String[] previousOptions = getCompileOptions(); + try { + String[] testOptions = { + "--enable-preview", + "-source", Integer.toString(Runtime.version().feature()), + "-XDgenerateAssertUnsetFieldsFrame", + "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED" + }; + setCompileOptions(testOptions); + + record Data(String src, int[] expectedFrameTypes, String[][] expectedUnsetFields) {} + for (Data data : List.of( + new Data( + """ + import jdk.internal.vm.annotation.Strict; + class Test { + @Strict + final int x; + @Strict + final int y; + Test(boolean a, boolean b) { + if (a) { // assert_unset_fields {x, y} + x = 1; + if (b) { // assert_unset_fields {y} + y = 1; + } else { // assert_unset_fields {y} + y = 2; + } + } else { // assert_unset_fields {x, y} + x = y = 3; + } + super(); + } + } + """, + // three unset_fields entries, entry type 246, are expected in the stackmap table + new int[] {246, 21, 246, 7, 246, 9}, + // expected fields for each of them: + new String[][] { new String[] { "y:I" }, new String[] { "x:I", "y:I" }, new String[] {} } + ), + new Data( + """ + import jdk.internal.vm.annotation.Strict; + class Test { + @Strict + final int x; + @Strict + final int y; + Test(int n) { + switch(n) { + case 2: + x = y = 2; + break; + default: + x = y = 100; + break; + } + super(); + } + } + """, + // here we expect only one + new int[] {20, 12, 246, 10}, + // stating that no field is unset + new String[][] { new String[] {} } + ) + )) { + File dir = assertOK(true, data.src()); + for (final File fileEntry : dir.listFiles()) { + ClassFile classFile = ClassFile.read(fileEntry); + for (Method method : classFile.methods) { + if (method.getName(classFile.constant_pool).equals("")) { + Code_attribute code = (Code_attribute)method.attributes.get("Code"); + StackMapTable_attribute stackMapTable = (StackMapTable_attribute)code.attributes.get("StackMapTable"); + int entryIndex = 0; + Assert.check(data.expectedFrameTypes().length == stackMapTable.entries.length, "unexpected stackmap length"); + int expectedUnsetFieldsIndex = 0; + for (StackMapTable_attribute.stack_map_entry entry : stackMapTable.entries) { + Assert.check(data.expectedFrameTypes()[entryIndex++] == entry.entry_type); + if (entry.entry_type == 246) { + StackMapTable_attribute.assert_unset_fields auf = (StackMapTable_attribute.assert_unset_fields)entry; + Assert.check(data.expectedUnsetFields()[expectedUnsetFieldsIndex].length == auf.number_of_unset_fields); + int index = 0; + for (int i : auf.unset_fields) { + String unsetStr = classFile.constant_pool.getNameAndTypeInfo(i).getName() + ":" + + classFile.constant_pool.getNameAndTypeInfo(i).getType(); + Assert.check(data.expectedUnsetFields()[expectedUnsetFieldsIndex][index++].equals(unsetStr)); + } + expectedUnsetFieldsIndex++; + } + } + } + } + } + } + } finally { + setCompileOptions(previousOptions); + } + } + private File findClassFileOrFail(File dir, String name) { for (final File fileEntry : dir.listFiles()) { if (fileEntry.getName().equals(name)) { From ada284bd1176264bf89a819c6f975c1f6554dbd9 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 6 Feb 2025 13:12:13 -0500 Subject: [PATCH 23/28] refactorings --- .../attribute/StackMapFrameInfo.java | 19 ++++++++++++++++--- .../java/lang/reflect/ProxyGenerator.java | 9 ++++----- .../classfile/impl/StackMapDecoder.java | 13 ++++++++++--- .../impl/verifier/VerificationTable.java | 3 ++- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java index 634a0d6ce6b..e1d65e8f675 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java @@ -74,12 +74,25 @@ public sealed interface StackMapFrameInfo * @param target the location of the frame * @param locals the complete list of frame locals * @param stack the complete frame stack - * @param unsetFields the complete list of unset fields */ public static StackMapFrameInfo of(Label target, List locals, - List stack, - List unsetFields) { + List stack) { + + return of(target, locals, stack, List.of()); + } + + /** + * {@return a new stack map frame} + * @param target the location of the frame + * @param locals the complete list of frame locals + * @param stack the complete frame stack + * @param unsetFields the complete list of unset fields + */ + public static StackMapFrameInfo of(Label target, + List locals, + List stack, + List unsetFields) { return new StackMapDecoder.StackMapFrameImpl(255, target, locals, stack, unsetFields); } diff --git a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java index fd878f77bc3..d7d4258cc82 100644 --- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java +++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java @@ -597,8 +597,8 @@ private void generateStaticInitializer(ClassBuilder clb) { .invokespecial(cp.methodRefEntry(ncdfError, exInit)) .athrow(); cob.with(StackMapTableAttribute.of(List.of( - StackMapFrameInfo.of(c1, classLoaderLocal, throwableStack, List.of()), - StackMapFrameInfo.of(c2, classLoaderLocal, throwableStack, List.of())))); + StackMapFrameInfo.of(c1, classLoaderLocal, throwableStack), + StackMapFrameInfo.of(c2, classLoaderLocal, throwableStack)))); }); } @@ -636,7 +636,6 @@ private void generateLookupAccessor(ClassBuilder clb) { .with(StackMapTableAttribute.of(List.of( StackMapFrameInfo.of(failLabel, List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(mhl)), - List.of(), List.of())))); })); } @@ -735,8 +734,8 @@ private void generateMethod(ClassBuilder clb) { .invokespecial(uteInit) .athrow() .with(StackMapTableAttribute.of(List.of( - StackMapFrameInfo.of(c1, List.of(), throwableStack, List.of()), - StackMapFrameInfo.of(c2, List.of(), throwableStack, List.of())))); + StackMapFrameInfo.of(c1, List.of(), throwableStack), + StackMapFrameInfo.of(c2, List.of(), throwableStack)))); } })); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 6f99b6ecaa0..1eb11a5148b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -58,6 +58,7 @@ public class StackMapDecoder { ASSERT_UNSET_FIELDS = 246, SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251; + private static final int RESERVED_TAGS_UPPER_LIMIT = ASSERT_UNSET_FIELDS; // not inclusive private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {}; private final ClassReader classReader; @@ -218,7 +219,7 @@ List entries() { bci += frameType - 63; stack = List.of(readVerificationTypeInfo()); } else { - if (frameType < ASSERT_UNSET_FIELDS) + if (frameType < RESERVED_TAGS_UPPER_LIMIT) throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType); if (frameType == ASSERT_UNSET_FIELDS) { unsetFields = readEntryList(p, NameAndTypeEntry.class); @@ -265,8 +266,7 @@ List entries() { entries.add(new StackMapFrameImpl(frameType, label, locals, - stack, - unsetFields)); + stack)); } return List.copyOf(entries); } @@ -348,5 +348,12 @@ public static record StackMapFrameImpl(int frameType, stack = List.copyOf(stack); unsetFields = List.copyOf(unsetFields); } + + public StackMapFrameImpl(int frameType, + Label target, + List locals, + List stack) { + this(frameType, target, locals, stack, List.of()); + } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java index fe88913c96b..94ea65cec71 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java @@ -163,6 +163,7 @@ void check_verification_type_array_size(int size, int max_size) { SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251, FULL = 255; + private static final int RESERVED_TAGS_UPPER_LIMIT = ASSERT_UNSET_FIELDS; // not inclusive public int get_frame_count() { return _frame_count; @@ -279,7 +280,7 @@ public VerificationFrame next(VerificationFrame pre_frame, boolean first, int ma return frame; } int offset_delta = _stream.get_u2(); - if (frame_type < ASSERT_UNSET_FIELDS) { + if (frame_type < RESERVED_TAGS_UPPER_LIMIT) { _verifier.classError("reserved frame type"); } if (frame_type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { From 352b1de11e2b00f6481b7372f9ae28421c86b678 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 6 Feb 2025 15:52:23 -0500 Subject: [PATCH 24/28] additional refactoring --- .../com/sun/tools/javac/comp/Flow.java | 11 ++++---- .../sun/tools/javac/comp/UnsetFieldsInfo.java | 14 +++++----- .../com/sun/tools/javac/jvm/ClassWriter.java | 4 +-- .../classes/com/sun/tools/javac/jvm/Code.java | 15 +++++------ .../classes/com/sun/tools/javac/jvm/Gen.java | 26 +++++++++---------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index a5fa4fca339..abb3a21ef17 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -27,6 +27,7 @@ package com.sun.tools.javac.comp; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; import java.util.HashMap; @@ -2543,7 +2544,7 @@ public void visitMethodDef(JCMethodDecl tree) { initParam(def); } if (isConstructor) { - List unsetFields = findUninitStrictFields(); + Set unsetFields = findUninitStrictFields(); if (unsetFields != null && !unsetFields.isEmpty()) { unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree.body, unsetFields); } @@ -2604,15 +2605,15 @@ public void visitMethodDef(JCMethodDecl tree) { } } - List findUninitStrictFields() { - ListBuffer unsetFields = new ListBuffer<>(); + Set findUninitStrictFields() { + Set unsetFields = new LinkedHashSet<>(); for (int i = uninits.nextBit(0); i >= 0; i = uninits.nextBit(i + 1)) { JCVariableDecl variableDecl = vardecls[i]; if (variableDecl.sym.isStrict()) { - unsetFields.append(variableDecl.sym); + unsetFields.add(variableDecl.sym); } } - return unsetFields.toList(); + return unsetFields; } private void clearPendingExits(boolean inMethod) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java index 43f180781ad..a75869e0aa3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; import com.sun.tools.javac.util.List; @@ -34,7 +35,6 @@ import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Context; /** @@ -62,10 +62,10 @@ protected UnsetFieldsInfo(Context context) { context.put(unsetFieldsInfoKey, this); } - private WeakHashMap>> unsetFieldsMap = new WeakHashMap<>(); + private WeakHashMap>> unsetFieldsMap = new WeakHashMap<>(); - public void addUnsetFieldsInfo(ClassSymbol csym, JCTree tree, List unsetFields) { - Map> treeToFieldsMap = unsetFieldsMap.get(csym); + public void addUnsetFieldsInfo(ClassSymbol csym, JCTree tree, Set unsetFields) { + Map> treeToFieldsMap = unsetFieldsMap.get(csym); if (treeToFieldsMap == null) { treeToFieldsMap = new HashMap<>(); treeToFieldsMap.put(tree, unsetFields); @@ -78,10 +78,10 @@ public void addUnsetFieldsInfo(ClassSymbol csym, JCTree tree, List un } } - public List getUnsetFields(ClassSymbol csym, JCTree tree) { - Map> treeToFieldsMap = unsetFieldsMap.get(csym); + public Set getUnsetFields(ClassSymbol csym, JCTree tree) { + Map> treeToFieldsMap = unsetFieldsMap.get(csym); if (treeToFieldsMap != null) { - List result = treeToFieldsMap.get(tree); + Set result = treeToFieldsMap.get(tree); if (result != null) { return result; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 17800898371..8df6791875a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1473,9 +1473,9 @@ void write(ClassWriter writer) { } static class AssertUnsetFields extends StackMapTableEntry { - java.util.List unsetFields; + Set unsetFields; - AssertUnsetFields(int pc, java.util.List unsetFields) { + AssertUnsetFields(int pc, Set unsetFields) { super(pc); this.unsetFields = unsetFields; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index 1208d3f3806..1ca52fc7ff2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -44,6 +44,7 @@ import java.util.Arrays; import java.util.Map; import java.util.HashMap; +import java.util.Set; /** An internal structure that corresponds to the code attribute of * methods in a classfile. The class also provides some utility operations to @@ -166,8 +167,6 @@ public boolean checkLimits(DiagnosticPosition pos, Log log) { */ int pendingStatPos = Position.NOPOS; - java.util.List unsetFields = null; - /** Set true when a stackMap is needed at the current PC. */ boolean pendingStackMap = false; @@ -190,9 +189,9 @@ public boolean checkLimits(DiagnosticPosition pos, Log log) { private int letExprStackPos = 0; - private Map> cpToUnsetFieldsMap = new HashMap<>(); + private Map> cpToUnsetFieldsMap = new HashMap<>(); - public List currentUnsetFields; + public Set currentUnsetFields; boolean generateAssertUnsetFieldsFrame; @@ -1228,7 +1227,7 @@ static class StackMapFrame { int pc; Type[] locals; Type[] stack; - List unsetFields; + Set unsetFields; } /** A buffer of cldc stack map entries. */ @@ -1363,10 +1362,10 @@ void emitStackMapFrame(int pc, int localsSize) { stackMapTableBuffer, stackMapBufferSize); } - List unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc); + Set unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc); if (unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame) { if (lastFrame.unsetFields != null) { - if (!lastFrame.unsetFields.diff(unsetFieldsAtPC).isEmpty() || !unsetFieldsAtPC.diff(lastFrame.unsetFields).isEmpty()) { + if (!lastFrame.unsetFields.equals(unsetFieldsAtPC)) { stackMapTableBuffer[stackMapBufferSize++] = new StackMapTableEntry.AssertUnsetFields(pc, unsetFieldsAtPC); frame.unsetFields = unsetFieldsAtPC; stackMapTableBuffer = ArrayUtils.ensureCapacity( @@ -1388,7 +1387,7 @@ void emitStackMapFrame(int pc, int localsSize) { lastFrame = frame; } - public void addUnsetFieldsAtCP(int cp, List unsetFields) { + public void addUnsetFieldsAtCP(int cp, Set unsetFields) { cpToUnsetFieldsMap.put(cp, unsetFields); } 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 c07b66b8505..a30be2e1990 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 @@ -99,7 +99,7 @@ public static Gen instance(Context context) { private final UnsetFieldsInfo unsetFieldsInfo; - private List currentUnsetFields = List.nil(); + private Set currentUnsetFields = Set.of(); @SuppressWarnings("this-escape") protected Gen(Context context) { @@ -996,7 +996,7 @@ void genMethod(JCMethodDecl tree, Env env, boolean fatcode) { else if (tree.body != null) { // Create a new code structure and initialize it. int startpcCrt = initCode(tree, env, fatcode); - List prevUnsetFields = currentUnsetFields; + Set prevUnsetFields = currentUnsetFields; if (meth.isConstructor()) { currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body); code.currentUnsetFields = currentUnsetFields; @@ -1218,8 +1218,8 @@ private void genLoop(JCStatement loop, boolean testFirst) { Env loopEnv = env.dup(loop, new GenContext()); int startpc = code.entryPoint(); - List prevUnsetFields = currentUnsetFields; - List prevCodeUnsetFields = code.currentUnsetFields; + Set prevUnsetFields = currentUnsetFields; + Set prevCodeUnsetFields = code.currentUnsetFields; try { code.currentUnsetFields = currentUnsetFields; if (testFirst) { //while or for loop @@ -1290,8 +1290,8 @@ public void visitSwitch(JCSwitch tree) { public void visitSwitchExpression(JCSwitchExpression tree) { code.resolvePending(); boolean prevInCondSwitchExpression = inCondSwitchExpression; - List prevUnsetFields = currentUnsetFields; - List prevCodeUnsetFields = code.currentUnsetFields; + Set prevUnsetFields = currentUnsetFields; + Set prevCodeUnsetFields = code.currentUnsetFields; try { code.currentUnsetFields = currentUnsetFields; inCondSwitchExpression = false; @@ -1375,8 +1375,8 @@ public void visitLambda(JCLambda tree) { private void handleSwitch(JCTree swtch, JCExpression selector, List cases, boolean patternSwitch) { - List prevUnsetFields = currentUnsetFields; - List prevCodeUnsetFields = code.currentUnsetFields; + Set prevUnsetFields = currentUnsetFields; + Set prevCodeUnsetFields = code.currentUnsetFields; try { code.currentUnsetFields = currentUnsetFields; int limit = code.nextreg; @@ -1635,8 +1635,8 @@ void afterBody() { * @param env The current environment of the body. */ void genTry(JCTree body, List catchers, Env env) { - List prevUnsetFields = currentUnsetFields; - List prevCodeUnsetFields = code.currentUnsetFields; + Set prevUnsetFields = currentUnsetFields; + Set prevCodeUnsetFields = code.currentUnsetFields; try { code.currentUnsetFields = currentUnsetFields; int limit = code.nextreg; @@ -1854,8 +1854,8 @@ void registerCatch(DiagnosticPosition pos, } public void visitIf(JCIf tree) { - List prevUnsetFields = currentUnsetFields; - List prevCodeUnsetFields = code.currentUnsetFields; + Set prevUnsetFields = currentUnsetFields; + Set prevCodeUnsetFields = code.currentUnsetFields; try { code.currentUnsetFields = currentUnsetFields; int limit = code.nextreg; @@ -2193,7 +2193,7 @@ public void visitParens(JCParens tree) { public void visitAssign(JCAssign tree) { Item l = genExpr(tree.lhs, tree.lhs.type); genExpr(tree.rhs, tree.lhs.type).load(); - List tmpUnsetSymbols = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree); + Set tmpUnsetSymbols = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree); currentUnsetFields = tmpUnsetSymbols != null ? tmpUnsetSymbols : currentUnsetFields; code.currentUnsetFields = currentUnsetFields; if (tree.rhs.type.hasTag(BOT)) { From bf82dfd6debeb046a8b9a955e3c14c7f76328215 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 7 Feb 2025 15:04:55 -0500 Subject: [PATCH 25/28] another refactoring --- .../classes/com/sun/tools/javac/jvm/Code.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index 1ca52fc7ff2..28e08b8832c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -27,7 +27,6 @@ import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.*; -import com.sun.tools.javac.comp.UnsetFieldsInfo; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -1355,29 +1354,21 @@ void emitStackMapFrame(int pc, int localsSize) { } } + Set unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc); + boolean generateAssertUnsetFieldsEntry = unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame; + if (stackMapTableBuffer == null) { stackMapTableBuffer = new StackMapTableEntry[20]; } else { stackMapTableBuffer = ArrayUtils.ensureCapacity( stackMapTableBuffer, - stackMapBufferSize); + stackMapBufferSize + (generateAssertUnsetFieldsEntry ? 1 : 0)); } - Set unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc); - if (unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame) { - if (lastFrame.unsetFields != null) { - if (!lastFrame.unsetFields.equals(unsetFieldsAtPC)) { - stackMapTableBuffer[stackMapBufferSize++] = new StackMapTableEntry.AssertUnsetFields(pc, unsetFieldsAtPC); - frame.unsetFields = unsetFieldsAtPC; - stackMapTableBuffer = ArrayUtils.ensureCapacity( - stackMapTableBuffer, - stackMapBufferSize); - } - } else { + + if (generateAssertUnsetFieldsEntry) { + if (lastFrame.unsetFields == null || !lastFrame.unsetFields.equals(unsetFieldsAtPC)) { stackMapTableBuffer[stackMapBufferSize++] = new StackMapTableEntry.AssertUnsetFields(pc, unsetFieldsAtPC); frame.unsetFields = unsetFieldsAtPC; - stackMapTableBuffer = ArrayUtils.ensureCapacity( - stackMapTableBuffer, - stackMapBufferSize); } } stackMapTableBuffer[stackMapBufferSize++] = @@ -1387,8 +1378,8 @@ void emitStackMapFrame(int pc, int localsSize) { lastFrame = frame; } - public void addUnsetFieldsAtCP(int cp, Set unsetFields) { - cpToUnsetFieldsMap.put(cp, unsetFields); + public void addUnsetFieldsAtPC(int pc, Set unsetFields) { + cpToUnsetFieldsMap.put(pc, unsetFields); } StackMapFrame getInitialFrame() { @@ -1491,7 +1482,7 @@ public Chain branch(int opcode) { result, state.dup()); if (currentUnsetFields != null) { - addUnsetFieldsAtCP(result.pc, currentUnsetFields); + addUnsetFieldsAtPC(result.pc, currentUnsetFields); } fixedPc = fatcode; if (opcode == goto_) alive = false; @@ -1534,7 +1525,7 @@ public void resolve(Chain chain, int target) { if (fatcode) { put4(chain.pc + 1, target - chain.pc); if (cpToUnsetFieldsMap.get(chain.pc) != null) { - addUnsetFieldsAtCP(originalTarget, cpToUnsetFieldsMap.get(chain.pc)); + addUnsetFieldsAtPC(originalTarget, cpToUnsetFieldsMap.get(chain.pc)); } } else if (target - chain.pc < Short.MIN_VALUE || @@ -1543,7 +1534,7 @@ else if (target - chain.pc < Short.MIN_VALUE || else { put2(chain.pc + 1, target - chain.pc); if (cpToUnsetFieldsMap.get(chain.pc) != null) { - addUnsetFieldsAtCP(originalTarget, cpToUnsetFieldsMap.get(chain.pc)); + addUnsetFieldsAtPC(originalTarget, cpToUnsetFieldsMap.get(chain.pc)); } } Assert.check(!alive || From 55d4ec94697ac6175a20718212dd7be6d53bd15e Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 7 Feb 2025 15:35:55 -0500 Subject: [PATCH 26/28] another simplification --- .../classes/com/sun/tools/javac/jvm/Gen.java | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) 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 a30be2e1990..9946f5db3d4 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 @@ -99,8 +99,6 @@ public static Gen instance(Context context) { private final UnsetFieldsInfo unsetFieldsInfo; - private Set currentUnsetFields = Set.of(); - @SuppressWarnings("this-escape") protected Gen(Context context) { context.put(genKey, this); @@ -996,10 +994,9 @@ void genMethod(JCMethodDecl tree, Env env, boolean fatcode) { else if (tree.body != null) { // Create a new code structure and initialize it. int startpcCrt = initCode(tree, env, fatcode); - Set prevUnsetFields = currentUnsetFields; + Set prevUnsetFields = code.currentUnsetFields; if (meth.isConstructor()) { - currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body); - code.currentUnsetFields = currentUnsetFields; + code.currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body);; } try { @@ -1009,7 +1006,6 @@ else if (tree.body != null) { startpcCrt = initCode(tree, env, fatcode); genStat(tree.body, env); } finally { - currentUnsetFields = prevUnsetFields; code.currentUnsetFields = prevUnsetFields; } @@ -1218,10 +1214,8 @@ private void genLoop(JCStatement loop, boolean testFirst) { Env loopEnv = env.dup(loop, new GenContext()); int startpc = code.entryPoint(); - Set prevUnsetFields = currentUnsetFields; Set prevCodeUnsetFields = code.currentUnsetFields; try { - code.currentUnsetFields = currentUnsetFields; if (testFirst) { //while or for loop CondItem c; if (cond != null) { @@ -1263,7 +1257,6 @@ private void genLoop(JCStatement loop, exit.state.defined.excludeFrom(code.nextreg); } } finally { - currentUnsetFields = prevUnsetFields; code.currentUnsetFields = prevCodeUnsetFields; } } @@ -1290,15 +1283,12 @@ public void visitSwitch(JCSwitch tree) { public void visitSwitchExpression(JCSwitchExpression tree) { code.resolvePending(); boolean prevInCondSwitchExpression = inCondSwitchExpression; - Set prevUnsetFields = currentUnsetFields; Set prevCodeUnsetFields = code.currentUnsetFields; try { - code.currentUnsetFields = currentUnsetFields; inCondSwitchExpression = false; doHandleSwitchExpression(tree); } finally { inCondSwitchExpression = prevInCondSwitchExpression; - currentUnsetFields = prevUnsetFields; code.currentUnsetFields = prevCodeUnsetFields; } result = items.makeStackItem(pt); @@ -1375,10 +1365,8 @@ public void visitLambda(JCLambda tree) { private void handleSwitch(JCTree swtch, JCExpression selector, List cases, boolean patternSwitch) { - Set prevUnsetFields = currentUnsetFields; Set prevCodeUnsetFields = code.currentUnsetFields; try { - code.currentUnsetFields = currentUnsetFields; int limit = code.nextreg; Assert.check(!selector.type.hasTag(CLASS)); int switchStart = patternSwitch ? code.entryPoint() : -1; @@ -1533,7 +1521,6 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case } code.endScopes(limit); } finally { - currentUnsetFields = prevUnsetFields; code.currentUnsetFields = prevCodeUnsetFields; } } @@ -1635,10 +1622,8 @@ void afterBody() { * @param env The current environment of the body. */ void genTry(JCTree body, List catchers, Env env) { - Set prevUnsetFields = currentUnsetFields; Set prevCodeUnsetFields = code.currentUnsetFields; try { - code.currentUnsetFields = currentUnsetFields; int limit = code.nextreg; int startpc = code.curCP(); Code.State stateTry = code.state.dup(); @@ -1737,7 +1722,6 @@ void genTry(JCTree body, List catchers, Env env) { code.endScopes(limit); } finally { - currentUnsetFields = prevUnsetFields; code.currentUnsetFields = prevCodeUnsetFields; } } @@ -1854,10 +1838,8 @@ void registerCatch(DiagnosticPosition pos, } public void visitIf(JCIf tree) { - Set prevUnsetFields = currentUnsetFields; Set prevCodeUnsetFields = code.currentUnsetFields; try { - code.currentUnsetFields = currentUnsetFields; int limit = code.nextreg; Chain thenExit = null; Assert.check(code.isStatementStart()); @@ -1880,7 +1862,6 @@ public void visitIf(JCIf tree) { code.endScopes(limit); Assert.check(code.isStatementStart()); } finally { - currentUnsetFields = prevUnsetFields; code.currentUnsetFields = prevCodeUnsetFields; } } @@ -2194,8 +2175,7 @@ public void visitAssign(JCAssign tree) { Item l = genExpr(tree.lhs, tree.lhs.type); genExpr(tree.rhs, tree.lhs.type).load(); Set tmpUnsetSymbols = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree); - currentUnsetFields = tmpUnsetSymbols != null ? tmpUnsetSymbols : currentUnsetFields; - code.currentUnsetFields = currentUnsetFields; + 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 From b258586bc2c3e6acc2bcb95abdc37b5874979fcf Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 7 Feb 2025 15:48:20 -0500 Subject: [PATCH 27/28] refactoring n --- .../classes/com/sun/tools/javac/jvm/Gen.java | 182 +++++++++--------- 1 file changed, 93 insertions(+), 89 deletions(-) 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 9946f5db3d4..adc0a0b4d66 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 @@ -996,7 +996,7 @@ else if (tree.body != null) { int startpcCrt = initCode(tree, env, fatcode); Set prevUnsetFields = code.currentUnsetFields; if (meth.isConstructor()) { - code.currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body);; + code.currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body); } try { @@ -1624,106 +1624,110 @@ void afterBody() { void genTry(JCTree body, List catchers, Env env) { Set prevCodeUnsetFields = code.currentUnsetFields; try { - int limit = code.nextreg; - int startpc = code.curCP(); - Code.State stateTry = code.state.dup(); - genStat(body, env, CRT_BLOCK); - int endpc = code.curCP(); - List gaps = env.info.gaps.toList(); - code.statBegin(TreeInfo.endPos(body)); + genTryHelper(body, catchers, env); + } finally { + code.currentUnsetFields = prevCodeUnsetFields; + } + } + + void genTryHelper(JCTree body, List catchers, Env env) { + int limit = code.nextreg; + int startpc = code.curCP(); + Code.State stateTry = code.state.dup(); + genStat(body, env, CRT_BLOCK); + int endpc = code.curCP(); + List gaps = env.info.gaps.toList(); + code.statBegin(TreeInfo.endPos(body)); + genFinalizer(env); + code.statBegin(TreeInfo.endPos(env.tree)); + Chain exitChain; + boolean actualTry = env.tree.hasTag(TRY); + if (startpc == endpc && actualTry) { + exitChain = code.branch(dontgoto); + } else { + 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 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); - code.statBegin(TreeInfo.endPos(env.tree)); - Chain exitChain; - boolean actualTry = env.tree.hasTag(TRY); - if (startpc == endpc && actualTry) { - exitChain = code.branch(dontgoto); - } else { - exitChain = code.branch(goto_); + if (hasFinalizer || l.tail.nonEmpty()) { + code.statBegin(TreeInfo.endPos(env.tree)); + exitChain = Code.mergeChains(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 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 + // the same variables in finalizers and other statements. + code.newRegSegment(); + + // Add a catch-all clause. + + // start off with exception on stack + int catchallpc = code.entryPoint(stateTry, syms.throwableType); + + // Register all exception ranges for catch all clause. + // The range of the catch all clause is from the beginning + // of the try or synchronized block until the present + // code pointer excluding all gaps in the current + // 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(); } - if (hasFinalizer && (startpc != endpc || !actualTry)) { - // Create a new register segment to avoid allocating - // the same variables in finalizers and other statements. - code.newRegSegment(); - - // Add a catch-all clause. - - // start off with exception on stack - int catchallpc = code.entryPoint(stateTry, syms.throwableType); - - // Register all exception ranges for catch all clause. - // The range of the catch all clause is from the beginning - // of the try or synchronized block until the present - // code pointer excluding all gaps in the current - // 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.FIRST_STAT_POS)); + code.markStatBegin(); - Item excVar = makeTemp(syms.throwableType); - excVar.store(); - genFinalizer(env); - code.resolvePending(); - 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(); + Item excVar = makeTemp(syms.throwableType); + excVar.store(); + genFinalizer(env); + code.resolvePending(); + 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) { - // Resolve all jsr's. - code.resolve(env.info.cont); + // If there are jsr's to this finalizer, ... + if (env.info.cont != null) { + // Resolve all jsr's. + code.resolve(env.info.cont); - // Mark statement line number - code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); - code.markStatBegin(); + // Mark statement line number + code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); + code.markStatBegin(); - // Save return address. - LocalItem retVar = makeTemp(syms.throwableType); - retVar.store(); + // Save return address. + LocalItem retVar = makeTemp(syms.throwableType); + retVar.store(); - // Generate finalizer code. - env.info.finalize.genLast(); + // Generate finalizer code. + env.info.finalize.genLast(); - // Return. - code.emitop1w(ret, retVar.reg); - code.markDead(); - } + // Return. + code.emitop1w(ret, retVar.reg); + code.markDead(); } - // Resolve all breaks. - code.resolve(exitChain); - - code.endScopes(limit); - } finally { - code.currentUnsetFields = prevCodeUnsetFields; } + // Resolve all breaks. + code.resolve(exitChain); + + code.endScopes(limit); } /** Generate code for a catch clause. From f98e216e9853e6e74dc3506a37ec6d9a6f3d52e6 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 7 Feb 2025 16:29:18 -0500 Subject: [PATCH 28/28] another refactoring --- .../classes/com/sun/tools/javac/jvm/Gen.java | 407 +++++++++--------- 1 file changed, 212 insertions(+), 195 deletions(-) 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 adc0a0b4d66..4f9e9b1ec25 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 @@ -1212,11 +1212,43 @@ private void genLoop(JCStatement loop, JCExpression cond, List step, boolean testFirst) { - Env loopEnv = env.dup(loop, new GenContext()); - int startpc = code.entryPoint(); Set prevCodeUnsetFields = code.currentUnsetFields; try { - if (testFirst) { //while or for loop + genLoopHelper(loop, body, cond, step, testFirst); + } finally { + code.currentUnsetFields = prevCodeUnsetFields; + } + } + + private void genLoopHelper(JCStatement loop, + JCStatement body, + JCExpression cond, + List step, + boolean testFirst) { + Env loopEnv = env.dup(loop, new GenContext()); + int startpc = code.entryPoint(); + if (testFirst) { //while or for loop + CondItem c; + if (cond != null) { + code.statBegin(cond.pos); + Assert.check(code.isStatementStart()); + c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); + } else { + c = items.makeCondItem(goto_); + } + Chain loopDone = c.jumpFalse(); + code.resolve(c.trueJumps); + Assert.check(code.isStatementStart()); + genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); + code.resolve(loopEnv.info.cont); + genStats(step, loopEnv); + code.resolve(code.branch(goto_), startpc); + code.resolve(loopDone); + } else { + genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); + code.resolve(loopEnv.info.cont); + genStats(step, loopEnv); + if (code.isAlive()) { CondItem c; if (cond != null) { code.statBegin(cond.pos); @@ -1225,39 +1257,15 @@ private void genLoop(JCStatement loop, } else { c = items.makeCondItem(goto_); } - Chain loopDone = c.jumpFalse(); - code.resolve(c.trueJumps); + code.resolve(c.jumpTrue(), startpc); Assert.check(code.isStatementStart()); - genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); - code.resolve(loopEnv.info.cont); - genStats(step, loopEnv); - code.resolve(code.branch(goto_), startpc); - code.resolve(loopDone); - } else { - genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); - code.resolve(loopEnv.info.cont); - genStats(step, loopEnv); - if (code.isAlive()) { - CondItem c; - if (cond != null) { - code.statBegin(cond.pos); - Assert.check(code.isStatementStart()); - c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); - } else { - c = items.makeCondItem(goto_); - } - code.resolve(c.jumpTrue(), startpc); - Assert.check(code.isStatementStart()); - code.resolve(c.falseJumps); - } - } - Chain exit = loopEnv.info.exit; - if (exit != null) { - code.resolve(exit); - exit.state.defined.excludeFrom(code.nextreg); + code.resolve(c.falseJumps); } - } finally { - code.currentUnsetFields = prevCodeUnsetFields; + } + Chain exit = loopEnv.info.exit; + if (exit != null) { + code.resolve(exit); + exit.state.defined.excludeFrom(code.nextreg); } } @@ -1367,162 +1375,167 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case boolean patternSwitch) { Set prevCodeUnsetFields = code.currentUnsetFields; try { - 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()); - Item sel = genExpr(selector, syms.intType); - if (cases.isEmpty()) { - // We are seeing: switch {} - sel.load().drop(); - if (genCrt) - code.crt.put(TreeInfo.skipParens(selector), - CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); + handleSwitchHelper(swtch, selector, cases, patternSwitch); + } finally { + code.currentUnsetFields = prevCodeUnsetFields; + } + } + + void handleSwitchHelper(JCTree swtch, JCExpression selector, List 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()); + Item sel = genExpr(selector, syms.intType); + if (cases.isEmpty()) { + // We are seeing: switch {} + 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 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. + int lo = Integer.MAX_VALUE; // minimum label. + int hi = Integer.MIN_VALUE; // maximum label. + int nlabels = 0; // number of labels. + + int[] labels = new int[cases.length()]; // the label array. + int defaultIndex = -1; // the index of the default clause. + + List l = cases; + for (int i = 0; i < labels.length; i++) { + if (l.head.labels.head instanceof JCConstantCaseLabel constLabel) { + Assert.check(l.head.labels.size() == 1); + int val = ((Number) constLabel.expr.type.constValue()).intValue(); + labels[i] = val; + if (val < lo) lo = val; + if (hi < val) hi = val; + nlabels++; + } else { + Assert.check(defaultIndex == -1); + defaultIndex = i; + } + l = l.tail; + } + + // Determine whether to issue a tableswitch or a lookupswitch + // instruction. + 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 + int[] offsets = null; // a table of offsets for a lookupswitch + code.emit4(-1); // leave space for default offset + if (opcode == tableswitch) { + code.emit4(lo); // minimum label + code.emit4(hi); // maximum label + for (long i = lo; i <= hi; i++) { // leave space for jump table + code.emit4(-1); + } } else { - // We are seeing a nonempty switch. - sel.load(); - if (genCrt) - code.crt.put(TreeInfo.skipParens(selector), - CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); - Env 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. - int lo = Integer.MAX_VALUE; // minimum label. - int hi = Integer.MIN_VALUE; // maximum label. - int nlabels = 0; // number of labels. - - int[] labels = new int[cases.length()]; // the label array. - int defaultIndex = -1; // the index of the default clause. - - List l = cases; - for (int i = 0; i < labels.length; i++) { - if (l.head.labels.head instanceof JCConstantCaseLabel constLabel) { - Assert.check(l.head.labels.size() == 1); - int val = ((Number) constLabel.expr.type.constValue()).intValue(); - labels[i] = val; - if (val < lo) lo = val; - if (hi < val) hi = val; - nlabels++; - } else { - Assert.check(defaultIndex == -1); - defaultIndex = i; - } - l = l.tail; + code.emit4(nlabels); // number of labels + for (int i = 0; i < nlabels; i++) { + code.emit4(-1); code.emit4(-1); // leave space for lookup table } - - // Determine whether to issue a tableswitch or a lookupswitch - // instruction. - 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 - int[] offsets = null; // a table of offsets for a lookupswitch - code.emit4(-1); // leave space for default offset - if (opcode == tableswitch) { - code.emit4(lo); // minimum label - code.emit4(hi); // maximum label - for (long i = lo; i <= hi; i++) { // leave space for jump table - code.emit4(-1); + offsets = new int[labels.length]; + } + Code.State stateSwitch = code.state.dup(); + code.markDead(); + + // For each case do: + l = cases; + for (int i = 0; i < labels.length; i++) { + JCCase c = l.head; + l = l.tail; + + int pc = code.entryPoint(stateSwitch); + // 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.emit4(nlabels); // number of labels - for (int i = 0; i < nlabels; i++) { - code.emit4(-1); code.emit4(-1); // leave space for lookup table - } - offsets = new int[labels.length]; + code.put4(tableBase, pc - startpc); } - Code.State stateSwitch = code.state.dup(); - code.markDead(); - // For each case do: - l = cases; - for (int i = 0; i < labels.length; i++) { - JCCase c = l.head; - l = l.tail; - - int pc = code.entryPoint(stateSwitch); - // 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); - } + // Generate code for the statements in this case. + genStats(c.stats, switchEnv, CRT_FLOW_TARGET); + } - // Generate code for the statements in this case. - genStats(c.stats, switchEnv, CRT_FLOW_TARGET); - } + if (switchEnv.info.cont != null) { + Assert.check(patternSwitch); + code.resolve(switchEnv.info.cont, switchStart); + } - if (switchEnv.info.cont != null) { - Assert.check(patternSwitch); - code.resolve(switchEnv.info.cont, switchStart); - } + // Resolve all breaks. + Chain exit = switchEnv.info.exit; + if (exit != null) { + code.resolve(exit); + exit.state.defined.excludeFrom(limit); + } - // Resolve all breaks. - Chain exit = switchEnv.info.exit; - if (exit != null) { - code.resolve(exit); - exit.state.defined.excludeFrom(limit); - } + // If we have not set the default offset, we do so now. + if (code.get4(tableBase) == -1) { + code.put4(tableBase, code.entryPoint(stateSwitch) - startpc); + } - // If we have not set the default offset, we do so now. - if (code.get4(tableBase) == -1) { - code.put4(tableBase, code.entryPoint(stateSwitch) - startpc); + if (opcode == tableswitch) { + // Let any unfilled slots point to the default case. + int defaultOffset = code.get4(tableBase); + for (long i = lo; i <= hi; i++) { + int t = (int)(tableBase + 4 * (i - lo + 3)); + if (code.get4(t) == -1) + code.put4(t, defaultOffset); } - - if (opcode == tableswitch) { - // Let any unfilled slots point to the default case. - int defaultOffset = code.get4(tableBase); - for (long i = lo; i <= hi; i++) { - int t = (int)(tableBase + 4 * (i - lo + 3)); - if (code.get4(t) == -1) - code.put4(t, defaultOffset); - } - } else { - // Sort non-default offsets and copy into lookup table. - if (defaultIndex >= 0) - for (int i = defaultIndex; i < labels.length - 1; i++) { - labels[i] = labels[i+1]; - offsets[i] = offsets[i+1]; - } - if (nlabels > 0) - qsort2(labels, offsets, 0, nlabels - 1); - for (int i = 0; i < nlabels; i++) { - int caseidx = tableBase + 8 * (i + 1); - code.put4(caseidx, labels[i]); - code.put4(caseidx + 4, offsets[i]); + } else { + // Sort non-default offsets and copy into lookup table. + if (defaultIndex >= 0) + for (int i = defaultIndex; i < labels.length - 1; i++) { + labels[i] = labels[i+1]; + offsets[i] = offsets[i+1]; } + if (nlabels > 0) + qsort2(labels, offsets, 0, nlabels - 1); + for (int i = 0; i < nlabels; i++) { + int caseidx = tableBase + 8 * (i + 1); + code.put4(caseidx, labels[i]); + 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)); - } + if (swtch instanceof JCSwitchExpression) { + // Emit line position for the end of a switch expression + code.statBegin(TreeInfo.endPos(swtch)); } - code.endScopes(limit); - } finally { - code.currentUnsetFields = prevCodeUnsetFields; } + code.endScopes(limit); } //where /** Sort (int) arrays of keys and values @@ -1844,32 +1857,36 @@ void registerCatch(DiagnosticPosition pos, public void visitIf(JCIf tree) { Set prevCodeUnsetFields = code.currentUnsetFields; try { - 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); - thenExit = code.branch(goto_); - } - if (elseChain != null) { - code.resolve(elseChain); - if (tree.elsepart != null) { - genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET); - } - } - code.resolve(thenExit); - code.endScopes(limit); - Assert.check(code.isStatementStart()); + 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); + thenExit = code.branch(goto_); + } + if (elseChain != null) { + code.resolve(elseChain); + if (tree.elsepart != null) { + genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET); + } + } + code.resolve(thenExit); + code.endScopes(limit); + Assert.check(code.isStatementStart()); + } + public void visitExec(JCExpressionStatement tree) { // Optimize x++ to ++x and x-- to --x. JCExpression e = tree.expr;