_children;
+
+ public:
+ IPInlineSite(ciMethod* method, int bci) : _method(method), _bci(bci) {}
+
+ IPInlineSite() : _method(nullptr), _bci(-999) {}
+
+ ~IPInlineSite() {
+ // Since GrowableArrayCHeap uses copy semantics to resize itself we
+ // cannot free the stream inside IPInlineAttempt's destructor unfortunately
+ // and have to take care of this here instead.
+ for (int i = 0; i < _attempts.length(); i++) {
+ _attempts.at(i).deallocate_stream();
+ }
+ }
+
+ void set_source(ciMethod* method, int bci) {
+ _method = method;
+ _bci = bci;
+ }
+
+ // Finds the node for an inline attempt that occurred inside this inline.
+ // If this is a new site, provide the callee otherwise null.
+ // Returned reference is valid until any at_bci is called with non-null callee.
+ IPInlineSite& at_bci(int bci, ciMethod* callee);
+ // The returned pointer stays valid until InlinePrinter is destructed.
+ outputStream* add(InliningResult result);
+
+ void dump(outputStream* tty, int level) const;
+ };
+
+ bool is_enabled() const;
+
+ Compile* C;
+
+ // In case print inline is disabled, this null stream is returned from ::record()
+ nullStream _nullStream;
+
+ // Locates the IPInlineSite node that corresponds to this JVM state.
+ // state may be null. In this case, the root node is returned.
+ // If this is a new site, provide the callee otherwise null.
+ // Returned pointer is valid until InlinePrinter is destructed.
+ IPInlineSite* locate(JVMState* state, ciMethod* callee);
+
+ IPInlineSite _root{nullptr, 0};
+
+public:
+ InlinePrinter(Compile* compile) : C(compile) {}
+
+ // Saves the result of an inline attempt of method at state.
+ // An optional string message with more details that is copied to the stream for this attempt. Pointer is not captured.
+ // Returns an output stream which stores the message associated with this attempt. The buffer stays valid until InlinePrinter is destructed.
+ // You can print arbitrary information to this stream but do not add line breaks, as this will break formatting.
+ outputStream* record(ciMethod* callee, JVMState* state, InliningResult result, const char* msg = nullptr);
+
+ // Prints all collected inlining information to the given output stream.
+ void print_on(outputStream* tty) const;
+};
+
+#endif // PRINTINLINING_HPP
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index 45580b36270..d8df234c57b 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -300,11 +300,9 @@ WB_END
WB_ENTRY(void, WB_ReadFromNoaccessArea(JNIEnv* env, jobject o))
size_t granularity = os::vm_allocation_granularity();
ReservedHeapSpace rhs = HeapReserver::reserve(100 * granularity, granularity, os::vm_page_size(), nullptr);
- VirtualSpace vs;
- vs.initialize(rhs, 50 * granularity);
// Check if constraints are complied
- if (!( UseCompressedOops && rhs.base() != nullptr &&
+ if (!( UseCompressedOops && rhs.is_reserved() &&
CompressedOops::base() != nullptr &&
CompressedOops::use_implicit_null_checks() )) {
tty->print_cr("WB_ReadFromNoaccessArea method is useless:\n "
@@ -318,6 +316,10 @@ WB_ENTRY(void, WB_ReadFromNoaccessArea(JNIEnv* env, jobject o))
CompressedOops::use_implicit_null_checks());
return;
}
+
+ VirtualSpace vs;
+ vs.initialize(rhs, 50 * granularity);
+
tty->print_cr("Reading from no access area... ");
tty->print_cr("*(vs.low_boundary() - rhs.noaccess_prefix() / 2 ) = %c",
*(vs.low_boundary() - rhs.noaccess_prefix() / 2 ));
@@ -327,6 +329,11 @@ static jint wb_stress_virtual_space_resize(size_t reserved_space_size,
size_t magnitude, size_t iterations) {
size_t granularity = os::vm_allocation_granularity();
ReservedHeapSpace rhs = HeapReserver::reserve(reserved_space_size * granularity, granularity, os::vm_page_size(), nullptr);
+ if (!rhs.is_reserved()) {
+ tty->print_cr("Failed to initialize ReservedSpace. Can't proceed.");
+ return 3;
+ }
+
VirtualSpace vs;
if (!vs.initialize(rhs, 0)) {
tty->print_cr("Failed to initialize VirtualSpace. Can't proceed.");
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index 094d68e1a84..0654bd3e092 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -1212,8 +1212,7 @@ void os::print_date_and_time(outputStream *st, char* buf, size_t buflen) {
if (localtime_pd(&tloc, &tz) != nullptr) {
wchar_t w_buf[80];
size_t n = ::wcsftime(w_buf, 80, L"%Z", &tz);
- if (n > 0) {
- ::wcstombs(buf, w_buf, buflen);
+ if (n > 0 && ::wcstombs(buf, w_buf, buflen) != (size_t)-1) {
st->print("Time: %s %s", timestring, buf);
} else {
st->print("Time: %s", timestring);
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 9e7692bda6d..9a428167e00 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -828,12 +828,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
}
#endif
-#if INCLUDE_JVMCI
- if (force_JVMCI_initialization) {
- JVMCI::initialize_compiler(CHECK_JNI_ERR);
- }
-#endif
-
if (NativeHeapTrimmer::enabled()) {
NativeHeapTrimmer::initialize();
}
@@ -848,6 +842,12 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
// Notify JVMTI agents that VM initialization is complete - nop if no agents.
JvmtiExport::post_vm_initialized();
+#if INCLUDE_JVMCI
+ if (force_JVMCI_initialization) {
+ JVMCI::initialize_compiler(CHECK_JNI_ERR);
+ }
+#endif
+
JFR_ONLY(Jfr::on_create_vm_3();)
// SapMachine 2019-02-20: Vitals
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
index 37c4aebf58b..6613f300161 100644
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -1261,6 +1261,8 @@
declare_type(CompilerThread, JavaThread) \
declare_type(StringDedupThread, JavaThread) \
declare_type(AttachListenerThread, JavaThread) \
+ DEBUG_ONLY(COMPILER2_OR_JVMCI_PRESENT( \
+ declare_type(DeoptimizeObjectsALotThread, JavaThread))) \
declare_toplevel_type(OSThread) \
declare_toplevel_type(JavaFrameAnchor) \
\
diff --git a/src/java.base/aix/native/libjli/java_md_aix.h b/src/java.base/aix/native/libjli/java_md_aix.h
index 231b7b2d0fd..d319a1d6353 100644
--- a/src/java.base/aix/native/libjli/java_md_aix.h
+++ b/src/java.base/aix/native/libjli/java_md_aix.h
@@ -29,7 +29,7 @@
/*
* Very limited AIX port of dladdr() for libjli.so.
*
- * We try to mimic dladdr(3) on Linux (see http://linux.die.net/man/3/dladdr)
+ * We try to mimic dladdr(3) on Linux (see https://linux.die.net/man/3/dladdr)
* dladdr(3) is not POSIX but a GNU extension, and is not available on AIX.
*
* We only support Dl_info.dli_fname here as this is the only thing that is
diff --git a/src/java.base/linux/native/libsimdsort/xss-common-qsort.h b/src/java.base/linux/native/libsimdsort/xss-common-qsort.h
index 07279a487c4..95fe8738d35 100644
--- a/src/java.base/linux/native/libsimdsort/xss-common-qsort.h
+++ b/src/java.base/linux/native/libsimdsort/xss-common-qsort.h
@@ -49,7 +49,7 @@
* [3] https://github.com/simd-sorting/fast-and-robust: SPDX-License-Identifier:
* MIT
*
- * [4] http://mitp-content-server.mit.edu:18180/books/content/sectbyfn?collid=books_pres_0&fn=Chapter%2027.pdf&id=8030
+ * [4] https://mitp-content-server.mit.edu/books/content/sectbyfn?collid=books_pres_0&fn=Chapter%2027.pdf&id=8030
*
*/
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java b/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java
index e13eec42905..2d9ad93d2fa 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -379,11 +379,10 @@ protected SecretKey engineGenerateSecret(String algorithm)
throw new NoSuchAlgorithmException("null algorithm");
}
- if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") &&
- !AllowKDF.VALUE) {
-
- throw new NoSuchAlgorithmException("Unsupported secret key "
- + "algorithm: " + algorithm);
+ if (!KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm) &&
+ !AllowKDF.VALUE) {
+ throw new NoSuchAlgorithmException(
+ "Unsupported secret key algorithm: " + algorithm);
}
byte[] secret = engineGenerateSecret();
@@ -419,13 +418,17 @@ protected SecretKey engineGenerateSecret(String algorithm)
throw new InvalidKeyException("Key material is too short");
}
return skey;
- } else if (algorithm.equals("TlsPremasterSecret")) {
- // remove leading zero bytes per RFC 5246 Section 8.1.2
- return new SecretKeySpec(
+ } else if (KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm)) {
+ if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) {
+ // remove leading zero bytes per RFC 5246 Section 8.1.2
+ return new SecretKeySpec(
KeyUtil.trimZeroes(secret), "TlsPremasterSecret");
+ } else {
+ return new SecretKeySpec(secret, algorithm);
+ }
} else {
- throw new NoSuchAlgorithmException("Unsupported secret key "
- + "algorithm: "+ algorithm);
+ throw new NoSuchAlgorithmException(
+ "Unsupported secret key algorithm: " + algorithm);
}
}
}
diff --git a/src/java.base/share/classes/java/io/OptionalDataException.java b/src/java.base/share/classes/java/io/OptionalDataException.java
index f98bf079e8e..215b3b2906c 100644
--- a/src/java.base/share/classes/java/io/OptionalDataException.java
+++ b/src/java.base/share/classes/java/io/OptionalDataException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -44,7 +44,7 @@
*
* @since 1.1
*/
-public class OptionalDataException extends ObjectStreamException {
+public final class OptionalDataException extends ObjectStreamException {
@java.io.Serial
private static final long serialVersionUID = -8011121865681257820L;
diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
index b40f6274412..fd9dcf60e54 100644
--- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
+++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
@@ -830,9 +830,9 @@ public AbstractStringBuilder append(int i) {
int spaceNeeded = count + DecimalDigits.stringSize(i);
ensureCapacityInternal(spaceNeeded);
if (isLatin1()) {
- StringLatin1.getChars(i, spaceNeeded, value);
+ DecimalDigits.getCharsLatin1(i, spaceNeeded, value);
} else {
- StringUTF16.getChars(i, count, spaceNeeded, value);
+ DecimalDigits.getCharsUTF16(i, spaceNeeded, value);
}
this.count = spaceNeeded;
return this;
@@ -855,9 +855,9 @@ public AbstractStringBuilder append(long l) {
int spaceNeeded = count + DecimalDigits.stringSize(l);
ensureCapacityInternal(spaceNeeded);
if (isLatin1()) {
- StringLatin1.getChars(l, spaceNeeded, value);
+ DecimalDigits.getCharsLatin1(l, spaceNeeded, value);
} else {
- StringUTF16.getChars(l, count, spaceNeeded, value);
+ DecimalDigits.getCharsUTF16(l, spaceNeeded, value);
}
this.count = spaceNeeded;
return this;
diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java
index a6bf739220f..99b056ec18a 100644
--- a/src/java.base/share/classes/java/lang/Integer.java
+++ b/src/java.base/share/classes/java/lang/Integer.java
@@ -432,11 +432,11 @@ public static String toString(int i) {
int size = DecimalDigits.stringSize(i);
if (COMPACT_STRINGS) {
byte[] buf = new byte[size];
- StringLatin1.getChars(i, size, buf);
+ DecimalDigits.getCharsLatin1(i, size, buf);
return new String(buf, LATIN1);
} else {
byte[] buf = new byte[size * 2];
- StringUTF16.getChars(i, size, buf);
+ DecimalDigits.getCharsUTF16(i, size, buf);
return new String(buf, UTF16);
}
}
diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java
index 822199bb09b..c401fcd8027 100644
--- a/src/java.base/share/classes/java/lang/Long.java
+++ b/src/java.base/share/classes/java/lang/Long.java
@@ -462,11 +462,11 @@ public static String toString(long i) {
int size = DecimalDigits.stringSize(i);
if (COMPACT_STRINGS) {
byte[] buf = new byte[size];
- StringLatin1.getChars(i, size, buf);
+ DecimalDigits.getCharsLatin1(i, size, buf);
return new String(buf, LATIN1);
} else {
byte[] buf = new byte[size * 2];
- StringUTF16.getChars(i, size, buf);
+ DecimalDigits.getCharsUTF16(i, size, buf);
return new String(buf, UTF16);
}
}
diff --git a/src/java.base/share/classes/java/lang/Package.java b/src/java.base/share/classes/java/lang/Package.java
index 424c390c8ef..82f40ce373b 100644
--- a/src/java.base/share/classes/java/lang/Package.java
+++ b/src/java.base/share/classes/java/lang/Package.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -113,7 +113,7 @@
*
* @since 1.2
*/
-public class Package extends NamedPackage implements java.lang.reflect.AnnotatedElement {
+public final class Package extends NamedPackage implements java.lang.reflect.AnnotatedElement {
/**
* Return the name of this package.
*
diff --git a/src/java.base/share/classes/java/lang/Runtime.java b/src/java.base/share/classes/java/lang/Runtime.java
index 764a9d7fa63..6f44114e533 100644
--- a/src/java.base/share/classes/java/lang/Runtime.java
+++ b/src/java.base/share/classes/java/lang/Runtime.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -120,7 +120,7 @@
* @since 1.0
*/
-public class Runtime {
+public final class Runtime {
private static final Runtime currentRuntime = new Runtime();
private static Version version;
diff --git a/src/java.base/share/classes/java/lang/StringConcatHelper.java b/src/java.base/share/classes/java/lang/StringConcatHelper.java
index b635d0dee0f..632fe0f58b5 100644
--- a/src/java.base/share/classes/java/lang/StringConcatHelper.java
+++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java
@@ -298,12 +298,12 @@ static long prepend(long indexCoder, byte[] buf, char value, String prefix) {
static long prepend(long indexCoder, byte[] buf, int value, String prefix) {
int index = (int)indexCoder;
if (indexCoder < UTF16) {
- index = StringLatin1.getChars(value, index, buf);
+ index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
return index;
} else {
- index = StringUTF16.getChars(value, index, buf);
+ index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
return index | UTF16;
@@ -324,12 +324,12 @@ static long prepend(long indexCoder, byte[] buf, int value, String prefix) {
static long prepend(long indexCoder, byte[] buf, long value, String prefix) {
int index = (int)indexCoder;
if (indexCoder < UTF16) {
- index = StringLatin1.getChars(value, index, buf);
+ index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
return index;
} else {
- index = StringUTF16.getChars(value, index, buf);
+ index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
return index | UTF16;
@@ -682,11 +682,11 @@ static int prepend(int index, byte coder, byte[] buf, char value, String prefix)
*/
static int prepend(int index, byte coder, byte[] buf, int value, String prefix) {
if (coder == String.LATIN1) {
- index = StringLatin1.getChars(value, index, buf);
+ index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
} else {
- index = StringUTF16.getChars(value, index, buf);
+ index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
}
@@ -706,11 +706,11 @@ static int prepend(int index, byte coder, byte[] buf, int value, String prefix)
*/
static int prepend(int index, byte coder, byte[] buf, long value, String prefix) {
if (coder == String.LATIN1) {
- index = StringLatin1.getChars(value, index, buf);
+ index = DecimalDigits.getCharsLatin1(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.LATIN1);
} else {
- index = StringUTF16.getChars(value, index, buf);
+ index = DecimalDigits.getCharsUTF16(value, index, buf);
index -= prefix.length();
prefix.getBytes(buf, index, String.UTF16);
}
diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java
index 7680bcbfa45..342439d1341 100644
--- a/src/java.base/share/classes/java/lang/StringLatin1.java
+++ b/src/java.base/share/classes/java/lang/StringLatin1.java
@@ -34,7 +34,6 @@
import java.util.stream.StreamSupport;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
-import jdk.internal.util.DecimalDigits;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import static java.lang.String.LATIN1;
@@ -86,120 +85,6 @@ public static byte[] inflate(byte[] value, int off, int len) {
return ret;
}
- /**
- * Places characters representing the integer i into the
- * character array buf. The characters are placed into
- * the buffer backwards starting with the least significant
- * digit at the specified index (exclusive), and working
- * backwards from there.
- *
- * @implNote This method converts positive inputs into negative
- * values, to cover the Integer.MIN_VALUE case. Converting otherwise
- * (negative to positive) will expose -Integer.MIN_VALUE that overflows
- * integer.
- *
- * @param i value to convert
- * @param index next index, after the least significant digit
- * @param buf target buffer, Latin1-encoded
- * @return index of the most significant digit or minus sign, if present
- */
- static int getChars(int i, int index, byte[] buf) {
- // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
- int q;
- int charPos = index;
-
- boolean negative = i < 0;
- if (!negative) {
- i = -i;
- }
-
- // Generate two digits per iteration
- while (i <= -100) {
- q = i / 100;
- charPos -= 2;
- writeDigitPair(buf, charPos, (q * 100) - i);
- i = q;
- }
-
- // We know there are at most two digits left at this point.
- if (i < -9) {
- charPos -= 2;
- writeDigitPair(buf, charPos, -i);
- } else {
- buf[--charPos] = (byte)('0' - i);
- }
-
- if (negative) {
- buf[--charPos] = (byte)'-';
- }
- return charPos;
- }
-
- /**
- * Places characters representing the long i into the
- * character array buf. The characters are placed into
- * the buffer backwards starting with the least significant
- * digit at the specified index (exclusive), and working
- * backwards from there.
- *
- * @implNote This method converts positive inputs into negative
- * values, to cover the Long.MIN_VALUE case. Converting otherwise
- * (negative to positive) will expose -Long.MIN_VALUE that overflows
- * long.
- *
- * @param i value to convert
- * @param index next index, after the least significant digit
- * @param buf target buffer, Latin1-encoded
- * @return index of the most significant digit or minus sign, if present
- */
- static int getChars(long i, int index, byte[] buf) {
- // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
- long q;
- int charPos = index;
-
- boolean negative = (i < 0);
- if (!negative) {
- i = -i;
- }
-
- // Get 2 digits/iteration using longs until quotient fits into an int
- while (i <= Integer.MIN_VALUE) {
- q = i / 100;
- charPos -= 2;
- writeDigitPair(buf, charPos, (int)((q * 100) - i));
- i = q;
- }
-
- // Get 2 digits/iteration using ints
- int q2;
- int i2 = (int)i;
- while (i2 <= -100) {
- q2 = i2 / 100;
- charPos -= 2;
- writeDigitPair(buf, charPos, (q2 * 100) - i2);
- i2 = q2;
- }
-
- // We know there are at most two digits left at this point.
- if (i2 < -9) {
- charPos -= 2;
- writeDigitPair(buf, charPos, -i2);
- } else {
- buf[--charPos] = (byte)('0' - i2);
- }
-
- if (negative) {
- buf[--charPos] = (byte)'-';
- }
- return charPos;
- }
-
- private static void writeDigitPair(byte[] buf, int charPos, int value) {
- short pair = DecimalDigits.digitPair(value);
- buf[charPos] = (byte)(pair);
- buf[charPos + 1] = (byte)(pair >> 8);
- }
-
public static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) {
inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java
index f04b991827f..a1dcca8ffad 100644
--- a/src/java.base/share/classes/java/lang/StringUTF16.java
+++ b/src/java.base/share/classes/java/lang/StringUTF16.java
@@ -35,7 +35,6 @@
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
-import jdk.internal.util.DecimalDigits;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
@@ -1513,20 +1512,6 @@ public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
return codePointCount(val, beginIndex, endIndex, true /* checked */);
}
- public static int getChars(int i, int begin, int end, byte[] value) {
- checkBoundsBeginEnd(begin, end, value);
- int pos = getChars(i, end, value);
- assert begin == pos;
- return pos;
- }
-
- public static int getChars(long l, int begin, int end, byte[] value) {
- checkBoundsBeginEnd(begin, end, value);
- int pos = getChars(l, end, value);
- assert begin == pos;
- return pos;
- }
-
public static boolean contentEquals(byte[] v1, byte[] v2, int len) {
checkBoundsOffCount(0, len, v2);
for (int i = 0; i < len; i++) {
@@ -1662,109 +1647,6 @@ public static int lastIndexOfLatin1(byte[] src, int srcCount,
static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
- // Used by trusted callers. Assumes all necessary bounds checks have
- // been done by the caller.
-
- /**
- * This is a variant of {@link StringLatin1#getChars(int, int, byte[])}, but for
- * UTF-16 coder.
- *
- * @param i value to convert
- * @param index next index, after the least significant digit
- * @param buf target buffer, UTF16-coded.
- * @return index of the most significant digit or minus sign, if present
- */
- static int getChars(int i, int index, byte[] buf) {
- // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
- int q, r;
- int charPos = index;
-
- boolean negative = (i < 0);
- if (!negative) {
- i = -i;
- }
-
- // Get 2 digits/iteration using ints
- while (i <= -100) {
- q = i / 100;
- r = (q * 100) - i;
- i = q;
- charPos -= 2;
- putPair(buf, charPos, r);
- }
-
- // We know there are at most two digits left at this point.
- if (i < -9) {
- charPos -= 2;
- putPair(buf, charPos, -i);
- } else {
- putChar(buf, --charPos, '0' - i);
- }
-
- if (negative) {
- putChar(buf, --charPos, '-');
- }
- return charPos;
- }
-
- /**
- * This is a variant of {@link StringLatin1#getChars(long, int, byte[])}, but for
- * UTF-16 coder.
- *
- * @param i value to convert
- * @param index next index, after the least significant digit
- * @param buf target buffer, UTF16-coded.
- * @return index of the most significant digit or minus sign, if present
- */
- static int getChars(long i, int index, byte[] buf) {
- // Used by trusted callers. Assumes all necessary bounds checks have been done by the caller.
- long q;
- int charPos = index;
-
- boolean negative = (i < 0);
- if (!negative) {
- i = -i;
- }
-
- // Get 2 digits/iteration using longs until quotient fits into an int
- while (i <= Integer.MIN_VALUE) {
- q = i / 100;
- charPos -= 2;
- putPair(buf, charPos, (int)((q * 100) - i));
- i = q;
- }
-
- // Get 2 digits/iteration using ints
- int q2;
- int i2 = (int)i;
- while (i2 <= -100) {
- q2 = i2 / 100;
- charPos -= 2;
- putPair(buf, charPos, (q2 * 100) - i2);
- i2 = q2;
- }
-
- // We know there are at most two digits left at this point.
- if (i2 < -9) {
- charPos -= 2;
- putPair(buf, charPos, -i2);
- } else {
- putChar(buf, --charPos, '0' - i2);
- }
-
- if (negative) {
- putChar(buf, --charPos, '-');
- }
- return charPos;
- }
-
- private static void putPair(byte[] buf, int charPos, int v) {
- int packed = (int) DecimalDigits.digitPair(v);
- putChar(buf, charPos, packed & 0xFF);
- putChar(buf, charPos + 1, packed >> 8);
- }
- // End of trusted methods.
-
public static void checkIndex(int off, byte[] val) {
String.checkIndex(off, length(val));
}
diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
index 87aca3e1ffd..d159bc8262a 100644
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -2179,14 +2179,6 @@ public byte stringCoder(String str) {
return str.coder();
}
- public int getCharsLatin1(long i, int index, byte[] buf) {
- return StringLatin1.getChars(i, index, buf);
- }
-
- public int getCharsUTF16(long i, int index, byte[] buf) {
- return StringUTF16.getChars(i, index, buf);
- }
-
public String join(String prefix, String suffix, String delimiter, String[] elements, int size) {
return String.join(prefix, suffix, delimiter, elements, size);
}
diff --git a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java b/src/java.base/share/classes/java/lang/classfile/AccessFlags.java
index b1d026f52e4..33db731efc8 100644
--- a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java
+++ b/src/java.base/share/classes/java/lang/classfile/AccessFlags.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -30,10 +30,33 @@
import jdk.internal.classfile.impl.AccessFlagsImpl;
/**
- * Models the access flags for a class, method, or field. Delivered as a
- * {@link ClassElement}, {@link FieldElement}, or {@link MethodElement}
- * when traversing the corresponding model type.
+ * Models the access flags for a class, method, or field. The access flags
+ * appears exactly once in each class, method, or field; a {@link
+ * ClassBuilder} and a {@link FieldBuilder} chooses an unspecified default value
+ * if access flags are not provided, and a {@link MethodBuilder} is always
+ * created with access flags.
+ *
+ * {@code AccessFlags} cannot be created via a factory method directly; it can
+ * be created with {@code withFlags} methods on the respective builders.
+ *
+ * A {@link MethodBuilder} throws an {@link IllegalArgumentException} if it is
+ * supplied an {@code AccessFlags} object that changes the preexisting
+ * {@link ClassFile#ACC_STATIC ACC_STATIC} flag of the builder, because the
+ * access flag change may invalidate previously supplied data to the builder.
*
+ * @apiNote
+ * The access flags of classes, methods, and fields are modeled as a standalone
+ * object to support streaming as elements for {@link ClassFileTransform}.
+ * Other access flags are not elements of a {@link CompoundElement} and thus not
+ * modeled by {@code AccessFlags}; they provide their own {@code flagsMask},
+ * {@code flags}, and {@code has} methods.
+ *
+ * @see ClassModel#flags()
+ * @see FieldModel#flags()
+ * @see MethodModel#flags()
+ * @see ClassBuilder#withFlags
+ * @see FieldBuilder#withFlags
+ * @see MethodBuilder#withFlags
* @since 24
*/
public sealed interface AccessFlags
@@ -41,26 +64,36 @@ public sealed interface AccessFlags
permits AccessFlagsImpl {
/**
- * {@return the access flags, as a bit mask}
+ * {@return the access flags, as a bit mask} It is in the range of unsigned
+ * short, {@code [0, 0xFFFF]}.
*/
int flagsMask();
/**
- * {@return the access flags}
+ * {@return the access flags, as a set of flag enums}
+ *
+ * @throws IllegalArgumentException if the flags mask has any undefined bit set
+ * @see #location()
*/
Set flags();
/**
- * {@return whether the specified flag is present} The specified flag
- * should be a valid flag for the classfile location associated with this
- * element otherwise false is returned.
+ * {@return whether the specified flag is set} If the specified flag
+ * is not available to this {@linkplain #location() location}, returns
+ * {@code false}.
+ *
* @param flag the flag to test
+ * @see #location()
*/
boolean has(AccessFlag flag);
/**
- * {@return the classfile location for this element, which is either class,
- * method, or field}
+ * {@return the {@code class} file location for this element, which is
+ * either class, method, or field}
+ *
+ * @see AccessFlag.Location#CLASS
+ * @see AccessFlag.Location#FIELD
+ * @see AccessFlag.Location#METHOD
*/
AccessFlag.Location location();
}
diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java
index 54a9ed80573..ae31fdf3967 100644
--- a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java
+++ b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java
@@ -54,7 +54,7 @@ public sealed interface AnnotationValue {
/**
* Models an annotation value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_ANNOTATION}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_ANNOTATION}.
*
* @since 24
*/
@@ -66,7 +66,7 @@ sealed interface OfAnnotation extends AnnotationValue
/**
* Models an array value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_ARRAY}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_ARRAY}.
*
* @since 24
*/
@@ -122,7 +122,7 @@ sealed interface OfConstant extends AnnotationValue {
/**
* Models a string value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_STRING}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_STRING}.
*
* @since 24
*/
@@ -149,7 +149,7 @@ default String resolvedValue() {
/**
* Models a double value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_DOUBLE}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_DOUBLE}.
*
* @since 24
*/
@@ -176,7 +176,7 @@ default Double resolvedValue() {
/**
* Models a float value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_FLOAT}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_FLOAT}.
*
* @since 24
*/
@@ -203,7 +203,7 @@ default Float resolvedValue() {
/**
* Models a long value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_LONG}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_LONG}.
*
* @since 24
*/
@@ -230,7 +230,7 @@ default Long resolvedValue() {
/**
* Models an int value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_INT}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_INT}.
*
* @since 24
*/
@@ -257,7 +257,7 @@ default Integer resolvedValue() {
/**
* Models a short value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_SHORT}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_SHORT}.
*
* @since 24
*/
@@ -287,7 +287,7 @@ default Short resolvedValue() {
/**
* Models a char value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_CHAR}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_CHAR}.
*
* @since 24
*/
@@ -317,7 +317,7 @@ default Character resolvedValue() {
/**
* Models a byte value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_BYTE}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_BYTE}.
*
* @since 24
*/
@@ -347,7 +347,7 @@ default Byte resolvedValue() {
/**
* Models a boolean value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_BOOLEAN}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_BOOLEAN}.
*
* @since 24
*/
@@ -377,7 +377,7 @@ default Boolean resolvedValue() {
/**
* Models a class value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_CLASS}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_CLASS}.
*
* @since 24
*/
@@ -394,7 +394,7 @@ default ClassDesc classSymbol() {
/**
* Models an enum value of an element-value pair.
- * The {@linkplain #tag tag} of this value is {@value TAG_ENUM}.
+ * The {@linkplain #tag tag} of this value is {@value %c TAG_ENUM}.
*
* @since 24
*/
diff --git a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java
index 0edb79a863d..1feef1ba35e 100644
--- a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java
+++ b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java
@@ -39,7 +39,14 @@
* A {@link ClassFileElement} describing a {@code class} file structure that has
* attributes, such as a {@code class} file, a field, a method, a {@link
* CodeAttribute Code} attribute, or a record component.
+ *
+ * Unless otherwise specified, most attributes that can be discovered in a
+ * {@link CompoundElement} implements the corresponding {@linkplain
+ * ClassFileElement##membership membership subinterface} of {@code
+ * ClassFileElement}, and can be sent to a {@link ClassFileBuilder} to be
+ * integrated into the built structure.
*
+ * @see java.lang.classfile.attribute
* @jvms 4.7 Attributes
* @sealedGraph
* @since 24
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java
index 996c63ddd01..6efd240d8af 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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,8 +25,8 @@
package java.lang.classfile;
-import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.constantpool.ClassEntry;
+import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.classfile.constantpool.Utf8Entry;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
@@ -41,14 +41,19 @@
import jdk.internal.classfile.impl.Util;
/**
- * A builder for classfiles. Builders are not created directly; they are passed
- * to handlers by methods such as {@link ClassFile#build(ClassDesc, Consumer)}
- * or to class transforms. The elements of a classfile can be specified
- * abstractly (by passing a {@link ClassElement} to {@link #with(ClassFileElement)})
- * or concretely by calling the various {@code withXxx} methods.
+ * A builder for a {@code class} file. {@link ClassFile} provides different
+ * {@code build} methods that accept handlers to configure such a builder;
+ * {@link ClassFile#build(ClassDesc, Consumer)} suffices for basic usage, while
+ * {@link ClassFile#build(ClassEntry, ConstantPoolBuilder, Consumer)} allows
+ * fine-grained control over {@linkplain ClassFileBuilder#constantPool() the
+ * constant pool}.
+ *
+ * Refer to {@link ClassFileBuilder} for general guidance and caution around
+ * the use of builders for structures in the {@code class} file format.
*
+ * @see ClassFile#build(ClassEntry, ConstantPoolBuilder, Consumer)
+ * @see ClassModel
* @see ClassTransform
- *
* @since 24
*/
public sealed interface ClassBuilder
@@ -56,28 +61,38 @@ public sealed interface ClassBuilder
permits ChainedClassBuilder, DirectClassBuilder {
/**
- * Sets the classfile version.
+ * Sets the version of this class.
+ *
* @param major the major version number
* @param minor the minor version number
* @return this builder
+ * @see ClassFileVersion
*/
default ClassBuilder withVersion(int major, int minor) {
return with(ClassFileVersion.of(major, minor));
}
/**
- * Sets the classfile access flags.
+ * Sets the access flags of this class.
+ *
* @param flags the access flags, as a bit mask
* @return this builder
+ * @see AccessFlags
+ * @see AccessFlag.Location#CLASS
*/
default ClassBuilder withFlags(int flags) {
return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags));
}
/**
- * Sets the classfile access flags.
- * @param flags the access flags
+ * Sets the access flags of this class.
+ *
+ * @param flags the access flags, as flag enums
* @return this builder
+ * @throws IllegalArgumentException if any flag cannot be applied to the
+ * {@link AccessFlag.Location#CLASS} location
+ * @see AccessFlags
+ * @see AccessFlag.Location#CLASS
*/
default ClassBuilder withFlags(AccessFlag... flags) {
return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags));
@@ -85,8 +100,10 @@ default ClassBuilder withFlags(AccessFlag... flags) {
/**
* Sets the superclass of this class.
+ *
* @param superclassEntry the superclass
* @return this builder
+ * @see Superclass
*/
default ClassBuilder withSuperclass(ClassEntry superclassEntry) {
return with(Superclass.of(superclassEntry));
@@ -94,9 +111,11 @@ default ClassBuilder withSuperclass(ClassEntry superclassEntry) {
/**
* Sets the superclass of this class.
+ *
* @param desc the superclass
* @return this builder
* @throws IllegalArgumentException if {@code desc} represents a primitive type
+ * @see Superclass
*/
default ClassBuilder withSuperclass(ClassDesc desc) {
return withSuperclass(constantPool().classEntry(desc));
@@ -104,8 +123,10 @@ default ClassBuilder withSuperclass(ClassDesc desc) {
/**
* Sets the interfaces of this class.
+ *
* @param interfaces the interfaces
* @return this builder
+ * @see Interfaces
*/
default ClassBuilder withInterfaces(List interfaces) {
return with(Interfaces.of(interfaces));
@@ -113,8 +134,10 @@ default ClassBuilder withInterfaces(List interfaces) {
/**
* Sets the interfaces of this class.
+ *
* @param interfaces the interfaces
* @return this builder
+ * @see Interfaces
*/
default ClassBuilder withInterfaces(ClassEntry... interfaces) {
return withInterfaces(List.of(interfaces));
@@ -122,8 +145,11 @@ default ClassBuilder withInterfaces(ClassEntry... interfaces) {
/**
* Sets the interfaces of this class.
+ *
* @param interfaces the interfaces
* @return this builder
+ * @throws IllegalArgumentException if any element of {@code interfaces} is primitive
+ * @see Interfaces
*/
default ClassBuilder withInterfaceSymbols(List interfaces) {
return withInterfaces(Util.entryList(interfaces));
@@ -131,32 +157,39 @@ default ClassBuilder withInterfaceSymbols(List interfaces) {
/**
* Sets the interfaces of this class.
+ *
* @param interfaces the interfaces
* @return this builder
+ * @throws IllegalArgumentException if any element of {@code interfaces} is primitive
+ * @see Interfaces
*/
default ClassBuilder withInterfaceSymbols(ClassDesc... interfaces) {
- // List view, since ref to interfaces is temporary
+ // list version does defensive copy
return withInterfaceSymbols(Arrays.asList(interfaces));
}
/**
* Adds a field.
- * @param name the name of the field
- * @param descriptor the field descriptor
- * @param handler handler which receives a {@link FieldBuilder} which can
- * further define the contents of the field
+ *
+ * @param name the field name
+ * @param descriptor the field descriptor string
+ * @param handler handler to supply the contents of the field
* @return this builder
+ * @see FieldModel
*/
ClassBuilder withField(Utf8Entry name,
Utf8Entry descriptor,
Consumer super FieldBuilder> handler);
/**
- * Adds a field.
- * @param name the name of the field
- * @param descriptor the field descriptor
- * @param flags the access flags for this field
+ * Adds a field, with only access flags.
+ *
+ * @param name the field name
+ * @param descriptor the field descriptor string
+ * @param flags the access flags for this field, as a bit mask
* @return this builder
+ * @see FieldModel
+ * @see FieldBuilder#withFlags(int)
*/
default ClassBuilder withField(Utf8Entry name,
Utf8Entry descriptor,
@@ -166,11 +199,12 @@ default ClassBuilder withField(Utf8Entry name,
/**
* Adds a field.
- * @param name the name of the field
- * @param descriptor the field descriptor
- * @param handler handler which receives a {@link FieldBuilder} which can
- * further define the contents of the field
+ *
+ * @param name the field name
+ * @param descriptor the symbolic field descriptor
+ * @param handler handler to supply the contents of the field
* @return this builder
+ * @see FieldModel
*/
default ClassBuilder withField(String name,
ClassDesc descriptor,
@@ -181,11 +215,14 @@ default ClassBuilder withField(String name,
}
/**
- * Adds a field.
- * @param name the name of the field
- * @param descriptor the field descriptor
- * @param flags the access flags for this field
+ * Adds a field, with only access flags.
+ *
+ * @param name the field name
+ * @param descriptor the symbolic field descriptor
+ * @param flags the access flags for this field, as a bit mask
* @return this builder
+ * @see FieldModel
+ * @see FieldBuilder#withFlags(int)
*/
default ClassBuilder withField(String name,
ClassDesc descriptor,
@@ -197,28 +234,33 @@ default ClassBuilder withField(String name,
/**
* Adds a field by transforming a field from another class.
- *
- * @implNote
- * This method behaves as if:
+ *
+ * This method behaves as if:
* {@snippet lang=java :
- * withField(field.fieldName(), field.fieldType(),
- * b -> b.transformField(field, transform));
+ * // @link substring=withField target="#withField(Utf8Entry, Utf8Entry, Consumer)" :
+ * withField(field.fieldName(), field.fieldType(),
+ * fb -> fb.transform(field, transform)) // @link regex="transform(?=\()" target="FieldBuilder#transform"
* }
*
* @param field the field to be transformed
* @param transform the transform to apply to the field
* @return this builder
+ * @see FieldTransform
*/
ClassBuilder transformField(FieldModel field, FieldTransform transform);
/**
- * Adds a method.
- * @param name the name of the method
+ * Adds a method. The bit for {@link ClassFile#ACC_STATIC ACC_STATIC} flag
+ * cannot be modified by the {@code handler} later, and must be set through
+ * {@code methodFlags}.
+ *
+ * @param name the method name
* @param descriptor the method descriptor
- * @param methodFlags the access flags
- * @param handler handler which receives a {@link MethodBuilder} which can
- * further define the contents of the method
+ * @param methodFlags the access flags as a bit mask, with the {@code
+ * ACC_STATIC} bit definitely set
+ * @param handler handler to supply the contents of the method
* @return this builder
+ * @see MethodModel
*/
ClassBuilder withMethod(Utf8Entry name,
Utf8Entry descriptor,
@@ -226,14 +268,23 @@ ClassBuilder withMethod(Utf8Entry name,
Consumer super MethodBuilder> handler);
/**
- * Adds a method, with only a {@code Code} attribute.
+ * Adds a method, with only access flags and a {@link CodeModel}. The bit
+ * for {@link ClassFile#ACC_STATIC ACC_STATIC} flag cannot be modified by
+ * the {@code handler} later, and must be set through {@code methodFlags}.
+ *
+ * This method behaves as if:
+ * {@snippet lang=java :
+ * // @link substring=withMethod target="#withMethod(Utf8Entry, Utf8Entry, int, Consumer)" :
+ * withMethod(name, descriptor, methodFlags, mb -> mb.withCode(handler)) // @link substring=withCode target="MethodBuilder#withCode"
+ * }
*
- * @param name the name of the method
+ * @param name the method name
* @param descriptor the method descriptor
- * @param methodFlags the access flags
- * @param handler handler which receives a {@link CodeBuilder} which can
- * define the contents of the method body
+ * @param methodFlags the access flags as a bit mask, with the {@code
+ * ACC_STATIC} bit definitely set
+ * @param handler handler to supply the contents of the method body
* @return this builder
+ * @see MethodModel
*/
default ClassBuilder withMethodBody(Utf8Entry name,
Utf8Entry descriptor,
@@ -243,13 +294,17 @@ default ClassBuilder withMethodBody(Utf8Entry name,
}
/**
- * Adds a method.
- * @param name the name of the method
+ * Adds a method. The bit for {@link ClassFile#ACC_STATIC ACC_STATIC} flag
+ * cannot be modified by the {@code handler}, and must be set through
+ * {@code methodFlags}.
+ *
+ * @param name the method name
* @param descriptor the method descriptor
- * @param methodFlags the access flags
- * @param handler handler which receives a {@link MethodBuilder} which can
- * further define the contents of the method
+ * @param methodFlags the access flags as a bit mask, with the {@code
+ * ACC_STATIC} bit definitely set
+ * @param handler handler to supply the contents of the method
* @return this builder
+ * @see MethodModel
*/
default ClassBuilder withMethod(String name,
MethodTypeDesc descriptor,
@@ -262,13 +317,23 @@ default ClassBuilder withMethod(String name,
}
/**
- * Adds a method, with only a {@link CodeAttribute}.
- * @param name the name of the method
+ * Adds a method, with only access flags and a {@link CodeModel}. The bit
+ * for {@link ClassFile#ACC_STATIC ACC_STATIC} flag cannot be modified by
+ * the {@code handler}, and must be set through {@code methodFlags}.
+ *
+ * This method behaves as if:
+ * {@snippet lang=java :
+ * // @link substring=withMethod target="#withMethod(String, MethodTypeDesc, int, Consumer)" :
+ * withMethod(name, descriptor, methodFlags, mb -> mb.withCode(handler)) // @link substring=withCode target="MethodBuilder#withCode"
+ * }
+ *
+ * @param name the method name
* @param descriptor the method descriptor
- * @param methodFlags the access flags
- * @param handler handler which receives a {@link CodeBuilder} which can
- * define the contents of the method body
+ * @param methodFlags the access flags as a bit mask, with the {@code
+ * ACC_STATIC} bit definitely set
+ * @param handler handler to supply the contents of the method body
* @return this builder
+ * @see MethodModel
*/
default ClassBuilder withMethodBody(String name,
MethodTypeDesc descriptor,
@@ -278,17 +343,21 @@ default ClassBuilder withMethodBody(String name,
}
/**
- * Adds a method by transforming a method from another class.
- *
- * @implNote
- *
This method behaves as if:
+ * Adds a method by transforming a method from another class. The transform
+ * cannot modify the {@link ClassFile#ACC_STATIC ACC_STATIC} flag of the
+ * original method.
+ *
+ * This method behaves as if:
* {@snippet lang=java :
- * withMethod(method.methodName(), method.methodType(),
- * b -> b.transformMethod(method, transform));
+ * // @link substring=withMethod target="#withMethod(Utf8Entry, Utf8Entry, int, Consumer)" :
+ * withMethod(method.methodName(), method.methodType(), method.flags().flagMask(),
+ * mb -> mb.transform(method, transform)) // @link regex="transform(?=\()" target="MethodBuilder#transform"
* }
+ *
* @param method the method to be transformed
* @param transform the transform to apply to the method
* @return this builder
+ * @see MethodTransform
*/
ClassBuilder transformMethod(MethodModel method, MethodTransform transform);
}
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassElement.java b/src/java.base/share/classes/java/lang/classfile/ClassElement.java
index c39ab3c4a64..866d1bafcc4 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassElement.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassElement.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -27,9 +27,21 @@
import java.lang.classfile.attribute.*;
/**
- * A marker interface for elements that can appear when traversing
- * a {@link ClassModel} or be presented to a {@link ClassBuilder}.
+ * Marker interface for a member element of a {@link ClassModel}. Such an
+ * element can appear when traversing a {@link ClassModel} unless otherwise
+ * specified, be supplied to a {@link ClassBuilder}, and be processed by a
+ * {@link ClassTransform}.
+ *
+ * {@link AccessFlags}, and {@link ClassFileVersion} are member elements of a
+ * class that appear exactly once during the traversal of a {@link ClassModel}.
+ * {@link Superclass} and {@link Interfaces} may be absent or appear at most
+ * once. A {@link ClassBuilder} may provide an alternative superclass if it is
+ * not defined but required.
*
+ * @see ClassFileElement##membership Membership Elements
+ * @see MethodElement
+ * @see FieldElement
+ * @see CodeElement
* @sealedGraph
* @since 24
*/
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFile.java b/src/java.base/share/classes/java/lang/classfile/ClassFile.java
index 43fb1927b38..4b529c39099 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassFile.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassFile.java
@@ -25,18 +25,27 @@
package java.lang.classfile;
import java.io.IOException;
+import java.lang.classfile.AttributeMapper.AttributeStability;
import java.lang.classfile.attribute.CharacterRangeInfo;
+import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.attribute.LocalVariableInfo;
import java.lang.classfile.attribute.LocalVariableTypeInfo;
import java.lang.classfile.attribute.ModuleAttribute;
+import java.lang.classfile.attribute.StackMapTableAttribute;
+import java.lang.classfile.attribute.UnknownAttribute;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.classfile.constantpool.Utf8Entry;
import java.lang.classfile.instruction.BranchInstruction;
+import java.lang.classfile.instruction.CharacterRange;
import java.lang.classfile.instruction.DiscontinuedInstruction;
import java.lang.classfile.instruction.ExceptionCatch;
+import java.lang.classfile.instruction.LineNumber;
+import java.lang.classfile.instruction.LocalVariable;
+import java.lang.classfile.instruction.LocalVariableType;
import java.lang.constant.ClassDesc;
import java.lang.reflect.AccessFlag;
+import java.lang.reflect.ClassFileFormatVersion;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@@ -50,9 +59,9 @@
import static jdk.internal.constant.ConstantUtils.CD_module_info;
/**
- * Represents a context for parsing, transforming, and generating classfiles.
- * A {@code ClassFile} has a set of options that condition how parsing and
- * generation is done.
+ * Provides ability to parse, transform, and generate {@code class} files.
+ * A {@code ClassFile} is a context with a set of options that condition how
+ * parsing and generation are done.
*
* @since 24
*/
@@ -60,14 +69,20 @@ public sealed interface ClassFile
permits ClassFileImpl {
/**
- * {@return a context with default options}
+ * {@return a context with default options} Each subtype of {@link Option}
+ * specifies its default.
+ *
+ * The default {@link AttributeMapperOption} and {@link
+ * ClassHierarchyResolverOption} may be unsuitable for some {@code class}
+ * files and result in parsing or generation errors.
*/
static ClassFile of() {
return ClassFileImpl.DEFAULT_CONTEXT;
}
/**
- * {@return a new context with options altered from the default}
+ * {@return a context with options altered from the default} Equivalent to
+ * {@link #of() ClassFile.of().withOptions(options)}.
* @param options the desired processing options
*/
static ClassFile of(Option... options) {
@@ -75,14 +90,15 @@ static ClassFile of(Option... options) {
}
/**
- * {@return a copy of the context with altered options}
+ * {@return a context with altered options from this context}
* @param options the desired processing options
*/
ClassFile withOptions(Option... options);
/**
- * An option that affects the parsing and writing of classfiles.
+ * An option that affects the parsing or writing of {@code class} files.
*
+ * @see java.lang.classfile##options Options
* @sealedGraph
* @since 24
*/
@@ -90,16 +106,32 @@ sealed interface Option {
}
/**
- * Option describing attribute mappers for custom attributes.
- * Default is only to process standard attributes.
+ * The option describing user-defined attributes for parsing {@code class}
+ * files. The default does not recognize any user-defined attribute.
+ *
+ * An {@code AttributeMapperOption} contains a function that maps an
+ * attribute name to a user attribute mapper. The function may return {@code
+ * null} if it does not recognize an attribute name. The returned mapper
+ * must ensure its {@link AttributeMapper#name() name()} is equivalent to
+ * the {@link Utf8Entry#stringValue() stringValue()} of the input {@link
+ * Utf8Entry}.
+ *
+ * The mapping function in this attribute has lower priority than mappers in
+ * {@link Attributes}, so it is impossible to override built-in attributes
+ * with this option. If an attribute is not recognized by any mapper in
+ * {@link Attributes} and is not assigned a mapper, or recognized, by this
+ * option, that attribute will be modeled by an {@link UnknownAttribute}.
*
+ * @see AttributeMapper
+ * @see CustomAttribute
* @since 24
*/
sealed interface AttributeMapperOption extends Option
permits ClassFileImpl.AttributeMapperOptionImpl {
/**
- * {@return an option describing attribute mappers for custom attributes}
+ * {@return an option describing user-defined attributes for parsing}
+ *
* @param attributeMapper a function mapping attribute names to attribute mappers
*/
static AttributeMapperOption of(Function> attributeMapper) {
@@ -114,17 +146,31 @@ static AttributeMapperOption of(Function> attribut
}
/**
- * Option describing the class hierarchy resolver to use when generating
- * stack maps.
+ * The option describing the class hierarchy resolver to use when generating
+ * stack maps or verifying classes. The default is {@link
+ * ClassHierarchyResolver#defaultResolver()}, which uses core reflection to
+ * find a class with a given name in {@linkplain ClassLoader#getSystemClassLoader()
+ * system class loader} and inspect it, and is insufficient if a class is
+ * not present in the system class loader as in applications, or if loading
+ * of system classes is not desired as in agents.
+ *
+ * A {@code ClassHierarchyResolverOption} contains a {@link ClassHierarchyResolver}.
+ * The resolver must be able to process all classes and interfaces, including
+ * those appearing as the component types of array types, that appear in the
+ * operand stack of the generated bytecode. If the resolver fails on any
+ * of the classes and interfaces with an {@link IllegalArgumentException},
+ * the {@code class} file generation fails.
*
+ * @see ClassHierarchyResolver
+ * @jvms 4.10.1.2 Verification Type System
* @since 24
*/
sealed interface ClassHierarchyResolverOption extends Option
permits ClassFileImpl.ClassHierarchyResolverOptionImpl {
/**
- * {@return an option describing the class hierarchy resolver to use when
- * generating stack maps}
+ * {@return an option describing the class hierarchy resolver to use}
+ *
* @param classHierarchyResolver the resolver
*/
static ClassHierarchyResolverOption of(ClassHierarchyResolver classHierarchyResolver) {
@@ -139,14 +185,22 @@ static ClassHierarchyResolverOption of(ClassHierarchyResolver classHierarchyReso
}
/**
- * Option describing whether to preserve the original constant pool when
- * transforming a {@code class} file. Reusing the constant pool enables
- * significant optimizations in processing time and minimizes differences
- * between the original and transformed {@code class} files, but may result
- * in a bigger transformed {@code class} file when many elements of the
- * original {@code class} file are dropped and many original constant
- * pool entries become unused. Default is {@link #SHARED_POOL} to preserve
- * the original constant pool.
+ * Option describing whether to extend from the original constant pool when
+ * transforming a {@code class} file. The default is {@link #SHARED_POOL}
+ * to extend from the original constant pool.
+ *
+ * This option affects all overloads of {@link #transformClass transformClass}.
+ * Extending from the original constant pool keeps the indices into the
+ * constant pool intact, which enables significant optimizations in processing
+ * time and minimizes differences between the original and transformed {@code
+ * class} files, but may result in a bigger transformed {@code class} file
+ * when many elements of the original {@code class} file are dropped and
+ * many original constant pool entries become unused.
+ *
+ * An alternative to this option is to use {@link #build(ClassEntry,
+ * ConstantPoolBuilder, Consumer)} directly. It allows extension from
+ * arbitrary constant pools, and may be useful if a built {@code class} file
+ * reuses structures from multiple original {@code class} files.
*
* @see ConstantPoolBuilder
* @see #build(ClassEntry, ConstantPoolBuilder, Consumer)
@@ -156,8 +210,8 @@ static ClassHierarchyResolverOption of(ClassHierarchyResolver classHierarchyReso
enum ConstantPoolSharingOption implements Option {
/**
- * Preserves the original constant pool when transforming the {@code
- * class} file.
+ * Extend the new constant pool from the original constant pool when
+ * transforming the {@code class} file.
*
* These two transformations below are equivalent:
* {@snippet lang=java :
@@ -194,87 +248,143 @@ enum ConstantPoolSharingOption implements Option {
}
/**
- * Option describing whether to patch out unreachable code.
- * Default is {@code PATCH_DEAD_CODE} to automatically patch out unreachable
- * code with NOPs.
+ * The option describing whether to patch out unreachable code for stack map
+ * generation. The default is {@link #PATCH_DEAD_CODE} to automatically
+ * patch unreachable code and generate a valid stack map entry for the
+ * patched code.
+ *
+ * The stack map generation process may fail when it encounters unreachable
+ * code and {@link #KEEP_DEAD_CODE} is set. In such cases, users should
+ * set {@link StackMapsOption#DROP_STACK_MAPS} and provide their own stack
+ * maps that passes verification (JVMS {@jvms 4.10.1}).
*
+ * @see StackMapsOption
+ * @jvms 4.10.1 Verification by Type Checking
* @since 24
*/
enum DeadCodeOption implements Option {
- /** Patch unreachable code */
+ /**
+ * Patch unreachable code with dummy code, and generate valid dummy
+ * stack map entries. This ensures the generated code can pass
+ * verification (JVMS {@jvms 4.10.1}).
+ */
PATCH_DEAD_CODE,
- /** Keep the unreachable code */
+ /**
+ * Keep the unreachable code for the accuracy of the generated {@code
+ * class} file. Users should set {@link StackMapsOption#DROP_STACK_MAPS}
+ * to prevent stack map generation from running and provide their own
+ * {@link StackMapTableAttribute} to a {@link CodeBuilder}.
+ */
KEEP_DEAD_CODE
}
/**
- * Option describing whether to filter unresolved labels.
- * Default is {@code FAIL_ON_DEAD_LABELS} to throw IllegalArgumentException
- * when any {@link ExceptionCatch}, {@link LocalVariableInfo},
- * {@link LocalVariableTypeInfo}, or {@link CharacterRangeInfo}
- * reference to unresolved {@link Label} during bytecode serialization.
- * Setting this option to {@code DROP_DEAD_LABELS} filters the above
- * elements instead.
+ * The option describing whether to filter {@linkplain
+ * CodeBuilder#labelBinding(Label) unbound labels} and drop their
+ * enclosing structures if possible. The default is {@link
+ * #FAIL_ON_DEAD_LABELS} to fail fast with an {@link IllegalArgumentException}
+ * when a {@link PseudoInstruction} refers to an unbound label during
+ * bytecode generation.
+ *
+ * The affected {@link PseudoInstruction}s include {@link ExceptionCatch},
+ * {@link LocalVariable}, {@link LocalVariableType}, and {@link
+ * CharacterRange}. Setting this option to {@link #DROP_DEAD_LABELS}
+ * filters these pseudo-instructions from a {@link CodeBuilder} instead.
+ * Note that instructions, such as {@link BranchInstruction}, with unbound
+ * labels always fail-fast with an {@link IllegalArgumentException}.
*
+ * @see DebugElementsOption
* @since 24
*/
enum DeadLabelsOption implements Option {
- /** Fail on unresolved labels */
+ /**
+ * Fail fast on {@linkplain CodeBuilder#labelBinding(Label) unbound
+ * labels}. This also ensures the accuracy of the generated {@code
+ * class} files.
+ */
FAIL_ON_DEAD_LABELS,
- /** Filter unresolved labels */
+ /**
+ * Filter {@link PseudoInstruction}s with {@linkplain
+ * CodeBuilder#labelBinding(Label) unbound labels}. Note that
+ * instructions with unbound labels still cause an {@link
+ * IllegalArgumentException}.
+ */
DROP_DEAD_LABELS
}
/**
- * Option describing whether to process or discard debug elements.
- * Debug elements include the local variable table, local variable type
- * table, and character range table. Discarding debug elements may
- * reduce the overhead of parsing or transforming classfiles.
- * Default is {@code PASS_DEBUG} to process debug elements.
+ * The option describing whether to process or discard debug {@link
+ * PseudoInstruction}s in the traversal of a {@link CodeModel} or a {@link
+ * CodeBuilder}. The default is {@link #PASS_DEBUG} to process debug
+ * pseudo-instructions as all other {@link CodeElement}.
+ *
+ * Debug pseudo-instructions include {@link LocalVariable}, {@link
+ * LocalVariableType}, and {@link CharacterRange}. Discarding debug
+ * elements may reduce the overhead of parsing or transforming {@code class}
+ * files and has no impact on the run-time behavior.
*
+ * @see LineNumbersOption
* @since 24
*/
enum DebugElementsOption implements Option {
- /** Process debug elements */
+ /**
+ * Process debug pseudo-instructions like other member elements of a
+ * {@link CodeModel}.
+ */
PASS_DEBUG,
- /** Drop debug elements */
+ /**
+ * Drop debug pseudo-instructions from traversal and builders.
+ */
DROP_DEBUG
}
/**
- * Option describing whether to process or discard line numbers.
+ * The option describing whether to process or discard {@link LineNumber}s
+ * in the traversal of a {@link CodeModel} or a {@link CodeBuilder}. The
+ * default is {@link #PASS_LINE_NUMBERS} to process all line number entries
+ * as all other {@link CodeElement}.
+ *
* Discarding line numbers may reduce the overhead of parsing or transforming
- * classfiles.
- * Default is {@code PASS_LINE_NUMBERS} to process line numbers.
+ * {@code class} files and has no impact on the run-time behavior.
*
+ * @see DebugElementsOption
* @since 24
*/
enum LineNumbersOption implements Option {
- /** Process line numbers */
+ /**
+ * Process {@link LineNumber} like other member elements of a {@link
+ * CodeModel}.
+ */
PASS_LINE_NUMBERS,
- /** Drop line numbers */
+ /**
+ * Drop {@link LineNumber} from traversal and builders.
+ */
DROP_LINE_NUMBERS;
}
/**
- * Option describing whether to automatically rewrite short jumps to
- * long when necessary.
- * Default is {@link #FIX_SHORT_JUMPS} to automatically rewrite jump
- * instructions.
+ * The option describing whether to automatically rewrite short jumps to
+ * equivalent instructions when necessary. The default is {@link
+ * #FIX_SHORT_JUMPS} to automatically rewrite.
*
* Due to physical restrictions, some types of instructions cannot encode
* certain jump targets with bci offsets less than -32768 or greater than
* 32767, as they use a {@code s2} to encode such an offset. (The maximum
* length of the {@code code} array is 65535.) These types of instructions
* are called "short jumps".
+ *
+ * Disabling rewrite can ensure the physical accuracy of a generated {@code
+ * class} file and avoid the overhead from a failed first attempt for
+ * overflowing forward jumps in some cases, if the generated {@code class}
+ * file is stable.
*
* @see BranchInstruction
* @see DiscontinuedInstruction.JsrInstruction
@@ -294,80 +404,153 @@ enum ShortJumpsOption implements Option {
* Fail with an {@link IllegalArgumentException} if short jump overflows.
*
* This is useful to ensure the physical accuracy of a generated {@code
- * class} file.
+ * class} file and avoids the overhead from a failed first attempt for
+ * overflowing forward jumps in some cases.
*/
FAIL_ON_SHORT_JUMPS
}
/**
- * Option describing whether to generate stackmaps.
- * Default is {@code STACK_MAPS_WHEN_REQUIRED} to generate stack
- * maps for {@link #JAVA_6_VERSION} or above, where specifically for
- * {@link #JAVA_6_VERSION} the stack maps may not be generated.
- * @jvms 4.10.1 Verification by Type Checking
+ * The option describing whether to generate stack maps. The default is
+ * {@link #STACK_MAPS_WHEN_REQUIRED} to generate stack maps or reuse
+ * existing ones if compatible.
+ *
+ * The {@link StackMapTableAttribute} is a derived property from a {@link
+ * CodeAttribute Code} attribute to allow a Java Virtual Machine to perform
+ * verification in one pass. Thus, it is not modeled as part of a {@link
+ * CodeModel}, but computed on-demand instead via stack maps generation.
+ *
+ * Stack map generation may fail with an {@link IllegalArgumentException} if
+ * there is {@linkplain DeadCodeOption unreachable code} or legacy
+ * {@linkplain DiscontinuedInstruction.JsrInstruction jump routine}
+ * instructions. When {@link #DROP_STACK_MAPS} option is used, users can
+ * provide their own stack maps by supplying a {@link StackMapTableAttribute}
+ * to a {@link CodeBuilder}.
*
+ * @see StackMapTableAttribute
+ * @see DeadCodeOption
+ * @jvms 4.10.1 Verification by Type Checking
* @since 24
*/
enum StackMapsOption implements Option {
- /** Generate stack maps when required */
+ /**
+ * Generate stack maps or reuse existing ones if compatible. Stack maps
+ * are present on major versions {@value #JAVA_6_VERSION} or above. For
+ * these versions, {@link CodeBuilder} tries to reuse compatible stack
+ * maps information if the code array and exception handlers are still
+ * compatible after a transformation; otherwise, it runs stack map
+ * generation. However, it does not fail fast if the major version is
+ * {@value #JAVA_6_VERSION}, which allows jump subroutine instructions
+ * that are incompatible with stack maps to exist in the {@code code}
+ * array.
+ */
STACK_MAPS_WHEN_REQUIRED,
- /** Always generate stack maps */
+ /**
+ * Forces running stack map generation. This runs stack map generation
+ * unconditionally and fails fast if the generation fails due to any
+ * reason.
+ */
GENERATE_STACK_MAPS,
- /** Drop stack maps from code */
+ /**
+ * Do not run stack map generation. Users must supply their own
+ * {@link StackMapTableAttribute} to a {@link CodeBuilder} if the code
+ * has branches or exception handlers; otherwise, the generated code
+ * will fail verification (JVMS {@jvms 4.10.1}).
+ *
+ * This option is required for user-supplied {@link StackMapTableAttribute}
+ * to be respected. Stack maps on an existing {@link CodeAttribute Code}
+ * attribute can be reused as below with this option:
+ * {@snippet lang=java file="PackageSnippets.java" region="manual-reuse-stack-maps"}
+ */
DROP_STACK_MAPS
}
/**
- * Option describing whether to process or discard unrecognized or problematic
- * original attributes when a class, record component, field, method or code is
- * transformed in its exploded form.
- * Default is {@code PASS_ALL_ATTRIBUTES} to process all original attributes.
- * @see AttributeMapper.AttributeStability
+ * The option describing whether to retain or discard attributes that cannot
+ * verify their correctness after a transformation. The default is {@link
+ * #PASS_ALL_ATTRIBUTES} to retain all attributes as-is.
+ *
+ * Many attributes only depend on data managed by the Class-File API, such
+ * as constant pool entries or labels into the {@code code} array. If they
+ * change, the Class-File API knows their updated values and can write a
+ * correct version by expanding the structures and recomputing the updated
+ * indexes, known as "explosion". However, some attributes, such as type
+ * annotations, depend on arbitrary data that may be modified during
+ * transformations but the Class-File API does not track, such as index to
+ * an entry in the {@linkplain ClassModel#interfaces() interfaces} of a
+ * {@code ClassFile} structure. As a result, the Class-File API cannot
+ * verify the correctness of such information.
*
+ * @see AttributeStability
* @since 24
*/
enum AttributesProcessingOption implements Option {
- /** Process all original attributes during transformation */
+ /**
+ * Retain all original attributes during transformation.
+ */
PASS_ALL_ATTRIBUTES,
- /** Drop unknown attributes during transformation */
+ /**
+ * Drop attributes with {@link AttributeStability#UNKNOWN} data
+ * dependency during transformation.
+ */
DROP_UNKNOWN_ATTRIBUTES,
- /** Drop unknown and unstable original attributes during transformation */
+ /**
+ * Drop attributes with {@link AttributeStability#UNSTABLE} or higher
+ * data dependency during transformation.
+ */
DROP_UNSTABLE_ATTRIBUTES
}
/**
- * Parse a classfile into a {@link ClassModel}.
- * @param bytes the bytes of the classfile
+ * Parses a {@code class} file into a {@link ClassModel}.
+ *
+ * Due to the on-demand nature of {@code class} file parsing, an {@link
+ * IllegalArgumentException} may be thrown on any accessor method invocation
+ * on the returned model or any structure returned by the accessors in the
+ * structure hierarchy.
+ *
+ * @param bytes the bytes of the {@code class} file
* @return the class model
- * @throws IllegalArgumentException or its subclass if the classfile format is
- * not supported or an incompatibility prevents parsing of the classfile
+ * @throws IllegalArgumentException if the {@code class} file is malformed
+ * or of a version {@linkplain #latestMajorVersion() not supported}
+ * by the current runtime
*/
ClassModel parse(byte[] bytes);
/**
- * Parse a classfile into a {@link ClassModel}.
- * @param path the path to the classfile
+ * Parses a {@code class} into a {@link ClassModel}.
+ *
+ * Due to the on-demand nature of {@code class} file parsing, an {@link
+ * IllegalArgumentException} may be thrown on any accessor method invocation
+ * on the returned model or any structure returned by the accessors in the
+ * structure hierarchy.
+ *
+ * @param path the path to the {@code class} file
* @return the class model
- * @throws java.io.IOException if an I/O error occurs
- * @throws IllegalArgumentException or its subclass if the classfile format is
- * not supported or an incompatibility prevents parsing of the classfile
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if the {@code class} file is malformed
+ * or of a version {@linkplain #latestMajorVersion() not supported}
+ * by the current runtime
+ * @see #parse(byte[])
*/
default ClassModel parse(Path path) throws IOException {
return parse(Files.readAllBytes(path));
}
/**
- * Build a classfile into a byte array.
+ * Builds a {@code class} file into a byte array.
+ *
* @param thisClass the name of the class to build
* @param handler a handler that receives a {@link ClassBuilder}
- * @return the classfile bytes
- * @throws IllegalArgumentException if {@code thisClass} represents a primitive type
+ * @return the {@code class} file bytes
+ * @throws IllegalArgumentException if {@code thisClass} represents a
+ * primitive type or building encounters a failure
*/
default byte[] build(ClassDesc thisClass,
Consumer super ClassBuilder> handler) {
@@ -376,24 +559,27 @@ default byte[] build(ClassDesc thisClass,
}
/**
- * Build a classfile into a byte array using the provided constant pool
- * builder.
+ * Builds a {@code class} file into a byte array using the provided constant
+ * pool builder.
*
* @param thisClassEntry the name of the class to build
* @param constantPool the constant pool builder
* @param handler a handler that receives a {@link ClassBuilder}
- * @return the classfile bytes
+ * @return the {@code class} file bytes
+ * @throws IllegalArgumentException if building encounters a failure
*/
byte[] build(ClassEntry thisClassEntry,
ConstantPoolBuilder constantPool,
Consumer super ClassBuilder> handler);
/**
- * Build a classfile into a file.
+ * Builds a {@code class} file into a file in a file system.
+ *
* @param path the path to the file to write
* @param thisClass the name of the class to build
* @param handler a handler that receives a {@link ClassBuilder}
- * @throws java.io.IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if building encounters a failure
*/
default void buildTo(Path path,
ClassDesc thisClass,
@@ -402,14 +588,15 @@ default void buildTo(Path path,
}
/**
- * Build a classfile into a file using the provided constant pool
- * builder.
+ * Builds a {@code class} file into a file in a file system using the
+ * provided constant pool builder.
*
* @param path the path to the file to write
* @param thisClassEntry the name of the class to build
* @param constantPool the constant pool builder
* @param handler a handler that receives a {@link ClassBuilder}
- * @throws java.io.IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if building encounters a failure
*/
default void buildTo(Path path,
ClassEntry thisClassEntry,
@@ -419,22 +606,26 @@ default void buildTo(Path path,
}
/**
- * Build a module descriptor into a byte array.
+ * Builds a module descriptor into a byte array.
+ *
* @param moduleAttribute the {@code Module} attribute
- * @return the classfile bytes
+ * @return the {@code class} file bytes
+ * @throws IllegalArgumentException if building encounters a failure
*/
default byte[] buildModule(ModuleAttribute moduleAttribute) {
return buildModule(moduleAttribute, clb -> {});
}
/**
- * Build a module descriptor into a byte array.
+ * Builds a module descriptor into a byte array.
+ *
* @param moduleAttribute the {@code Module} attribute
* @param handler a handler that receives a {@link ClassBuilder}
- * @return the classfile bytes
+ * @return the {@code class} file bytes
+ * @throws IllegalArgumentException if building encounters a failure
*/
default byte[] buildModule(ModuleAttribute moduleAttribute,
- Consumer super ClassBuilder> handler) {
+ Consumer super ClassBuilder> handler) {
return build(CD_module_info, clb -> {
clb.withFlags(AccessFlag.MODULE);
clb.with(moduleAttribute);
@@ -443,10 +634,12 @@ default byte[] buildModule(ModuleAttribute moduleAttribute,
}
/**
- * Build a module descriptor into a file.
+ * Builds a module descriptor into a file in a file system.
+ *
* @param path the file to write
* @param moduleAttribute the {@code Module} attribute
- * @throws java.io.IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if building encounters a failure
*/
default void buildModuleTo(Path path,
ModuleAttribute moduleAttribute) throws IOException {
@@ -454,11 +647,13 @@ default void buildModuleTo(Path path,
}
/**
- * Build a module descriptor into a file.
+ * Builds a module descriptor into a file in a file system.
+ *
* @param path the file to write
* @param moduleAttribute the {@code Module} attribute
* @param handler a handler that receives a {@link ClassBuilder}
- * @throws java.io.IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
+ * @throws IllegalArgumentException if building encounters a failure
*/
default void buildModuleTo(Path path,
ModuleAttribute moduleAttribute,
@@ -467,257 +662,396 @@ default void buildModuleTo(Path path,
}
/**
- * Transform one classfile into a new classfile with the aid of a
- * {@link ClassTransform}. The transform will receive each element of
+ * Transform one {@code class} file into a new {@code class} file according
+ * to a {@link ClassTransform}. The transform will receive each element of
* this class, as well as a {@link ClassBuilder} for building the new class.
* The transform is free to preserve, remove, or replace elements as it
* sees fit.
- *
- * @implNote
+ *
* This method behaves as if:
* {@snippet lang=java :
- * this.build(model.thisClass(), ConstantPoolBuilder.of(model),
- * clb -> clb.transform(model, transform));
+ * ConstantPoolBuilder cpb = null; // @replace substring=null; replacement=...
+ * this.build(model.thisClass(), cpb,
+ * clb -> clb.transform(model, transform));
* }
+ * where {@code cpb} is determined by {@link ConstantPoolSharingOption}.
+ *
+ * @apiNote
+ * This is named {@code transformClass} instead of {@code transform} for
+ * consistency with {@link ClassBuilder#transformField}, {@link
+ * ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode},
+ * and to distinguish from {@link ClassFileBuilder#transform}, which is
+ * more generic and powerful.
*
* @param model the class model to transform
* @param transform the transform
* @return the bytes of the new class
+ * @throws IllegalArgumentException if building encounters a failure
+ * @see ConstantPoolSharingOption
*/
default byte[] transformClass(ClassModel model, ClassTransform transform) {
return transformClass(model, model.thisClass(), transform);
}
/**
- * Transform one classfile into a new classfile with the aid of a
- * {@link ClassTransform}. The transform will receive each element of
+ * Transform one {@code class} file into a new {@code class} file according
+ * to a {@link ClassTransform}. The transform will receive each element of
* this class, as well as a {@link ClassBuilder} for building the new class.
* The transform is free to preserve, remove, or replace elements as it
* sees fit.
*
+ * @apiNote
+ * This is named {@code transformClass} instead of {@code transform} for
+ * consistency with {@link ClassBuilder#transformField}, {@link
+ * ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode},
+ * and to distinguish from {@link ClassFileBuilder#transform}, which is
+ * more generic and powerful.
+ *
* @param model the class model to transform
* @param newClassName new class name
* @param transform the transform
* @return the bytes of the new class
+ * @throws IllegalArgumentException if building encounters a failure
+ * @see ConstantPoolSharingOption
*/
default byte[] transformClass(ClassModel model, ClassDesc newClassName, ClassTransform transform) {
return transformClass(model, TemporaryConstantPool.INSTANCE.classEntry(newClassName), transform);
}
/**
- * Transform one classfile into a new classfile with the aid of a
- * {@link ClassTransform}. The transform will receive each element of
+ * Transform one {@code class} file into a new {@code class} file according
+ * to a {@link ClassTransform}. The transform will receive each element of
* this class, as well as a {@link ClassBuilder} for building the new class.
* The transform is free to preserve, remove, or replace elements as it
* sees fit.
- *
- * @implNote
+ *
* This method behaves as if:
* {@snippet lang=java :
- * this.build(newClassName, ConstantPoolBuilder.of(model),
- * clb -> clb.transform(model, transform));
+ * ConstantPoolBuilder cpb = null; // @replace substring=null; replacement=...
+ * this.build(newClassName, cpb, clb -> clb.transform(model, transform));
* }
+ * where {@code cpb} is determined by {@link ConstantPoolSharingOption}.
+ *
+ * @apiNote
+ * This is named {@code transformClass} instead of {@code transform} for
+ * consistency with {@link ClassBuilder#transformField}, {@link
+ * ClassBuilder#transformMethod}, and {@link MethodBuilder#transformCode},
+ * and to distinguish from {@link ClassFileBuilder#transform}, which is
+ * more generic and powerful.
*
* @param model the class model to transform
* @param newClassName new class name
* @param transform the transform
* @return the bytes of the new class
+ * @throws IllegalArgumentException if building encounters a failure
+ * @see ConstantPoolSharingOption
*/
byte[] transformClass(ClassModel model, ClassEntry newClassName, ClassTransform transform);
/**
- * Verify a classfile. Any verification errors found will be returned.
+ * Verify a {@code class} file. All verification errors found will be returned.
+ *
* @param model the class model to verify
- * @return a list of verification errors, or an empty list if no errors are
+ * @return a list of verification errors, or an empty list if no error is
* found
*/
List verify(ClassModel model);
/**
- * Verify a classfile. Any verification errors found will be returned.
- * @param bytes the classfile bytes to verify
- * @return a list of verification errors, or an empty list if no errors are
+ * Verify a {@code class} file. All verification errors found will be returned.
+ *
+ * @param bytes the {@code class} file bytes to verify
+ * @return a list of verification errors, or an empty list if no error is
* found
*/
List verify(byte[] bytes);
/**
- * Verify a classfile. Any verification errors found will be returned.
- * @param path the classfile path to verify
- * @return a list of verification errors, or an empty list if no errors are
+ * Verify a {@code class} file. All verification errors found will be returned.
+ *
+ * @param path the {@code class} file path to verify
+ * @return a list of verification errors, or an empty list if no error is
* found
- * @throws java.io.IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
*/
default List verify(Path path) throws IOException {
return verify(Files.readAllBytes(path));
}
- /** 0xCAFEBABE */
+ /**
+ * The magic number identifying the {@code class} file format, {@value
+ * "0x%04x" #MAGIC_NUMBER}. It is a big-endian 4-byte value.
+ */
int MAGIC_NUMBER = 0xCAFEBABE;
- /** The bit mask of PUBLIC access and property modifier. */
+ /** The bit mask of {@link AccessFlag#PUBLIC} access and property modifier. */
int ACC_PUBLIC = 0x0001;
- /** The bit mask of PROTECTED access and property modifier. */
+ /** The bit mask of {@link AccessFlag#PROTECTED} access and property modifier. */
int ACC_PROTECTED = 0x0004;
- /** The bit mask of PRIVATE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#PRIVATE} access and property modifier. */
int ACC_PRIVATE = 0x0002;
- /** The bit mask of INTERFACE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#INTERFACE} access and property modifier. */
int ACC_INTERFACE = 0x0200;
- /** The bit mask of ENUM access and property modifier. */
+ /** The bit mask of {@link AccessFlag#ENUM} access and property modifier. */
int ACC_ENUM = 0x4000;
- /** The bit mask of ANNOTATION access and property modifier. */
+ /** The bit mask of {@link AccessFlag#ANNOTATION} access and property modifier. */
int ACC_ANNOTATION = 0x2000;
- /** The bit mask of SUPER access and property modifier. */
+ /** The bit mask of {@link AccessFlag#SUPER} access and property modifier. */
int ACC_SUPER = 0x0020;
- /** The bit mask of ABSTRACT access and property modifier. */
+ /** The bit mask of {@link AccessFlag#ABSTRACT} access and property modifier. */
int ACC_ABSTRACT = 0x0400;
- /** The bit mask of VOLATILE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#VOLATILE} access and property modifier. */
int ACC_VOLATILE = 0x0040;
- /** The bit mask of TRANSIENT access and property modifier. */
+ /** The bit mask of {@link AccessFlag#TRANSIENT} access and property modifier. */
int ACC_TRANSIENT = 0x0080;
- /** The bit mask of SYNTHETIC access and property modifier. */
+ /** The bit mask of {@link AccessFlag#SYNTHETIC} access and property modifier. */
int ACC_SYNTHETIC = 0x1000;
- /** The bit mask of STATIC access and property modifier. */
+ /** The bit mask of {@link AccessFlag#STATIC} access and property modifier. */
int ACC_STATIC = 0x0008;
- /** The bit mask of FINAL access and property modifier. */
+ /** The bit mask of {@link AccessFlag#FINAL} access and property modifier. */
int ACC_FINAL = 0x0010;
- /** The bit mask of SYNCHRONIZED access and property modifier. */
+ /** The bit mask of {@link AccessFlag#SYNCHRONIZED} access and property modifier. */
int ACC_SYNCHRONIZED = 0x0020;
- /** The bit mask of BRIDGE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#BRIDGE} access and property modifier. */
int ACC_BRIDGE = 0x0040;
- /** The bit mask of VARARGS access and property modifier. */
+ /** The bit mask of {@link AccessFlag#VARARGS} access and property modifier. */
int ACC_VARARGS = 0x0080;
- /** The bit mask of NATIVE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#NATIVE} access and property modifier. */
int ACC_NATIVE = 0x0100;
- /** The bit mask of STRICT access and property modifier. */
+ /** The bit mask of {@link AccessFlag#STRICT} access and property modifier. */
int ACC_STRICT = 0x0800;
- /** The bit mask of MODULE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#MODULE} access and property modifier. */
int ACC_MODULE = 0x8000;
- /** The bit mask of OPEN access and property modifier. */
+ /** The bit mask of {@link AccessFlag#OPEN} access and property modifier. */
int ACC_OPEN = 0x20;
- /** The bit mask of MANDATED access and property modifier. */
+ /** The bit mask of {@link AccessFlag#MANDATED} access and property modifier. */
int ACC_MANDATED = 0x8000;
- /** The bit mask of TRANSITIVE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#TRANSITIVE} access and property modifier. */
int ACC_TRANSITIVE = 0x20;
- /** The bit mask of STATIC_PHASE access and property modifier. */
+ /** The bit mask of {@link AccessFlag#STATIC_PHASE} access and property modifier. */
int ACC_STATIC_PHASE = 0x40;
- /** The class major version of JAVA_1. */
+ /**
+ * The class major version of the initial version of Java, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_0
+ * @see ClassFileFormatVersion#RELEASE_1
+ */
int JAVA_1_VERSION = 45;
- /** The class major version of JAVA_2. */
+ /**
+ * The class major version introduced by Java 2 SE 1.2, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_2
+ */
int JAVA_2_VERSION = 46;
- /** The class major version of JAVA_3. */
+ /**
+ * The class major version introduced by Java 2 SE 1.3, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_3
+ */
int JAVA_3_VERSION = 47;
- /** The class major version of JAVA_4. */
+ /**
+ * The class major version introduced by Java 2 SE 1.4, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_4
+ */
int JAVA_4_VERSION = 48;
- /** The class major version of JAVA_5. */
+ /**
+ * The class major version introduced by Java 2 SE 5.0, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_5
+ */
int JAVA_5_VERSION = 49;
- /** The class major version of JAVA_6. */
+ /**
+ * The class major version introduced by Java SE 6, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_6
+ */
int JAVA_6_VERSION = 50;
- /** The class major version of JAVA_7. */
+ /**
+ * The class major version introduced by Java SE 7, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_7
+ */
int JAVA_7_VERSION = 51;
- /** The class major version of JAVA_8. */
+ /**
+ * The class major version introduced by Java SE 8, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_8
+ */
int JAVA_8_VERSION = 52;
- /** The class major version of JAVA_9. */
+ /**
+ * The class major version introduced by Java SE 9, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_9
+ */
int JAVA_9_VERSION = 53;
- /** The class major version of JAVA_10. */
+ /**
+ * The class major version introduced by Java SE 10, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_10
+ */
int JAVA_10_VERSION = 54;
- /** The class major version of JAVA_11. */
+ /**
+ * The class major version introduced by Java SE 11, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_11
+ */
int JAVA_11_VERSION = 55;
- /** The class major version of JAVA_12. */
+ /**
+ * The class major version introduced by Java SE 12, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_12
+ */
int JAVA_12_VERSION = 56;
- /** The class major version of JAVA_13. */
+ /**
+ * The class major version introduced by Java SE 13, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_13
+ */
int JAVA_13_VERSION = 57;
- /** The class major version of JAVA_14. */
+ /**
+ * The class major version introduced by Java SE 14, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_14
+ */
int JAVA_14_VERSION = 58;
- /** The class major version of JAVA_15. */
+ /**
+ * The class major version introduced by Java SE 15, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_15
+ */
int JAVA_15_VERSION = 59;
- /** The class major version of JAVA_16. */
+ /**
+ * The class major version introduced by Java SE 16, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_16
+ */
int JAVA_16_VERSION = 60;
- /** The class major version of JAVA_17. */
+ /**
+ * The class major version introduced by Java SE 17, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_17
+ */
int JAVA_17_VERSION = 61;
- /** The class major version of JAVA_18. */
+ /**
+ * The class major version introduced by Java SE 18, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_18
+ */
int JAVA_18_VERSION = 62;
- /** The class major version of JAVA_19. */
+ /**
+ * The class major version introduced by Java SE 19, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_19
+ */
int JAVA_19_VERSION = 63;
- /** The class major version of JAVA_20. */
+ /**
+ * The class major version introduced by Java SE 20, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_20
+ */
int JAVA_20_VERSION = 64;
- /** The class major version of JAVA_21. */
+ /**
+ * The class major version introduced by Java SE 21, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_21
+ */
int JAVA_21_VERSION = 65;
- /** The class major version of JAVA_22. */
+ /**
+ * The class major version introduced by Java SE 22, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_22
+ */
int JAVA_22_VERSION = 66;
- /** The class major version of JAVA_23. */
+ /**
+ * The class major version introduced by Java SE 23, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_23
+ */
int JAVA_23_VERSION = 67;
- /** The class major version of JAVA_24. */
+ /**
+ * The class major version introduced by Java SE 24, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_24
+ */
int JAVA_24_VERSION = 68;
/**
- * The class major version of JAVA_25.
+ * The class major version introduced by Java SE 25, {@value}.
+ *
+ * @see ClassFileFormatVersion#RELEASE_25
* @since 25
*/
int JAVA_25_VERSION = 69;
/**
- * A minor version number indicating a class uses preview features
- * of a Java SE version since 12, for major versions {@value
+ * A minor version number {@value} indicating a class uses preview features
+ * of a Java SE release since 12, for major versions {@value
* #JAVA_12_VERSION} and above.
*/
int PREVIEW_MINOR_VERSION = 65535;
/**
- * {@return the latest major Java version}
+ * {@return the latest class major version supported by the current runtime}
*/
static int latestMajorVersion() {
return JAVA_25_VERSION;
}
/**
- * {@return the latest minor Java version}
+ * {@return the latest class minor version supported by the current runtime}
+ *
+ * @apiNote
+ * This does not report the {@link #PREVIEW_MINOR_VERSION} when the current
+ * runtime has preview feature enabled, as {@code class} files with a major
+ * version other than {@link #latestMajorVersion()} and the preview minor
+ * version are not supported.
*/
static int latestMinorVersion() {
return 0;
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.java b/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.java
index 01eeefd0b9b..172daa83cf8 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassFileBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -31,17 +31,36 @@
import jdk.internal.classfile.impl.TransformImpl;
/**
- * A builder for a classfile or portion of a classfile. Builders are rarely
- * created directly; they are passed to handlers by methods such as
- * {@link ClassFile#build(ClassDesc, Consumer)} or to transforms.
- * Elements of the newly built entity can be specified
- * abstractly (by passing a {@link ClassFileElement} to {@link #with(ClassFileElement)}
- * or concretely by calling the various {@code withXxx} methods.
+ * A builder for a {@link CompoundElement}, which accepts the member elements
+ * to be integrated into the built structure. Builders are usually passed as
+ * an argument to {@link Consumer} handlers, such as in {@link
+ * ClassFile#build(ClassDesc, Consumer)}. The handlers should deliver elements
+ * to a builder similar to how a {@link CompoundElement} traverses its member
+ * elements.
+ *
+ * The basic way a builder accepts elements is through {@link #with}, which
+ * supports call chaining. Concrete subtypes of builders usually define extra
+ * methods to define elements directly to the builder, such as {@link
+ * ClassBuilder#withFlags(int)} or {@link CodeBuilder#aload(int)}.
+ *
+ * Whether a member element can appear multiple times in a compound structure
+ * affects the behavior of the element in {@code ClassFileBuilder}s. If an
+ * element can appear at most once but multiple instances are supplied to a
+ * {@code ClassFileBuilder}, the last supplied instance appears on the built
+ * structure. If an element appears exactly once but no instance is supplied,
+ * an unspecified default value element may be used in that structure.
+ *
+ * Due to restrictions of the {@code class} file format, certain member elements
+ * that can be modeled by the API cannot be represented in the built structure
+ * under specific circumstances. Passing such elements to the builder causes
+ * {@link IllegalArgumentException}. Some {@link ClassFile.Option}s control
+ * whether such elements should be altered or dropped to produce valid {@code
+ * class} files.
*
- * @param the element type
- * @param the builder type
+ * @param the member element type
+ * @param the self type of this builder
+ * @see CompoundElement
* @see ClassFileTransform
- *
* @sealedGraph
* @since 24
*/
@@ -49,8 +68,15 @@ public sealed interface ClassFileBuilder permits ClassBuilder, FieldBuilder, MethodBuilder, CodeBuilder {
/**
- * Integrate the {@link ClassFileElement} into the entity being built.
- * @param e the element
+ * Integrates the member element into the structure being built.
+ *
+ * @apiNote
+ * This method exists to implement {@link Consumer}; users can use {@link
+ * #with} for call chaining.
+ *
+ * @param e the member element
+ * @throws IllegalArgumentException if the member element cannot be
+ * represented in the {@code class} file format
*/
@Override
default void accept(E e) {
@@ -58,9 +84,12 @@ default void accept(E e) {
}
/**
- * Integrate the {@link ClassFileElement} into the entity being built.
- * @param e the element
+ * Integrates the member element into the structure being built.
+ *
+ * @param e the member element
* @return this builder
+ * @throws IllegalArgumentException if the member element cannot be
+ * represented in the {@code class} file format
*/
B with(E e);
@@ -70,10 +99,29 @@ default void accept(E e) {
ConstantPoolBuilder constantPool();
/**
- * Apply a transform to a model, directing results to this builder.
- * @param model the model to transform
+ * Applies a transform to a compound structure, directing results to this
+ * builder.
+ *
+ * The transform will receive each element of the compound structure, as
+ * well as this builder for building the structure. The transform is free
+ * to preserve, remove, or replace elements as it sees fit.
+ *
+ * A builder can run multiple transforms against different compound
+ * structures, integrating member elements of different origins.
+ *
+ * @apiNote
+ * Many subinterfaces have methods like {@link ClassBuilder#transformMethod}
+ * or {@link MethodBuilder#transformCode}. However, calling them is
+ * fundamentally different from calling this method: those methods call the
+ * {@code transform} on the child builders instead of on itself. For
+ * example, {@code classBuilder.transformMethod} calls {@code
+ * methodBuilder.transform} with a new method builder instead of calling
+ * {@code classBuilder.transform} on itself.
+ *
+ * @param model the structure to transform
* @param transform the transform to apply
* @return this builder
+ * @see ClassFileTransform
*/
default B transform(CompoundElement model, ClassFileTransform, E, B> transform) {
@SuppressWarnings("unchecked")
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java b/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java
index ed84eb39d53..5d5d0020949 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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,12 +25,34 @@
package java.lang.classfile;
/**
- * Immutable model for a portion of (or the entirety of) a classfile. Elements
- * that model parts of the classfile that have attributes will implement {@link
- * AttributedElement}; elements that model complex parts of the classfile that
- * themselves contain their own child elements will implement {@link
- * CompoundElement}. Elements specific to various locations in the classfile
- * will implement {@link ClassElement}, {@link MethodElement}, etc.
+ * Marker interface for structures with special capabilities in the {@code
+ * class} file format. {@link AttributedElement} indicates a structure has
+ * {@link Attribute}s. {@link CompoundElement} indicates a structure can be
+ * viewed as a composition of member structures, whose memberships are marked by
+ * {@link ClassElement}, {@link MethodElement}, {@link FieldElement}, or {@link
+ * CodeElement}.
+ *
+ * Membership Elements
+ * {@link ClassModel}, {@link MethodModel}, {@link FieldModel}, and {@link
+ * CodeModel} each has a dedicated interface marking its member structures:
+ * {@link ClassElement}, {@link MethodElement}, {@link FieldElement}, and
+ * {@link CodeElement}. They can be supplied to a {@link ClassBuilder}, a
+ * {@link MethodBuilder}, a {@link FieldBuilder}, or a {@link CodeBuilder} to be
+ * included as members of the built model. Unless otherwise specified, these
+ * structures are delivered during the {@linkplain CompoundElement traversal} of
+ * the corresponding models. Some of these elements may appear at most once or
+ * exactly once in the traversal of the models; such elements have special
+ * treatment by {@link ClassFileBuilder} and are specified in their modeling
+ * interfaces. If such elements appear multiple times during traversal, the
+ * last occurrence should be used and all previous instances should be
+ * discarded.
+ *
+ * These membership element marker interfaces are sealed; future versions of the
+ * Java SE Platform may define new elements to the sealed hierarchy when the
+ * {@code class} file format for the Java Platform evolves. Using an exhaustive
+ * pattern matching switch over these hierarchies indicates the user only wish
+ * the processing code to run on a specific version of Java Platform, and will
+ * fail if unknown new elements are encountered.
*
* @sealedGraph
* @since 24
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java b/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java
index 7d9385eed68..9126b13bce9 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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,44 +25,74 @@
package java.lang.classfile;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
+import java.util.function.Predicate;
import java.util.function.Supplier;
/**
- * A transformation on streams of elements. Transforms are used during
- * transformation of classfile entities; a transform is provided to a method like
- * {@link ClassFile#transformClass(ClassModel, ClassTransform)}, and the elements of the class,
- * along with a builder, are presented to the transform.
- *
- *
The subtypes of {@linkplain
- * ClassFileTransform} (e.g., {@link ClassTransform}) are functional interfaces
- * that accept an element and a corresponding builder. Since any element can be
- * reproduced on the builder via {@link ClassBuilder#with(ClassFileElement)}, a
- * transform can easily leave elements in place, remove them, replace them, or
- * augment them with other elements. This enables localized transforms to be
- * represented concisely.
- *
- *
Transforms also have an {@link #atEnd(ClassFileBuilder)} method, for
- * which the default implementation does nothing, so that a transform can
- * perform additional building after the stream of elements is exhausted.
- *
- *
Transforms can be chained together via the {@link
- * #andThen(ClassFileTransform)} method, so that the output of one becomes the
- * input to another. This allows smaller units of transformation to be captured
- * and reused.
- *
- *
Some transforms are stateful; for example, a transform that injects an
- * annotation on a class may watch for the {@link RuntimeVisibleAnnotationsAttribute}
- * element and transform it if found, but if it is not found, will generate a
- * {@linkplain RuntimeVisibleAnnotationsAttribute} element containing the
- * injected annotation from the {@linkplain #atEnd(ClassFileBuilder)} handler.
- * To do this, the transform must accumulate some state during the traversal so
- * that the end handler knows what to do. If such a transform is to be reused,
- * its state must be reset for each traversal; this will happen automatically if
- * the transform is created with {@link ClassTransform#ofStateful(Supplier)} (or
- * corresponding methods for other classfile locations.)
+ * A transformation on a {@link CompoundElement} by processing its individual
+ * member elements and sending the results to a {@link ClassFileBuilder},
+ * through {@link ClassFileBuilder#transform}. A subtype of {@code
+ * ClassFileTransform} is defined for each subtype of {@link CompoundElement}
+ * and {@link ClassFileBuilder}, as shown in the sealed class hierarchy below.
+ *
+ * For example, this is a basic transformation of a {@link CodeModel} that
+ * redirects all calls to static methods in the {@code Foo} class to the {@code
+ * Bar} class, preserving all other elements:
+ * {@snippet file="PackageSnippets.java" region=fooToBarTransform}
+ * Note that if no transformation of a member element is desired, the element
+ * should be presented to {@link ClassFileBuilder#with builder::with}. If no
+ * action is taken, that member element is dropped.
+ *
+ * More advanced usages of transforms include {@linkplain ##start-end start or
+ * end handling}, {@linkplain ##stateful stateful transformation} that makes a
+ * decision based on previously encountered member elements, and {@linkplain
+ * ##composition composition} of transforms, where one transform processes the
+ * results of a previous transform on the input compound structure. All these
+ * capabilities are supported by this interface and accessible to user transform
+ * implementations.
+ *
+ * Users can define custom start and end handling for a transform by overriding
+ * {@link #atStart} and {@link #atEnd}. The start handler is called before any
+ * member element is processed, and the end handler is called after all member
+ * elements are processed. For example, the start handler can be used to inject
+ * extra code elements to the beginning of a code array, and the end handler,
+ * combined with stateful transformation, can perform cleanup actions, such as
+ * determining if an attribute has been merged, or if a new attribute should be
+ * defined. Each subtype of {@code ClassFileTransform} defines a utility method
+ * {@code endHandler} that returns a transform that only has end handling.
+ *
+ * Transforms can have states that persist across processing of individual
+ * member elements. For example, if a transform injects an annotation, the
+ * transform may keep track if it has encountered and presented an updated
+ * {@link RuntimeVisibleAnnotationsAttribute} to the builder; if it has not yet,
+ * it can present a new attribute containing only the injected annotation in its
+ * end handler. If such a transform is to be shared or reused, each returned
+ * transform should have its own state. Each subtype of {@code ClassFileTransform}
+ * defines a utility method {@code ofStateful} where a supplier creates the
+ * transform at its initial state each time the transform is reused.
+ *
+ * Transforms can be composed via {@link #andThen}. When this transform is
+ * composed with another transform, it means the output member elements received
+ * by the {@link ClassFileBuilder} become the input elements to that other
+ * transform. Composition avoids building intermediate structures for multiple
+ * transforms to run on. Each subtype of {@code ClassFileTransform} implements
+ * {@link #andThen}, which generally should not be implemented by users.
+ *
+ * Transforms that run on smaller structures can be lifted to its enclosing
+ * structures to selectively run on all enclosed smaller structures of the same
+ * kind. For example, a {@link CodeTransform} can be lifted via {@link
+ * ClassTransform#transformingMethodBodies(Predicate, CodeTransform)} to
+ * transform the method body of select methods in the class it runs on. This
+ * allows users to write small transforms and apply to larger scales.
+ *
+ * Besides {@link ClassFileBuilder#transform}, there are other methods that
+ * accepts a transform conveniently, such as {@link ClassFile#transformClass},
+ * {@link ClassBuilder#transformField}, {@link ClassBuilder#transformMethod}, or
+ * {@link MethodBuilder#transformCode}. They are convenience methods that suit
+ * the majority of transformation scenarios.
*
* @param the transform type
- * @param the element type
+ * @param the member element type
* @param the builder type
*
* @sealedGraph
@@ -79,6 +109,9 @@ public sealed interface ClassFileTransform<
* body.) If no transformation is desired, the element can be presented to
* {@link B#with(ClassFileElement)}. If the element is to be dropped, no
* action is required.
+ *
+ * This method is called by the Class-File API. Users should never call
+ * this method.
*
* @param builder the builder for the new entity
* @param element the element
@@ -89,6 +122,9 @@ public sealed interface ClassFileTransform<
* Take any final action during transformation of a classfile entity. Called
* after all elements of the class are presented to {@link
* #accept(ClassFileBuilder, ClassFileElement)}.
+ *
+ * This method is called by the Class-File API. Users should never call
+ * this method.
*
* @param builder the builder for the new entity
* @implSpec The default implementation does nothing.
@@ -100,6 +136,9 @@ default void atEnd(B builder) {
* Take any preliminary action during transformation of a classfile entity.
* Called before any elements of the class are presented to {@link
* #accept(ClassFileBuilder, ClassFileElement)}.
+ *
+ * This method is called by the Class-File API. Users should never call
+ * this method.
*
* @param builder the builder for the new entity
* @implSpec The default implementation does nothing.
@@ -110,6 +149,10 @@ default void atStart(B builder) {
/**
* Chain this transform with another; elements presented to the builder of
* this transform will become the input to the next transform.
+ *
+ * This method is implemented by the Class-File API. Users usually don't
+ * have sufficient access to Class-File API functionalities to override this
+ * method correctly for generic downstream transforms.
*
* @param next the downstream transform
* @return the chained transform
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.java b/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.java
index 1916a185cc8..5a795b94865 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassFileVersion.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -24,32 +24,62 @@
*/
package java.lang.classfile;
+import java.lang.reflect.ClassFileFormatVersion;
+
import jdk.internal.classfile.impl.ClassFileVersionImpl;
/**
- * Models the classfile version information for a class. Delivered as a {@link
- * java.lang.classfile.ClassElement} when traversing the elements of a {@link
- * ClassModel}.
+ * Models the minor and major version numbers of a {@code class} file (JVMS
+ * {@jvms 4.1}). The {@code class} file version appears exactly once in each
+ * class, and is set to an unspecified default value if not explicitly provided.
+ *
+ * The major versions of {@code class} file format begins at {@value
+ * ClassFile#JAVA_1_VERSION} for Java Platform version 1.0.2, and is continuous
+ * up to {@link ClassFile#latestMajorVersion()}. In general, each major version
+ * defines a new supported {@code class} file format, modeled by {@link
+ * ClassFileFormatVersion}, and supports all previous formats.
+ *
+ * For major versions up to {@value ClassFile#JAVA_11_VERSION} for Java SE
+ * Platform 11, the minor version of any value is supported. For major versions
+ * {@value ClassFile#JAVA_12_VERSION} for Java SE Platform version 12 and above,
+ * the minor version must be {@code 0} or {@value ClassFile#PREVIEW_MINOR_VERSION}.
+ * The minor version {@code 0} is always supported, and represents the format
+ * modeled by {@link ClassFileFormatVersion}. The minor version {@code 65535}
+ * indicates the {@code class} file uses preview features of the Java SE
+ * Platform release represented by the major version. A Java Virtual Machine
+ * can only load such a {@code class} file if it has the same Java SE Platform
+ * version and the JVM has preview features enabled.
*
+ * @see ClassModel#majorVersion()
+ * @see ClassModel#minorVersion()
+ * @see ClassFileFormatVersion
+ * @jvms 4.1 The {@code ClassFile} Structure
* @since 24
*/
public sealed interface ClassFileVersion
extends ClassElement
permits ClassFileVersionImpl {
/**
- * {@return the major classfile version}
+ * {@return the major version} It is in the range of unsigned short, {@code
+ * [0, 65535]}.
+ *
+ * @apiNote
+ * Constants in {@link ClassFile} named {@code Java_#_VERSION}, where # is
+ * a release number, such as {@link ClassFile#JAVA_21_VERSION}, describe the
+ * class major versions of the Java Platform SE.
*/
int majorVersion();
/**
- * {@return the minor classfile version}
+ * {@return the minor version} It is in the range of unsigned short, {@code
+ * [0, 65535]}.
*/
int minorVersion();
/**
* {@return a {@link ClassFileVersion} element}
- * @param majorVersion the major classfile version
- * @param minorVersion the minor classfile version
+ * @param majorVersion the major version
+ * @param minorVersion the minor version
*/
static ClassFileVersion of(int majorVersion, int minorVersion) {
return new ClassFileVersionImpl(majorVersion, minorVersion);
diff --git a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java
index 2c612854a64..3bc56b43b7b 100644
--- a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -24,7 +24,11 @@
*/
package java.lang.classfile;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.classfile.ClassFile.StackMapsOption;
+import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.constant.ClassDesc;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
@@ -42,27 +46,42 @@
import static java.util.Objects.requireNonNull;
/**
- * Provides class hierarchy information for generating correct stack maps
- * during code building.
+ * Provides class hierarchy information for {@linkplain StackMapsOption stack
+ * maps generation} and {@linkplain ClassFile#verify(byte[]) verification}.
+ * A class hierarchy resolver must be able to process all classes and interfaces
+ * encountered during these workloads.
*
+ * @see ClassFile.ClassHierarchyResolverOption
+ * @see StackMapTableAttribute
+ * @jvms 4.10.1.2 Verification Type System
* @since 24
*/
@FunctionalInterface
public interface ClassHierarchyResolver {
/**
- * {@return the default instance of {@linkplain ClassHierarchyResolver} that
+ * {@return the default instance of {@code ClassHierarchyResolver} that
* gets {@link ClassHierarchyInfo} from system class loader with reflection}
+ * This default instance cannot load classes from other class loaders, such
+ * as the caller's class loader; it also loads the system classes if they
+ * are not yet loaded, which makes it unsuitable for instrumentation.
*/
static ClassHierarchyResolver defaultResolver() {
return ClassHierarchyImpl.DEFAULT_RESOLVER;
}
/**
- * {@return the {@link ClassHierarchyInfo} for a given class name, or null
- * if the name is unknown to the resolver}
+ * {@return the {@code ClassHierarchyInfo} for a given class name, or {@code
+ * null} if the name is unknown to the resolver}
+ *
+ * This method is called by the Class-File API to obtain the hierarchy
+ * information of a class or interface; users should not call this method.
+ * The symbolic descriptor passed by the Class-File API always represents
+ * a class or interface.
+ *
* @param classDesc descriptor of the class
- * @throws IllegalArgumentException if a class shouldn't be queried for hierarchy
+ * @throws IllegalArgumentException if a class shouldn't be queried for
+ * hierarchy, such as when it is inaccessible
*/
ClassHierarchyInfo getClassInfo(ClassDesc classDesc);
@@ -78,6 +97,7 @@ sealed interface ClassHierarchyInfo permits ClassHierarchyImpl.ClassHierarchyInf
*
* @param superClass descriptor of the super class, may be {@code null}
* @return the info indicating the super class
+ * @see Superclass
*/
static ClassHierarchyInfo ofClass(ClassDesc superClass) {
return new ClassHierarchyImpl.ClassHierarchyInfoImpl(superClass, false);
@@ -94,14 +114,15 @@ static ClassHierarchyInfo ofInterface() {
}
/**
- * Chains this {@linkplain ClassHierarchyResolver} with another to be
- * consulted if this resolver does not know about the specified class.
+ * Chains this {@code ClassHierarchyResolver} with another to be consulted
+ * if this resolver does not know about the specified class.
+ *
+ * @implSpec
+ * The default implementation returns resolver implemented to query {@code
+ * other} resolver in case this resolver returns {@code null}.
*
* @param other the other resolver
* @return the chained resolver
- *
- * @implSpec The default implementation returns resolver implemented to ask
- * other resolver in cases where this resolver returns {@code null}.
*/
default ClassHierarchyResolver orElse(ClassHierarchyResolver other) {
requireNonNull(other);
@@ -117,32 +138,32 @@ public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) {
}
/**
- * Returns a ClassHierarchyResolver that caches class hierarchy information from this
- * resolver. The returned resolver will not update if delegate resolver returns differently.
- * The thread safety of the returned resolver depends on the thread safety of the map
+ * {@return a {@code ClassHierarchyResolver} that caches class hierarchy
+ * information from this resolver} The returned resolver will not update if
+ * the query results from this resolver changed over time. The thread
+ * safety of the returned resolver depends on the thread safety of the map
* returned by the {@code cacheFactory}.
*
- * @param cacheFactory the factory for the cache
- * @return the ClassHierarchyResolver with caching
+ * @implSpec
+ * The default implementation returns a resolver holding an instance of the
+ * cache map provided by the {@code cacheFactory}. It looks up in the cache
+ * map, or if a class name has not yet been queried, queries this resolver
+ * and caches the result, including a {@code null} that indicates unknown
+ * class names. The cache map may refuse {@code null} keys and values.
*
- * @implSpec The default implementation returns resolver holding an instance
- * of the cache map provided by the {@code cacheFactory}. It asks
- * the cache map always first and fills the cache map with all
- * resolved and also unresolved class info. The cache map may refuse
- * {@code null} keys and values.
+ * @param cacheFactory the factory for the cache
*/
default ClassHierarchyResolver cached(Supplier