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/java/lang/classfile/attribute/StackMapFrameInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java index 920db8ac9d7..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 @@ -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; @@ -61,6 +62,13 @@ public sealed interface StackMapFrameInfo */ List stack(); + /** + * {@return the expanded unset fields} + * + * @see Specs + */ + List unsetFields(); + /** * {@return a new stack map frame} * @param target the location of the frame @@ -71,7 +79,22 @@ public static StackMapFrameInfo of(Label target, List locals, List stack) { - return new StackMapDecoder.StackMapFrameImpl(255, target, locals, 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/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 47908b99f44..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 @@ -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; @@ -289,8 +289,14 @@ else if (frameType < 128) { offsetDelta = frameType & 0x3f; p = adjustForObjectOrUninitialized(p + 1); } - else + else { switch (frameType) { + case 246 -> { + int numberOfUnsetFields = classReader.readU2(p + 1); + p += 3; + p += 2 * numberOfUnsetFields; + continue; // do not move bci/create label + } case 247 -> { offsetDelta = classReader.readU2(p + 1); p = adjustForObjectOrUninitialized(p + 3); @@ -323,6 +329,7 @@ else if (frameType < 128) { } default -> throw new IllegalArgumentException("Bad frame type: " + frameType); } + } bci += offsetDelta + 1; inflateLabel(bci); } 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..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 @@ -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; @@ -50,8 +55,10 @@ public class StackMapDecoder { private static final int + 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; @@ -181,12 +188,29 @@ 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(); int bci = -1; - var entries = new StackMapFrameInfo[u2()]; - for (int ei = 0; ei < entries.length; ei++) { + 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; @@ -195,8 +219,14 @@ List entries() { bci += frameType - 63; stack = List.of(readVerificationTypeInfo()); } else { - if (frameType < SAME_LOCALS_1_STACK_ITEM_EXTENDED) + if (frameType < RESERVED_TAGS_UPPER_LIMIT) throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType); + if (frameType == ASSERT_UNSET_FIELDS) { + unsetFields = readEntryList(p, NameAndTypeEntry.class); + p += 2 + unsetFields.size() * 2; + deferredUnsetFields.add(unsetFields); + continue; // defer entry until we can get the bci + } bci += u2() + 1; if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { stack = List.of(readVerificationTypeInfo()); @@ -223,12 +253,22 @@ List entries() { stack = List.of(newStack); } } - entries[ei] = new StackMapFrameImpl(frameType, - ctx.getLabel(bci), + 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, locals, - stack); + stack)); } - return List.of(entries); + return List.copyOf(entries); } private VerificationTypeInfo readVerificationTypeInfo() { @@ -299,12 +339,21 @@ 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); + } + + 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 0c4131dd1ba..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 @@ -159,9 +159,11 @@ 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; + private static final int RESERVED_TAGS_UPPER_LIMIT = ASSERT_UNSET_FIELDS; // not inclusive public int get_frame_count() { return _frame_count; @@ -278,7 +280,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 < RESERVED_TAGS_UPPER_LIMIT) { _verifier.classError("reserved frame type"); } if (frame_type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { 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 2cd881120a9..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; @@ -34,7 +35,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; @@ -60,8 +60,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; @@ -218,6 +216,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 +343,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); } /** @@ -2298,11 +2297,21 @@ 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); 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 + */ + unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, assign != null ? assign : tree, findUninitStrictFields()); + } } } } @@ -2534,6 +2543,13 @@ public void visitMethodDef(JCMethodDecl tree) { */ initParam(def); } + if (isConstructor) { + Set unsetFields = findUninitStrictFields(); + if (unsetFields != null && !unsetFields.isEmpty()) { + unsetFieldsInfo.addUnsetFieldsInfo(classDef.sym, tree.body, unsetFields); + } + } + // else we are in an instance initializer block; // leave caught unchanged. scan(tree.body); @@ -2589,6 +2605,17 @@ public void visitMethodDef(JCMethodDecl tree) { } } + 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.add(variableDecl.sym); + } + } + return unsetFields; + } + private void clearPendingExits(boolean inMethod) { List exits = pendingExits.toList(); pendingExits = new ListBuffer<>(); @@ -3153,7 +3180,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 new file mode 100644 index 00000000000..a75869e0aa3 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/UnsetFieldsInfo.java @@ -0,0 +1,91 @@ +/* + * 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.Map; +import java.util.Set; +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; +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, Set 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 Set getUnsetFields(ClassSymbol csym, JCTree tree) { + Map> treeToFieldsMap = unsetFieldsMap.get(csym); + if (treeToFieldsMap != null) { + Set result = treeToFieldsMap.get(tree); + if (result != null) { + return result; + } + } + 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 ea383805eb9..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 @@ -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; @@ -1263,7 +1264,7 @@ void writeStackMap(Code code) { Assert.checkNull(code.stackMapBuffer); for (int i=0; i> cpToUnsetFieldsMap = new HashMap<>(); + + public Set currentUnsetFields; + + boolean generateAssertUnsetFieldsFrame; + /** Construct a code object, given the settings of the fatcode, * debugging info switches and the CharacterRangeTable. */ @@ -209,7 +206,8 @@ public Code(MethodSymbol meth, CRTable crt, Symtab syms, Types types, - PoolWriter poolWriter) { + PoolWriter poolWriter, + boolean generateAssertUnsetFieldsFrame) { this.meth = meth; this.fatcode = fatcode; this.lineMap = lineMap; @@ -231,6 +229,7 @@ public Code(MethodSymbol meth, } state = new State(); lvar = new LocalVar[20]; + this.generateAssertUnsetFieldsFrame = generateAssertUnsetFieldsFrame; } @@ -1081,7 +1080,6 @@ public void emitop2(int op, int od, PoolConstant data) { } // postop(); } - /** Emit an opcode with a four-byte operand field. */ public void emitop4(int op, int od) { @@ -1228,13 +1226,14 @@ static class StackMapFrame { int pc; Type[] locals; Type[] stack; + Set unsetFields; } /** A buffer of cldc stack map entries. */ StackMapFrame[] stackMapBuffer = null; /** A buffer of compressed StackMapTable entries. */ - StackMapTableFrame[] stackMapTableBuffer = null; + StackMapTableEntry[] stackMapTableBuffer = null; int stackMapBufferSize = 0; /** The last PC at which we generated a stack map. */ @@ -1355,20 +1354,34 @@ void emitStackMapFrame(int pc, int localsSize) { } } + Set unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc); + boolean generateAssertUnsetFieldsEntry = unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame; + if (stackMapTableBuffer == null) { - stackMapTableBuffer = new StackMapTableFrame[20]; + stackMapTableBuffer = new StackMapTableEntry[20]; } else { stackMapTableBuffer = ArrayUtils.ensureCapacity( stackMapTableBuffer, - stackMapBufferSize); + stackMapBufferSize + (generateAssertUnsetFieldsEntry ? 1 : 0)); + } + + if (generateAssertUnsetFieldsEntry) { + if (lastFrame.unsetFields == null || !lastFrame.unsetFields.equals(unsetFieldsAtPC)) { + stackMapTableBuffer[stackMapBufferSize++] = new StackMapTableEntry.AssertUnsetFields(pc, unsetFieldsAtPC); + frame.unsetFields = unsetFieldsAtPC; + } } stackMapTableBuffer[stackMapBufferSize++] = - StackMapTableFrame.getInstance(frame, lastFrame.pc, lastFrame.locals, types); + StackMapTableEntry.getInstance(frame, lastFrame, types, pc); frameBeforeLast = lastFrame; lastFrame = frame; } + public void addUnsetFieldsAtPC(int pc, Set unsetFields) { + cpToUnsetFieldsMap.put(pc, unsetFields); + } + StackMapFrame getInitialFrame() { StackMapFrame frame = new StackMapFrame(); List arg_types = ((MethodType)meth.externalType(types)).argtypes; @@ -1468,6 +1481,9 @@ public Chain branch(int opcode) { result = new Chain(emitJump(opcode), result, state.dup()); + if (currentUnsetFields != null) { + addUnsetFieldsAtPC(result.pc, currentUnsetFields); + } fixedPc = fatcode; if (opcode == goto_) alive = false; } @@ -1479,6 +1495,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())); @@ -1505,13 +1522,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) { + addUnsetFieldsAtPC(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) { + addUnsetFieldsAtPC(originalTarget, cpToUnsetFieldsMap.get(chain.pc)); + } + } Assert.check(!alive || chain.state.stacksize == newState.stacksize && chain.state.nlocks == newState.nlocks); 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 beb666a0240..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 @@ -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,11 +129,13 @@ 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; annotate = Annotate.instance(context); qualifiedSymbolCache = new HashMap<>(); + generateAssertUnsetFieldsFrame = options.isSet("generateAssertUnsetFieldsFrame"); } /** Switches @@ -141,6 +145,7 @@ protected Gen(Context context) { private final boolean genCrt; private final boolean debugCode; private boolean disableVirtualizedPrivateInvoke; + private boolean generateAssertUnsetFieldsFrame; /** Code buffer, set by genMethod. */ @@ -989,6 +994,10 @@ 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 = code.currentUnsetFields; + if (meth.isConstructor()) { + code.currentUnsetFields = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree.body); + } try { genStat(tree.body, env); @@ -996,6 +1005,8 @@ 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 { + code.currentUnsetFields = prevUnsetFields; } if (code.state.stacksize != 0) { @@ -1064,7 +1075,8 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { : null, syms, types, - poolWriter); + poolWriter, + generateAssertUnsetFieldsFrame); items = new Items(poolWriter, code, syms, types); if (code.debugCode) { System.err.println(meth + " for body " + tree); @@ -1200,6 +1212,19 @@ private void genLoop(JCStatement loop, JCExpression cond, List step, boolean testFirst) { + Set prevCodeUnsetFields = code.currentUnsetFields; + try { + 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 @@ -1266,11 +1291,13 @@ public void visitSwitch(JCSwitch tree) { public void visitSwitchExpression(JCSwitchExpression tree) { code.resolvePending(); boolean prevInCondSwitchExpression = inCondSwitchExpression; + Set prevCodeUnsetFields = code.currentUnsetFields; try { inCondSwitchExpression = false; doHandleSwitchExpression(tree); } finally { inCondSwitchExpression = prevInCondSwitchExpression; + code.currentUnsetFields = prevCodeUnsetFields; } result = items.makeStackItem(pt); } @@ -1346,6 +1373,16 @@ public void visitLambda(JCLambda tree) { private void handleSwitch(JCTree swtch, JCExpression selector, List cases, boolean patternSwitch) { + Set prevCodeUnsetFields = code.currentUnsetFields; + try { + 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; @@ -1357,13 +1394,13 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case sel.load().drop(); if (genCrt) code.crt.put(TreeInfo.skipParens(selector), - CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); + 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()); + CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); Env switchEnv = env.dup(swtch, new GenContext()); switchEnv.info.isSwitch = true; @@ -1399,11 +1436,11 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case 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; + 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); @@ -1439,8 +1476,8 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case if (i != defaultIndex) { if (opcode == tableswitch) { code.put4( - tableBase + 4 * (labels[i] - lo + 3), - pc - startpc); + tableBase + 4 * (labels[i] - lo + 3), + pc - startpc); } else { offsets[i] = pc - startpc; } @@ -1494,8 +1531,8 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case } if (swtch instanceof JCSwitchExpression) { - // Emit line position for the end of a switch expression - code.statBegin(TreeInfo.endPos(swtch)); + // Emit line position for the end of a switch expression + code.statBegin(TreeInfo.endPos(swtch)); } } code.endScopes(limit); @@ -1598,6 +1635,15 @@ void afterBody() { * @param env The current environment of the body. */ void genTry(JCTree body, List catchers, Env env) { + Set prevCodeUnsetFields = code.currentUnsetFields; + try { + 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(); @@ -1617,8 +1663,8 @@ void genTry(JCTree body, List catchers, Env env) { endFinalizerGap(env); env.info.finalize.afterBody(); boolean hasFinalizer = - env.info.finalize != null && - env.info.finalize.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); @@ -1627,7 +1673,7 @@ void genTry(JCTree body, List catchers, Env env) { if (hasFinalizer || l.tail.nonEmpty()) { code.statBegin(TreeInfo.endPos(env.tree)); exitChain = Code.mergeChains(exitChain, - code.branch(goto_)); + code.branch(goto_)); } endFinalizerGap(env); } @@ -1650,7 +1696,7 @@ void genTry(JCTree body, List catchers, Env env) { while (env.info.gaps.nonEmpty()) { int endseg = env.info.gaps.next().intValue(); registerCatch(body.pos(), startseg, endseg, - catchallpc, 0); + catchallpc, 0); startseg = env.info.gaps.next().intValue(); } code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); @@ -1665,8 +1711,8 @@ void genTry(JCTree body, List catchers, Env env) { excVar.load(); registerCatch(body.pos(), startseg, - env.info.gaps.next().intValue(), - catchallpc, 0); + env.info.gaps.next().intValue(), + catchallpc, 0); code.emitop0(athrow); code.markDead(); @@ -1809,11 +1855,20 @@ void registerCatch(DiagnosticPosition pos, } public void visitIf(JCIf tree) { + Set prevCodeUnsetFields = code.currentUnsetFields; + try { + visitIfHelper(tree); + } finally { + code.currentUnsetFields = prevCodeUnsetFields; + } + } + + public void visitIfHelper(JCIf tree) { int limit = code.nextreg; Chain thenExit = null; Assert.check(code.isStatementStart()); CondItem c = genCond(TreeInfo.skipParens(tree.cond), - CRT_FLOW_CONTROLLER); + CRT_FLOW_CONTROLLER); Chain elseChain = c.jumpFalse(); Assert.check(code.isStatementStart()); if (!c.isFalse()) { @@ -2140,6 +2195,8 @@ 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(); + Set tmpUnsetSymbols = unsetFieldsInfo.getUnsetFields(env.enclClass.sym, tree); + code.currentUnsetFields = tmpUnsetSymbols != null ? tmpUnsetSymbols : code.currentUnsetFields; if (tree.rhs.type.hasTag(BOT)) { /* This is just a case of widening reference conversion that per 5.1.5 simply calls for "regarding a reference as having some other type in a manner that can be proved 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]; 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..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 @@ -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,53 @@ 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 == 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) + 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 +121,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 +152,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 +173,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 +192,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 +210,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 +235,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 +269,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 +276,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 65c48d60126..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 @@ -569,10 +569,23 @@ 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 (NameAndTypeEntry field : frame.unsetFields()) { + print("unset_field = #"); + constantWriter.write(field.index()); + println(); + } + indent(-1); + indent(-1); + } 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 +598,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 +613,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/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java index 5b7de2a4565..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, 2024, 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); diff --git a/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java index 8f4af741e70..5651f9a5076 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; @@ -1391,6 +1392,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)) { 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" +