diff --git a/doc/testing.html b/doc/testing.html index 195153c8612..c8d0b928bb0 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -284,9 +284,10 @@
Note: To be able to run the Gtest suite, you need to configure your build to be able to find a proper version of the gtest -source. For details, see the section "Running Tests" in the build -documentation.
+source. For details, see the section "Running Tests" in the +build documentation (html, markdown).Since the Hotspot Gtest suite is so quick, the default is to run all
tests. This is specified by just gtest, or as a fully
qualified test descriptor gtest:all.
- * Strings are constant; their values cannot be changed after they - * are created. String buffers support mutable strings. - * Because String objects are immutable they can be shared. For example: + * Strings are immutable; their values cannot be changed after they + * are created. Because String objects are immutable they can be shared. + * For example: *
* String str = "abc"; *
diff --git a/src/java.base/share/classes/java/text/SimpleDateFormat.java b/src/java.base/share/classes/java/text/SimpleDateFormat.java index ba73e5b5a86..4c57214dbba 100644 --- a/src/java.base/share/classes/java/text/SimpleDateFormat.java +++ b/src/java.base/share/classes/java/text/SimpleDateFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; -import static java.text.DateFormatSymbols.*; +import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -57,6 +57,8 @@ import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.TimeZoneNameUtility; +import static java.text.DateFormatSymbols.*; + /** * {@code SimpleDateFormat} is a concrete class for formatting and * parsing dates in a locale-sensitive manner. It allows for formatting @@ -1293,15 +1295,22 @@ private void subFormat(int patternCharIndex, int count, case PATTERN_ZONE_NAME: // 'z' if (current == null) { + TimeZone tz = calendar.getTimeZone(); + String tzid = tz.getID(); + int zoneOffset = calendar.get(Calendar.ZONE_OFFSET); + int dstOffset = calendar.get(Calendar.DST_OFFSET) + zoneOffset; + + // Check if an explicit metazone DST offset exists + String explicitDstOffset = TimeZoneNameUtility.explicitDstOffset(tzid); + boolean daylight = explicitDstOffset != null ? + dstOffset == ZoneOffset.of(explicitDstOffset).getTotalSeconds() * 1_000 : + dstOffset != zoneOffset; if (formatData.locale == null || formatData.isZoneStringsSet) { - int zoneIndex = - formatData.getZoneIndex(calendar.getTimeZone().getID()); + int zoneIndex = formatData.getZoneIndex(tzid); if (zoneIndex == -1) { - value = calendar.get(Calendar.ZONE_OFFSET) + - calendar.get(Calendar.DST_OFFSET); - buffer.append(ZoneInfoFile.toCustomID(value)); + buffer.append(ZoneInfoFile.toCustomID(dstOffset)); } else { - int index = (calendar.get(Calendar.DST_OFFSET) == 0) ? 1: 3; + int index = daylight ? 3 : 1; if (count < 4) { // Use the short name index++; @@ -1310,8 +1319,6 @@ private void subFormat(int patternCharIndex, int count, buffer.append(zoneStrings[zoneIndex][index]); } } else { - TimeZone tz = calendar.getTimeZone(); - boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0); int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG); buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale)); } diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 4708094effb..4594dc6f1dc 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -4513,7 +4513,11 @@ public boolean format(DateTimePrintContext context, StringBuilder buf, boolean o TemporalAccessor dt = context.getTemporal(); int type = GENERIC; if (!isGeneric) { - if (dt.isSupported(ChronoField.INSTANT_SECONDS)) { + // Check if an explicit metazone DST offset exists + String dstOffset = TimeZoneNameUtility.explicitDstOffset(zname); + if (dt.isSupported(OFFSET_SECONDS) && dstOffset != null) { + type = ZoneOffset.from(dt).equals(ZoneOffset.of(dstOffset)) ? DST : STD; + } else if (dt.isSupported(ChronoField.INSTANT_SECONDS)) { type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD; } else if (dt.isSupported(ChronoField.EPOCH_DAY) && dt.isSupported(ChronoField.NANO_OF_DAY)) { diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 682476d8082..6b071cd15b2 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -1932,7 +1932,7 @@ public String getISO3Country() throws MissingResourceException { } /** - * Returns a name for the locale's language that is appropriate for display to the + * Returns a name for {@code this} locale's language that is appropriate for display to the * user. * If possible, the name returned will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. @@ -1946,14 +1946,15 @@ public String getISO3Country() throws MissingResourceException { * this function falls back on the English name, and uses the ISO code as a last-resort * value. If the locale doesn't specify a language, this function returns the empty string. * - * @return The name of the display language. + * @return The name of the display language appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayLanguage() { + public String getDisplayLanguage() { return getDisplayLanguage(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's language that is appropriate for display to the + * Returns a name for {@code this} locale's language that is appropriate for display to the * user. * If possible, the name returned will be localized according to inLocale. * For example, if the locale is fr_FR and inLocale @@ -1964,7 +1965,7 @@ public final String getDisplayLanguage() { * on the ISO code as a last-resort value. If the locale doesn't specify a language, * this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display language. + * @param inLocale The locale in which to localize the display language. * @return The name of the display language appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -1973,13 +1974,13 @@ public String getDisplayLanguage(Locale inLocale) { } /** - * Returns a name for the locale's script that is appropriate for display to + * Returns a name for {@code this} locale's script that is appropriate for display to * the user. If possible, the name will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. Returns * the empty string if this locale doesn't specify a script code. * - * @return the display name of the script code for the current default - * {@link Locale.Category#DISPLAY DISPLAY} locale + * @return The display name of the script code appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. * @since 1.7 */ public String getDisplayScript() { @@ -1987,14 +1988,13 @@ public String getDisplayScript() { } /** - * Returns a name for the locale's script that is appropriate + * Returns a name for {@code this} locale's script that is appropriate * for display to the user. If possible, the name will be * localized for the given locale. Returns the empty string if * this locale doesn't specify a script code. * - * @param inLocale The locale for which to retrieve the display script. - * @return the display name of the script code for the current default - * {@link Locale.Category#DISPLAY DISPLAY} locale + * @param inLocale The locale in which to localize the display script. + * @return The display name of the script code appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} * @since 1.7 */ @@ -2003,7 +2003,7 @@ public String getDisplayScript(Locale inLocale) { } /** - * Returns a name for the locale's country that is appropriate for display to the + * Returns a name for {@code this} locale's country that is appropriate for display to the * user. * If possible, the name returned will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. @@ -2017,14 +2017,15 @@ public String getDisplayScript(Locale inLocale) { * this function falls back on the English name, and uses the ISO code as a last-resort * value. If the locale doesn't specify a country, this function returns the empty string. * - * @return The name of the country appropriate to the locale. + * @return The name of the country appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayCountry() { + public String getDisplayCountry() { return getDisplayCountry(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's country that is appropriate for display to the + * Returns a name for {@code this} locale's country that is appropriate for display to the * user. * If possible, the name returned will be localized according to inLocale. * For example, if the locale is fr_FR and inLocale @@ -2035,7 +2036,7 @@ public final String getDisplayCountry() { * on the ISO code as a last-resort value. If the locale doesn't specify a country, * this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display country. + * @param inLocale The locale in which to localize the display country. * @return The name of the country appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -2061,23 +2062,24 @@ private String getDisplayString(String code, String cat, Locale inLocale, int ty } /** - * Returns a name for the locale's variant code that is appropriate for display to the + * Returns a name for {@code this} locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. If the locale * doesn't specify a variant code, this function returns the empty string. * - * @return The name of the display variant code appropriate to the locale. + * @return The name of the display variant code appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayVariant() { + public String getDisplayVariant() { return getDisplayVariant(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's variant code that is appropriate for display to the + * Returns a name for {@code this} locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for inLocale. If the locale * doesn't specify a variant code, this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display variant code. + * @param inLocale The locale in which to localize the display variant code. * @return The name of the display variant code appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -2098,7 +2100,7 @@ public String getDisplayVariant(Locale inLocale) { } /** - * Returns a name for the locale that is appropriate for display to the + * Returns a name for {@code this} locale that is appropriate for display to the * user. This will be the values returned by getDisplayLanguage(), * getDisplayScript(), getDisplayCountry(), getDisplayVariant() and * optional {@linkplain ##def_locale_extension Unicode extensions} @@ -2116,14 +2118,15 @@ public String getDisplayVariant(Locale inLocale) { * be localized depending on the locale. If the language, script, country, * and variant fields are all empty, this function returns the empty string. * - * @return The name of the locale appropriate to display. + * @return The display name appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayName() { + public String getDisplayName() { return getDisplayName(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale that is appropriate for display + * Returns a name for {@code this} locale that is appropriate for display * to the user. This will be the values returned by * getDisplayLanguage(), getDisplayScript(), getDisplayCountry(), * getDisplayVariant(), and optional {@linkplain ##def_locale_extension @@ -2142,8 +2145,8 @@ public final String getDisplayName() { * be localized depending on the locale. If the language, script, country, * and variant fields are all empty, this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display name. - * @return The name of the locale appropriate to display. + * @param inLocale The locale in which to localize the display name. + * @return The display name appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ public String getDisplayName(Locale inLocale) { diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index db19eda6399..f91db79891b 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1119,15 +1119,15 @@ public static final ResourceBundle getBundle(String baseName, Locale targetLocal * sequence of bundle names generated by truncating the last underscore and * the part following it is inserted after a candidate bundle name with the * original variant. For example, for a locale with language "en", script - * "Latn, country "US" and variant "WINDOWS_VISTA", and bundle base name + * "Latn", country "US" and variant "WINDOWS_WIN11", and bundle base name * "MyResource", the list of candidate bundle names below is generated: * *
- * MyResource_en_Latn_US_WINDOWS_VISTA
+ * MyResource_en_Latn_US_WINDOWS_WIN11
* MyResource_en_Latn_US_WINDOWS
* MyResource_en_Latn_US
* MyResource_en_Latn
- * MyResource_en_US_WINDOWS_VISTA
+ * MyResource_en_US_WINDOWS_WIN11
* MyResource_en_US_WINDOWS
* MyResource_en_US
* MyResource_en
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
index 3a915cf96df..480553e9a62 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
@@ -1924,9 +1924,15 @@ private void doTunneling0() throws IOException {
}
statusLine = responses.getValue(0);
- StringTokenizer st = new StringTokenizer(statusLine);
- st.nextToken();
- respCode = Integer.parseInt(st.nextToken().trim());
+ respCode = parseConnectResponseCode(statusLine);
+ if (respCode == -1) {
+ // a respCode of -1, due to a invalid status line,
+ // will (rightly) result in an IOException being thrown
+ // later in this code. here we merely log the invalid status line.
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("invalid status line: \"" + statusLine + "\"");
+ }
+ }
if (respCode == HTTP_PROXY_AUTH) {
// Read comments labeled "Failed Negotiate" for details.
boolean dontUseNegotiate = false;
@@ -2027,6 +2033,37 @@ private void doTunneling0() throws IOException {
responses.reset();
}
+ // parses the status line, that was returned for a CONNECT request, and returns
+ // the response code from that line. returns -1 if the response code could not be
+ // parsed.
+ private static int parseConnectResponseCode(final String statusLine) {
+ final int invalidStatusLine = -1;
+ if (statusLine == null || statusLine.isBlank()) {
+ return invalidStatusLine;
+ }
+ //
+ // status-line = HTTP-version SP status-code SP [ reason-phrase ]
+ // SP = space character
+ //
+ final StringTokenizer st = new StringTokenizer(statusLine, " ");
+ if (!st.hasMoreTokens()) {
+ return invalidStatusLine;
+ }
+ st.nextToken(); // the HTTP version part (ex: HTTP/1.1)
+ if (!st.hasMoreTokens()) {
+ return invalidStatusLine;
+ }
+ final String v = st.nextToken().trim(); // status code
+ try {
+ return Integer.parseInt(v);
+ } catch (NumberFormatException nfe) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("invalid response code: " + v);
+ }
+ }
+ return invalidStatusLine;
+ }
+
/**
* Overridden in https to also include the server certificate
*/
diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java
index dea86351cc8..0ca197160a9 100644
--- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java
+++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java
@@ -49,6 +49,9 @@
*/
public class KAKeyDerivation implements SSLKeyDerivation {
+ // Algorithm used to derive TLS 1.3 shared secrets
+ private static final String t13KeyDerivationAlgorithm =
+ System.getProperty("jdk.tls.t13KeyDerivationAlgorithm", "Generic");
private final String algorithmName;
private final HandshakeContext context;
private final PrivateKey localPrivateKey;
@@ -234,7 +237,7 @@ private SecretKey t13DeriveKey(String type)
var decapsulator = kem.newDecapsulator(localPrivateKey);
sharedSecret = decapsulator.decapsulate(
keyshare, 0, decapsulator.secretSize(),
- "TlsPremasterSecret");
+ t13KeyDerivationAlgorithm);
} catch (IllegalArgumentException | InvalidKeyException |
DecapsulateException e) {
// Peer validation failure
@@ -252,7 +255,7 @@ private SecretKey t13DeriveKey(String type)
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
ka.init(localPrivateKey);
ka.doPhase(peerPublicKey, true);
- sharedSecret = ka.generateSecret("TlsPremasterSecret");
+ sharedSecret = ka.generateSecret(t13KeyDerivationAlgorithm);
}
return deriveHandshakeSecret(type, sharedSecret);
diff --git a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java
index 74975fc1e5b..b5f1eff179c 100644
--- a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java
+++ b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -660,7 +660,7 @@ private void handleHandshakeMessage(KeySpace keySpace, ByteBuffer message)
}
Alert alert = ((QuicEngineOutputRecord)
conContext.outputRecord).getAlert();
- throw new QuicTransportException(alert.description, keySpace, 0,
+ throw new QuicTransportException(e.getMessage(), keySpace, 0,
BASE_CRYPTO_ERROR + alert.id, e);
} catch (IOException e) {
throw new RuntimeException(e);
diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
index ac43b22a3bd..76b383c03e1 100644
--- a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
+++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
@@ -105,6 +105,9 @@ public class LocaleResources {
// TimeZoneNamesBundle exemplar city prefix
private static final String TZNB_EXCITY_PREFIX = "timezone.excity.";
+ // TimeZoneNamesBundle explicit metazone dst offset prefix
+ private static final String TZNB_METAZONE_DSTOFFSET_PREFIX = "metazone.dstoffset.";
+
// null singleton cache value
private static final Object NULLOBJECT = new Object();
@@ -321,7 +324,8 @@ public Object getTimeZoneNames(String key) {
if (Objects.isNull(data) || Objects.isNull(val = data.get())) {
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
- if (key.startsWith(TZNB_EXCITY_PREFIX)) {
+ if (key.startsWith(TZNB_EXCITY_PREFIX) ||
+ key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) {
if (tznb.containsKey(key)) {
val = tznb.getString(key);
assert val instanceof String;
@@ -378,7 +382,8 @@ String[][] getZoneStrings() {
Set value = new LinkedHashSet<>();
Set tzIds = new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs()));
for (String key : keyset) {
- if (!key.startsWith(TZNB_EXCITY_PREFIX)) {
+ if (!key.startsWith(TZNB_EXCITY_PREFIX) &&
+ !key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) {
value.add(rb.getStringArray(key));
tzIds.remove(key);
}
diff --git a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java
index fd3d4965db3..6c684e176c8 100644
--- a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java
+++ b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java
@@ -37,7 +37,7 @@
import java.util.spi.TimeZoneNameProvider;
import sun.util.calendar.ZoneInfo;
import sun.util.cldr.CLDRLocaleProviderAdapter;
-import static sun.util.locale.provider.LocaleProviderAdapter.Type;
+import static sun.util.locale.provider.LocaleProviderAdapter.Type.CLDR;
/**
* Utility class that deals with the localized time zone names
@@ -169,10 +169,22 @@ public static Optional convertLDMLShortID(String shortID) {
* Returns the canonical ID for the given ID
*/
public static Optional canonicalTZID(String id) {
- return ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR))
+ return ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(CLDR))
.canonicalTZID(id);
}
+ /**
+ * {@return the explicit metazone DST offset for the specified time zone ID, if exists}
+ * @param tzid the time zone ID
+ */
+ public static String explicitDstOffset(String tzid) {
+ return (String) (LocaleProviderAdapter.forType(CLDR) instanceof CLDRLocaleProviderAdapter ca ?
+ ca.getLocaleResources(Locale.ROOT)
+ .getTimeZoneNames("metazone.dstoffset." +
+ ca.canonicalTZID(tzid).orElse(tzid)) :
+ null);
+ }
+
private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
diff --git a/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java b/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java
index a30b84c6872..c5e95c8a404 100644
--- a/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java
+++ b/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,8 +43,6 @@
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
-import java.util.MissingResourceException;
-import java.util.Objects;
import java.util.Set;
/**
diff --git a/src/java.desktop/share/classes/java/awt/MouseInfo.java b/src/java.desktop/share/classes/java/awt/MouseInfo.java
index 6b913adf06e..7a30243b06c 100644
--- a/src/java.desktop/share/classes/java/awt/MouseInfo.java
+++ b/src/java.desktop/share/classes/java/awt/MouseInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@ private MouseInfo() {
*
* @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true
* @return location of the mouse pointer
+ * @see GraphicsConfiguration
* @since 1.5
*/
public static PointerInfo getPointerInfo() throws HeadlessException {
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java
index 5e2384dce27..3fc013b4fde 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -203,6 +203,9 @@ private void unregisterConnFromEndpoint() {
// an endpoint has been established (which is OK)
return;
}
+ // close the connection ID managers; any in-flight connection ID changes should be ignored.
+ connection.localConnectionIdManager().close();
+ connection.peerConnectionIdManager().close();
endpoint.removeConnection(this.connection);
}
@@ -434,6 +437,9 @@ private void pushConnectionCloseFrame(final KeySpace keySpace,
final QuicPacket packet = connection.newQuicPacket(keySpace, List.of(toSend));
final ProtectionRecord protectionRecord = ProtectionRecord.single(packet,
connection::allocateDatagramForEncryption);
+ // close the connection ID managers; any in-flight connection ID changes should be ignored.
+ connection.localConnectionIdManager().close();
+ connection.peerConnectionIdManager().close();
// while sending the packet containing the CONNECTION_CLOSE frame, the pushDatagram will
// remap the QuicConnectionImpl in QuicEndpoint.
connection.pushDatagram(protectionRecord);
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java
index 2bc759a920a..0646026e28b 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java
@@ -65,6 +65,7 @@ final class PeerConnIdManager {
private final QuicConnectionImpl connection;
private final String logTag;
private final boolean isClient;
+ private boolean closed; // when true, no more reset tokens are registered
private enum State {
INITIAL_PKT_NOT_RECEIVED_FROM_PEER,
@@ -267,6 +268,7 @@ void handshakeStatelessResetToken(final byte[] statelessResetToken) {
if (handshakeConnId == null) {
throw new IllegalStateException("No handshake peer connection available");
}
+ if (closed) return;
// recreate the conn id with the stateless token
this.peerConnectionIds.put(0L, new PeerConnectionId(handshakeConnId.asReadOnlyBuffer(),
statelessResetToken));
@@ -283,6 +285,10 @@ void handshakeStatelessResetToken(final byte[] statelessResetToken) {
public List activeResetTokens() {
lock.lock();
try {
+ // this method is currently only used to remove a connection from the endpoint
+ // after the connection is closed.
+ // The below assert can be removed if the method is needed elsewhere.
+ assert closed;
// we only support one active connection ID at the time
PeerConnectionId cid = peerConnectionIds.get(activeConnIdSeq);
byte[] statelessResetToken = null;
@@ -305,7 +311,7 @@ public List activeResetTokens() {
QuicConnectionId getPeerConnId() {
lock.lock();
try {
- if (activeConnIdSeq < largestReceivedRetirePriorTo) {
+ if (activeConnIdSeq < largestReceivedRetirePriorTo && !closed) {
// stop using the old connection ID
switchConnectionId();
}
@@ -496,9 +502,11 @@ private void retirePriorTo(final long priorTo) {
// connection ids. It does however store the peer-issued stateless reset token of a
// peer connection id, so we let the endpoint know that the stateless reset token needs
// to be forgotten since the corresponding peer connection id is being retired
- final byte[] resetTokenToForget = entry.getValue().getStatelessResetToken();
- if (resetTokenToForget != null) {
- this.connection.endpoint().forgetStatelessResetToken(resetTokenToForget);
+ if (seqNumToRetire == activeConnIdSeq) {
+ final byte[] resetTokenToForget = entry.getValue().getStatelessResetToken();
+ if (resetTokenToForget != null) {
+ this.connection.endpoint().forgetStatelessResetToken(resetTokenToForget);
+ }
}
}
for (Iterator iterator = gaps.iterator(); iterator.hasNext(); ) {
@@ -540,4 +548,13 @@ public QuicFrame nextFrame(int remaining) {
lock.unlock();
}
}
+
+ public void close() {
+ lock.lock();
+ try {
+ closed = true;
+ } finally {
+ lock.unlock();
+ }
+ }
}
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java
index 41b814a551c..b13d49ead7d 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java
@@ -1758,6 +1758,10 @@ LocalConnIdManager localConnectionIdManager() {
return localConnIdManager;
}
+ PeerConnIdManager peerConnectionIdManager() {
+ return peerConnIdManager;
+ }
+
/**
* {@return the local connection id}
*/
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java
index ef342d4cb56..3dee814e1f1 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java
@@ -1532,12 +1532,16 @@ public void pushClosedDatagram(QuicConnectionImpl connection,
*/
void removeConnection(final QuicPacketReceiver connection) {
if (debug.on()) debug.log("removing connection " + connection);
- // remove the connection completely
- connection.connectionIds().forEach(connections::remove);
- assert !connections.containsValue(connection) : connection;
// remove references to this connection from the map which holds the peer issued
// reset tokens
dropPeerIssuedResetTokensFor(connection);
+ // remove the connection completely
+ connection.connectionIds().forEach(connections::remove);
+ assert !connections.containsValue(connection) : connection;
+ // Check that if there are no connections, there are no reset tokens either.
+ // This is safe because connections are added before reset tokens and removed after,
+ // except when we're closing the endpoint and don't bother with removing tokens.
+ assert peerIssuedResetTokens.isEmpty() || !connections.isEmpty() || closed : peerIssuedResetTokens;
}
/**
@@ -1587,7 +1591,6 @@ public void draining(final QuicConnectionImpl connection) {
if (closed) return;
final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO
- connection.localConnectionIdManager().close();
DrainingConnection draining = new DrainingConnection(connection.connectionIds(),
connection.activeResetTokens(), idleTimeout);
// we can ignore stateless reset in the draining state.
@@ -1626,7 +1629,6 @@ protected void closing(QuicConnectionImpl connection, ByteBuffer datagram) {
closingDatagram.flip();
final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO
- connection.localConnectionIdManager().close();
var closingConnection = new ClosingConnection(connection.connectionIds(),
connection.activeResetTokens(), idleTimeout, datagram);
remapPeerIssuedResetToken(closingConnection);
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java
index 9e441cf7873..df8c229a000 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -128,6 +128,9 @@ private static IOException toReportedCause(final Throwable original,
? new IOException("connection terminated")
: new IOException(fallbackExceptionMsg);
} else if (original instanceof QuicTransportException qte) {
+ if (qte.getCause() instanceof IOException ioe) {
+ return ioe;
+ }
return new IOException(qte.getMessage());
} else if (original instanceof IOException ioe) {
return ioe;
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java b/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java
index d0afd126bf6..4eb6d12fd38 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java
@@ -83,6 +83,7 @@ default long getStartPosition(CompilationUnitTree file, DocCommentTree comment,
* @param comment the comment tree that encloses the tree for which the
* position is being sought
* @param tree tree for which a position is sought
+ * @since 27
*/
long getStartPosition(DocCommentTree comment, DocTree tree);
@@ -146,6 +147,7 @@ default long getEndPosition(CompilationUnitTree file, DocCommentTree comment, Do
* @param comment the comment tree that encloses the tree for which the
* position is being sought
* @param tree tree for which a position is sought
+ * @since 27
*/
long getEndPosition(DocCommentTree comment, DocTree tree);
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java b/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java
index 460f4f2a1ce..3ff6fafe58b 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java
@@ -74,6 +74,7 @@ default long getStartPosition(CompilationUnitTree file, Tree tree) {
*
*
* @param tree tree for which a position is sought
+ * @since 27
*/
long getStartPosition(Tree tree);
@@ -130,6 +131,7 @@ default long getEndPosition(CompilationUnitTree file, Tree tree) {
*
*
* @param tree tree for which a position is sought
+ * @since 27
*/
long getEndPosition(Tree tree);
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
index 8eba79c7480..452d15ed219 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
@@ -154,6 +154,14 @@ public void organizeTypeAnnotationsSignaturesForLocalVarType(final Env env1 = env;
+ while (env1 != null && !env1.tree.hasTag(Tag.CLASSDEF)) {
+ if (env1.tree instanceof JCLambda l) {
+ pos.currentLambda = l;
+ break;
+ }
+ env1 = env1.next;
+ }
pos.scan(tree);
} finally {
log.useSource(oldSource);
@@ -472,11 +480,12 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type,
Assert.check(tc.position == pos);
}
- if (type.hasTag(TypeTag.ARRAY))
- return rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos);
+ Type ret;
- if (type.hasTag(TypeTag.TYPEVAR)) {
- return type.annotatedType(onlyTypeAnnotations);
+ if (type.hasTag(TypeTag.ARRAY)) {
+ ret = rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos);
+ } else if (type.hasTag(TypeTag.TYPEVAR)) {
+ ret = type.annotatedType(onlyTypeAnnotations);
} else if (type.getKind() == TypeKind.UNION) {
// There is a TypeKind, but no TypeTag.
UnionClassType ut = (UnionClassType) type;
@@ -487,7 +496,7 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type,
ListBuffer alternatives = new ListBuffer<>();
alternatives.add(res);
alternatives.addAll(ut.alternatives_field.tail);
- return new UnionClassType((ClassType) ut.getLub(), alternatives.toList());
+ ret = new UnionClassType((ClassType) ut.getLub(), alternatives.toList());
} else {
Type enclTy = type;
Element enclEl = type.asElement();
@@ -567,10 +576,10 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type,
pos.location = pos.location.appendList(depth.toList());
}
- Type ret = typeWithAnnotations(type, enclTy, annotations);
- typetree.type = ret;
- return ret;
+ ret = typeWithAnnotations(type, enclTy, annotations);
}
+ typetree.type = ret;
+ return ret;
}
/**
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java
index 09b64efbdd0..1e875691537 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -181,9 +181,9 @@ private boolean updatePaths() throws IOException, DirectoryIteratorException {
List added = new ArrayList<>();
Set current = new HashSet<>();
for (Path p : dirStream) {
- if (!pathLookup.containsKey(p)) {
- String s = p.toString();
- if (s.endsWith(".jfr")) {
+ String s = p.toString();
+ if (s.endsWith(".jfr")) {
+ if (!pathLookup.containsKey(p)) {
added.add(p);
Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "New file found: " + p.toAbsolutePath());
}
diff --git a/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java b/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java
index 8ce7c03f62d..9ed1069155b 100644
--- a/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java
+++ b/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -117,11 +117,8 @@ public interface HotSpotDiagnosticMXBean extends PlatformManagedObject {
* does not exist.
*
* When the format is specified as {@link ThreadDumpFormat#JSON JSON}, the
- * thread dump is generated in JavaScript Object Notation.
- * threadDump.schema.json
- * describes the thread dump format in draft
- *
- * JSON Schema Language version 2.
+ * thread dump is generated as JSON text in the
+ * JSON Thread Dump Format.
*
*
The thread dump will include output for all platform threads. It may
* include output for some or all virtual threads.
diff --git a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.html b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.html
new file mode 100644
index 00000000000..f7dd84d4f32
--- /dev/null
+++ b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.html
@@ -0,0 +1,283 @@
+
+
+
+
+ JSON Thread Dump Format
+
+
+
+JSON Thread Dump Format
+
+The JSON text is an object with a member named "threadDump" that is an object with the
+following members:
+
+
+
+
+ Name
+ Data type
+ Optional
+ Description
+
+
+
+
+ formatVersion
+ number
+ no
+ The format version. Its value is 2.
+
+
+ processId
+ number
+ no
+ The process identifier (pid) of the VM that generated the thread dump.
+
+
+ time
+ string
+ no
+ The timestamp, in ISO 8601 format, when the thread dump was generated.
+
+
+ runtimeVersion
+ string
+ no
+ The {@linkplain java.lang.Runtime.Version runtime version} of the VM that
+ generated the thread dump.
+
+
+ threadContainers
+ array
+ no
+ The array of thread "groupings". The first element is the root grouping/container
+ with name "<root>".
+
+
+
+
+ Each element in the "threadContainers" array is an object that represents a
+ grouping/container of threads with the following members:
+
+
+
+
+ Name
+ Data type
+ Optional
+ Description
+
+
+
+
+ container
+ string
+ no
+ The thread container name. The name is unique.
+
+
+ parent
+ string or null
+ no
+ The thread container parent's name or {@code null} for the root container.
+
+
+ owner
+ number or null
+ no
+ The {@linkplain java.lang.Thread#threadId() thread identifier} of the thread
+ that owns the thread container or {@code null} if no owner.
+
+
+ threads
+ array
+ no
+ The array of threads in the thread grouping/container.
+
+
+ threadCount
+ number
+ yes
+ The number of threads in the thread grouping/container. If the thread dump
+ includes all virtual threads then this count is the same as the number of
+ elements in the threads array. If all virtual threads are not included then
+ it may be larger than the number of elements in the threads array.
+
+
+
+
+ Each element in a "threads" array is an object with the following members:
+
+
+
+
+ Name
+ Data type
+ Optional
+ Description
+
+
+
+
+ tid
+ number
+ no
+ The {@linkplain java.lang.Thread#threadId() thread identifier}.
+
+
+ time
+ string
+ no
+ The timestamp, in ISO 8601 format, when the thread was sampled.
+
+
+ name
+ string
+ no
+ The {@linkplain java.lang.Thread#getName() thread name}.
+
+
+ state
+ string
+ no
+ The string representation of the {@linkplain java.lang.Thread#getState() thread state}.
+
+
+ virtual
+ boolean
+ yes
+ {@code true} if the thread is a {@linkplain java.lang.Thread#isVirtual() virtual thread}.
+
+
+ carrier
+ number
+ yes
+ The thread identifier of the carrier thread when this thread is a mounted
+ virtual thread.
+
+
+ stack
+ array
+ no
+ The thread stack. The elements in the array are of type string with the string
+ representation of a {@linkplain java.lang.StackTraceElement stack trace element}.
+ If the thread stack has one or more elements, then the first element is the top
+ of the stack.
+
+
+ parkBlocker
+ object
+ yes
+ The object responsible for the thread parking. Its members identify the
+ blocker object, and the exclusive owner thread if owned.
+
+
+ blockedOn
+ string
+ yes
+ The {@linkplain java.util.Objects#toIdentityString(Object) identity string} of the
+ object that the thread is blocked on waiting to enter/re-enter a synchronization
+ method or block.
+
+
+ waitingOn
+ string
+ yes
+ The {@linkplain java.util.Objects#toIdentityString(Object) identity string}
+ of the object that the thread is {@linkplain java.lang.Object#wait() waiting}
+ to be notified.
+
+
+ monitorsOwned
+ array
+ yes
+ The objects for which a monitor is owned by the thread.
+
+
+
+
+ A "parkBlocker" object has the following members:
+
+
+
+
+ Name
+ Data type
+ Optional
+ Description
+
+
+
+
+ object
+ string
+ no
+ The {@linkplain java.util.Objects#toIdentityString(Object) identity string}
+ of the {@linkplain java.util.concurrent.locks.LockSupport#park(Object) blocker
+ object} responsible for the thread parking.
+
+
+ owner
+ number
+ yes
+ The thread identifier of the exclusive owner thread when the parkBlocker is
+ an {@link java.util.concurrent.locks.AbstractOwnableSynchronizer}.
+
+
+
+
+ Each element in a "monitorsOwned" array is an object with the following members:
+
+
+
+
+ Name
+ Data type
+ Optional
+ Description
+
+
+
+
+ depth
+ number
+ no
+ The stack depth at which the monitors are owned.
+
+
+ locks
+ array
+ no
+ The elements of the array are of type string or null. An element of type string
+ has a value that is the {@linkplain java.util.Objects#toIdentityString(Object)
+ identity string} of the object for which the monitor is owned by the thread.
+ The element is of type null when the object has been eliminated.
+
+
+
+
+
+
diff --git a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json
deleted file mode 100644
index 30161b0bb74..00000000000
--- a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json
+++ /dev/null
@@ -1,169 +0,0 @@
-{
- "type": "object",
- "properties": {
- "threadDump": {
- "type": "object",
- "properties": {
- "formatVersion": {
- "type": "integer",
- "description": "Format version (2)."
- },
- "processId": {
- "type": "integer",
- "description": "The native process id of the Java virtual machine."
- },
- "time": {
- "type": "string",
- "description": "The time in ISO 8601 format when the thread dump was generated."
- },
- "runtimeVersion": {
- "type": "string",
- "description": "The runtime version, see java.lang.Runtime.Version"
- },
- "threadContainers": {
- "type": "array",
- "description": "The array of thread containers (thread groupings).",
- "items": [
- {
- "type": "object",
- "properties": {
- "container": {
- "type": "string",
- "description": "The container name. The container name is unique."
- },
- "parent": {
- "type": [ "string", "null" ],
- "description": "The parent container name or null for the root container."
- },
- "owner": {
- "type": [ "integer", "null" ],
- "description": "The thread identifier of the owner thread or null if no owner."
- },
- "threads": {
- "type": "array",
- "description": "The array of threads in the thread container.",
- "items": [
- {
- "type": "object",
- "properties": {
- "tid": {
- "type": "integer",
- "description": "The thread identifier."
- },
- "time": {
- "type": "string",
- "description": "The time in ISO 8601 format that the thread was sampled."
- },
- "name": {
- "type": "string",
- "description": "The thread name."
- },
- "state": {
- "type": "string",
- "description": "The thread state (Thread::getState)."
- },
- "virtual" : {
- "type": "boolean",
- "description": "true for a virtual thread."
- },
- "parkBlocker": {
- "type": "object",
- "properties": {
- "object": {
- "type": "string",
- "description": "The blocker object responsible for the thread parking."
- }
- },
- "owner": {
- "type": "integer",
- "description": "The thread identifier of the owner when the parkBlocker is an AbstractOwnableSynchronizer."
- },
- "required": [
- "object"
- ]
- },
- "blockedOn": {
- "type": "string",
- "description": "The object that the thread is blocked on waiting to enter/re-enter a synchronization block/method."
- },
- "waitingOn": {
- "type": "string",
- "description": "The object that the thread is waiting to be notified (Object.wait)."
- },
- "stack": {
- "type": "array",
- "description": "The thread stack. The first element is the top of the stack.",
- "items": [
- {
- "type": "string",
- "description": "A stack trace element (java.lang.StackTraceElement)."
- }
- ]
- },
- "monitorsOwned": {
- "type": "array",
- "description": "The objects for which monitors are owned by the thread.",
- "items": {
- "type": "object",
- "properties": {
- "depth": {
- "type": "integer",
- "description": "The stack depth at which the monitors are owned."
- },
- "locks": {
- "type": "array",
- "items": {
- "type": [ "string", "null" ],
- "description": "The object for which the monitor is owned by the thread, null if eliminated."
- }
- }
- },
- "required": [
- "depth",
- "locks"
- ]
- }
- },
- "carrier": {
- "type": "integer",
- "description": "The thread identifier of the carrier thread if mounted."
- }
- },
- "required": [
- "tid",
- "time",
- "name",
- "state",
- "stack"
- ]
- }
- ]
- },
- "threadCount": {
- "type": "integer",
- "description": "The number of threads in the thread container."
- }
- },
- "required": [
- "container",
- "parent",
- "owner",
- "threads"
- ]
- }
- ]
- }
- },
- "required": [
- "formatVersion",
- "processId",
- "time",
- "runtimeVersion",
- "threadContainers"
- ]
- }
- },
- "required": [
- "threadDump"
- ]
-}
diff --git a/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java b/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java
index a7935928cab..3b2451cb740 100644
--- a/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java
+++ b/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
* @bug 8304954
* @summary Code cache reservation should gracefully downgrade to using smaller pages if the code cache size is too small to host the requested page size.
* @requires os.family == "linux"
- * @requires vm.gc != "Z"
+ * @requires vm.flagless
* @library /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
diff --git a/test/hotspot/jtreg/compiler/cpuflags/CPUFeaturesClearTest.java b/test/hotspot/jtreg/compiler/cpuflags/CPUFeaturesClearTest.java
new file mode 100644
index 00000000000..a0fb3525381
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/cpuflags/CPUFeaturesClearTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @library /test/lib /
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64"
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm/timeout=600 -Xbootclasspath/a:.
+ * -XX:+UnlockDiagnosticVMOptions
+ * -XX:+WhiteBoxAPI -Xbatch
+ * compiler.cpuflags.CPUFeaturesClearTest
+ */
+
+package compiler.cpuflags;
+
+import java.util.List;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.whitebox.WhiteBox;
+import jdk.test.whitebox.cpuinfo.CPUInfo;
+import static jdk.test.lib.cli.CommandLineOptionTest.*;
+
+public class CPUFeaturesClearTest {
+ private static List cpuFeaturesList;
+ public void runTestCases() throws Throwable {
+ if (Platform.isX64()) {
+ testX86Flags();
+ } else if (Platform.isAArch64()) {
+ testAArch64Flags();
+ }
+ }
+
+ String[] generateArgs(String vmFlag) {
+ String[] args = {"-Xlog:os+cpu", "-XX:+UnlockDiagnosticVMOptions", vmFlag, "-version"};
+ return args;
+ }
+
+ public void testX86Flags() throws Throwable {
+ OutputAnalyzer outputAnalyzer;
+ String vmFlagsToTest[] = {"UseCLMUL", "UseAES", "UseFMA", "UseSHA"};
+ String cpuFeatures[] = {"clmul", "aes", "fma", "sha"};
+ for (int i = 0; i < vmFlagsToTest.length; i++) {
+ String vmFlag = vmFlagsToTest[i];
+ String cpuFeature = cpuFeatures[i];
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareBooleanFlag(vmFlag, false)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* " + cpuFeatures[i] + ".*");
+ }
+ if (isCpuFeatureSupported("sse4")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 3)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse4.*");
+ }
+ if (isCpuFeatureSupported("sse3")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 2)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse3.*");
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* ssse3.*");
+ }
+ if (isCpuFeatureSupported("sse2")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 1)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse2.*");
+ }
+ if (isCpuFeatureSupported("sse")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 0)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse.*");
+ }
+ if (isCpuFeatureSupported("avx512f")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseAVX", 2)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* avx512.*");
+ }
+ if (isCpuFeatureSupported("avx2")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseAVX", 1)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* avx2.*");
+ }
+ if (isCpuFeatureSupported("avx")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseAVX", 0)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* avx.*");
+ }
+ }
+
+ public void testAArch64Flags() throws Throwable {
+ OutputAnalyzer outputAnalyzer;
+ String vmFlagsToTest[] = {"UseCRC32", "UseLSE", "UseAES"};
+ String cpuFeatures[] = {"crc32", "lse", "aes"};
+ for (int i = 0; i < vmFlagsToTest.length; i++) {
+ String vmFlag = vmFlagsToTest[i];
+ String cpuFeature = cpuFeatures[i];
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareBooleanFlag(vmFlag, false)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* " + cpuFeatures[i] + ".*");
+ }
+
+ // Disabling UseSHA should clear all shaXXX cpu features
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareBooleanFlag("UseSHA", false)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha1.*");
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha256.*");
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha3.*");
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha512.*");
+
+ if (isCpuFeatureSupported("sve2")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSVE", 1)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sve2.*");
+ verifyOutput(null, new String[]{"sve2"}, null, outputAnalyzer);
+ }
+ if (isCpuFeatureSupported("sve")) {
+ outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSVE", 0)));
+ outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sve.*");
+ verifyOutput(null, new String[]{"sve"}, null, outputAnalyzer);
+ }
+ }
+
+ static boolean isCpuFeatureSupported(String feature) {
+ return cpuFeaturesList.contains(feature);
+ }
+
+ public static void main(String args[]) throws Throwable {
+ cpuFeaturesList = CPUInfo.getFeatures();
+ new CPUFeaturesClearTest().runTestCases();
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java b/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java
index 9542e48e54e..60185fa91ad 100644
--- a/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java
+++ b/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
* @key stress randomness
* @bug 8252219 8256535 8325478
* @requires vm.compiler2.enabled
+ * @requires vm.flagless
* @summary Tests that using a stress option without -XX:StressSeed=N generates
* and logs a random seed.
* @library /test/lib /
diff --git a/test/hotspot/jtreg/compiler/debug/TestStressCM.java b/test/hotspot/jtreg/compiler/debug/TestStressCM.java
index 0fb624bc16f..a6065fa4d09 100644
--- a/test/hotspot/jtreg/compiler/debug/TestStressCM.java
+++ b/test/hotspot/jtreg/compiler/debug/TestStressCM.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -48,7 +48,7 @@ static String cmTrace(String stressOpt, int stressSeed) throws Exception {
"-XX:CompileOnly=" + className + "::sum",
"-XX:+TraceOptoPipelining", "-XX:+" + stressOpt,
"-XX:StressSeed=" + stressSeed, className, "10"};
- ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(procArgs);
+ ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(procArgs);
OutputAnalyzer out = new OutputAnalyzer(pb.start());
out.shouldHaveExitValue(0);
// Extract the trace of our method (the last one after those of all
diff --git a/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java b/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java
index 89da61a5763..1e76887732f 100644
--- a/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java
+++ b/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,7 @@ public static void main(String[] args) throws Exception {
ProcessBuilder pb;
OutputAnalyzer out;
- pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xcomp", "-XX:+IgnoreUnrecognizedVMOptions",
+ pb = ProcessTools.createTestJavaProcessBuilder("-Xcomp", "-XX:+IgnoreUnrecognizedVMOptions",
"-XX:+VerifyAdapterSharing", "-version");
out = new OutputAnalyzer(pb.start());
out.shouldHaveExitValue(0);
diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestArrayCopyEliminationUncRematerialization.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestArrayCopyEliminationUncRematerialization.java
new file mode 100644
index 00000000000..a2a0a1a05f6
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestArrayCopyEliminationUncRematerialization.java
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8370416
+ * @key randomness
+ * @summary Ensure that rematerialization loads for a scalarized arraycopy destination use the correct control and memory state.
+ * @library /test/lib /
+ * @run driver ${test.main.class}
+ */
+
+package compiler.escapeAnalysis;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+import jdk.test.lib.Utils;
+import compiler.lib.compile_framework.*;
+import compiler.lib.template_framework.Template;
+import compiler.lib.template_framework.Template.ZeroArgs;
+import compiler.lib.template_framework.TemplateToken;
+import static compiler.lib.template_framework.Template.scope;
+import static compiler.lib.template_framework.Template.let;
+import compiler.lib.template_framework.library.CodeGenerationDataNameType;
+import compiler.lib.template_framework.library.Hooks;
+import compiler.lib.template_framework.library.PrimitiveType;
+import compiler.lib.template_framework.library.TestFrameworkClass;
+
+
+public class TestArrayCopyEliminationUncRematerialization {
+ private static final Random RANDOM = Utils.getRandomInstance();
+ private static final String PACKAGE = "compiler.escapeAnalysis.templated";
+ private static final String CLASS_NAME = "TestArrayCopyEliminationUncRematerializationGenerated";
+
+ // This generates a test containing test methods of the form
+ // static int testStore(int[] src, boolean flag) {
+ // int[] dst = new int[COPY_LEN];
+ // System.arraycopy(src, 0, dst, 0, COPY_LEN);
+ // src[WRITE_IDX] = WRITE_VAL_I; // Pollute the element in the source array corresponding to the returned element in dst.
+ // if (flag) { // Compiles to unstable if trap when called exclusively with flag = false.
+ // dst[0] = (byte) 0x7f;
+ // }
+ // return dst[RETURN_IDX]; // Corresponds to src[WRITE_IDX]
+ // }
+ // for all primitive types except boolean and for different methods of polluting the source
+ // array and producing an unstable if trap.
+ // The templates below generate an IR test that validates that as many rematerialization loads
+ // as possible are placed in the uncommon path and that the returned result is in fact from
+ // the source array.
+ // Between different runs of the test, it will generate different sized arrays and indices
+ // to read from and store to (see TestConfig below). Further, this generates a variant of the
+ // test method, where the offset into src is provided in an argument. C2 cannot put any rematerialization
+ // loads in the uncommon path then, but it is useful for checking the correct result.
+ public static void main(String[] args) {
+ final CompileFramework comp = new CompileFramework();
+
+ comp.addJavaSourceCode(PACKAGE + "." + CLASS_NAME, generate(comp));
+
+ comp.compile();
+
+ // Ensure consistent results for the node counts in the arraycopy subtests.
+ comp.invoke(PACKAGE + "." + CLASS_NAME, "main", new Object[] { new String[] { } });
+ }
+
+ private record TestConfig(int srcSize, int copyLen, int copyIdx, int writeIdx, int returnIdx) {
+ static TestConfig make() {
+ int copyLen = RANDOM.nextInt(10, 64); // 64 is the default value for -XX:EliminateAllocationArraySizeLimit.
+ int srcSize = RANDOM.nextInt(copyLen + 20, 1000);
+ int copyIdx = RANDOM.nextInt(1, srcSize - copyLen); // The index we start arraycopying src from.
+ int returnIdx = RANDOM.nextInt(0, copyLen); // The index where dst returns from. Must correspond to writeIdx in src.
+ int writeIdx = copyIdx + returnIdx; // The index we write to in src.
+
+ return new TestConfig(srcSize, copyLen, copyIdx, writeIdx, returnIdx);
+ }
+
+ public TemplateToken constDefinitions() {
+ return Template.make(() -> scope(
+ Hooks.CLASS_HOOK.insert(scope(
+ String.format("private static final int SRC_SIZE = %d;\n", srcSize),
+ String.format("private static final int COPY_LEN = %d;\n", copyLen),
+ String.format("private static final int COPY_IDX = %d;\n", copyIdx),
+ String.format("private static final int WRITE_IDX = %d;\n", writeIdx),
+ String.format("private static final int RETURN_IDX = %d;\n", returnIdx)
+ )))).asToken();
+ }
+
+ public int copyEnd() {
+ return copyIdx + copyLen;
+ }
+ }
+
+ private static String generate(CompileFramework comp) {
+ TestConfig config = TestConfig.make();
+
+ final List tests = new ArrayList<>();
+ tests.add(config.constDefinitions());
+ tests.add(PrimitiveType.generateLibraryRNG());
+
+ // Generate all testcases for all primitive types except boolean.
+ tests.addAll(CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES
+ .stream()
+ .map(pty -> new TestsPerType(pty).generate(config))
+ .collect(Collectors.toList()));
+
+ final Set imports = Set.of("java.lang.foreign.MemorySegment",
+ "java.lang.foreign.ValueLayout",
+ "java.lang.invoke.MethodHandles",
+ "java.lang.invoke.VarHandle",
+ "java.util.Arrays",
+ "java.util.Random",
+ "jdk.test.lib.Asserts",
+ "jdk.test.lib.Utils",
+ "compiler.lib.generators.*");
+
+ return TestFrameworkClass.render(PACKAGE, CLASS_NAME, imports, comp.getEscapedClassPathOfCompiledClasses(), tests);
+ }
+
+ record TestsPerType(PrimitiveType pty) {
+ private record TestTemplates(ZeroArgs store, ZeroArgs trap, ZeroArgs prelude) {
+ TestTemplates(ZeroArgs store, ZeroArgs trap) {
+ this(store, trap, Template.make(() -> scope("")));
+ }
+ }
+
+ TemplateToken generate(TestConfig config) {
+ final String srcArray = "src" + pty.abbrev();
+ final String handle = pty.name().toUpperCase() + "_ARR";
+
+ var runTestConst = Template.make("testName", (String testName) -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("srcField", srcArray),
+ """
+ @Run(test = "test#{testName}")
+ static void run#{testName}(RunInfo info) {
+ Arrays.fill(#{srcField}, SRC_VAL_#{typeAbbrev});
+ #type res = test#{testName}(#{srcField}, #{srcField}, info.isWarmUp());
+ Asserts.assertEQ((#type) SRC_VAL_#{typeAbbrev}, res, "Wrong result from " + info.getTest().getName() + " with flag " + info.isWarmUp());
+ }
+ """
+ ));
+
+ var runTestIdx = Template.make("testName", (String testName) -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("srcField", srcArray),
+ """
+ @Run(test = "test#{testName}")
+ static void run#{testName}(RunInfo info) {
+ Arrays.fill(#{srcField}, SRC_VAL_#{typeAbbrev});
+ #type res = test#{testName}(#{srcField}, COPY_IDX, info.isWarmUp());
+ Asserts.assertEQ((#type) SRC_VAL_#{typeAbbrev}, res, "Wrong result from " + info.getTest().getName() + " with flag " + info.isWarmUp());
+ }
+ """
+ ));
+
+ var testMethodConst = Template.make("testName", "tmp", (String TestName, TestTemplates templates) -> scope(
+ let("type", pty),
+ """
+ static #type test#{testName}(#type[] src, #type[] alias, boolean flag) {
+ """,
+ templates.prelude.asToken(),
+ """
+ #type[] dst = new #type[COPY_LEN];
+ System.arraycopy(src, COPY_IDX, dst, 0, COPY_LEN);
+ """,
+ templates.store.asToken(),
+ templates.trap.asToken(),
+ """
+ return dst[RETURN_IDX];
+ }
+ """
+ ));
+
+ var testMethodIdx = Template.make("testName", "tmp", (String TestName, TestTemplates templates) -> scope(
+ let("type", pty),
+ """
+ static #type test#{testName}(#type[] src, int idx, boolean flag) {
+ """,
+ templates.prelude.asToken(),
+ """
+ #type[] dst = new #type[COPY_LEN];
+ System.arraycopy(src, idx, dst, 0, COPY_LEN);
+ """,
+ templates.store.asToken(),
+ templates.trap.asToken(),
+ """
+ return dst[RETURN_IDX];
+ }
+ """
+ ));
+
+ var testMethodClone = Template.make("testName", "tmp", (String testName, TestTemplates templates) -> scope(
+ let("type", pty),
+ """
+ static #type test#{testName}(#type[] realSrc, #type[] alias, boolean flag) {
+ """,
+ templates.prelude.asToken(),
+ """
+ // Set up a src array with statically known length, so scalar replacement works.
+ #type[] src = new #type[COPY_LEN];
+ System.arraycopy(realSrc, COPY_IDX, src, 0, COPY_LEN);
+
+ // Clone the src array into a dst array to get the clonebasic variant of the ArraycopyNode.
+ #type[] dst = src.clone();
+ """,
+ templates.store.asToken(),
+ templates.trap.asToken(),
+ """
+ return dst[RETURN_IDX];
+ }
+ """
+ ));
+
+ // For methods with a constant offset into src, validate that only the necessary rematerialization nodes are
+ // in the common path.
+ var testCaseConst = Template.make("testName", "loadCount", "tmp", (String testName, Integer loadCount, TestTemplates templates) -> scope(
+ let("typeAbbrev", pty.abbrev().equals("C") ? "US" : pty.abbrev()),
+ runTestConst.asToken(testName),
+ """
+ @Test
+ @IR(counts = { IRNode.LOAD_#{typeAbbrev}, "=#{loadCount}" },
+ applyIf = { "TieredCompilation", "true"})
+ """,
+ testMethodConst.asToken(testName, templates)
+ ));
+
+ var testCaseConstPlusOne = Template.make("testName", "loadCount", "tmp", (String testName, Integer loadCount, TestTemplates templates) -> scope(
+ let("typeAbbrev", pty.abbrev().equals("C") ? "US" : pty.abbrev()),
+ runTestConst.asToken(testName),
+ let("countPlusOne", loadCount + 1),
+ """
+ @Test
+ @IR(counts = { IRNode.LOAD_#{typeAbbrev}, ">=#{loadCount}",
+ IRNode.LOAD_#{typeAbbrev}, "<=#{countPlusOne}" },
+ applyIf = { "TieredCompilation", "true"})
+ """,
+ testMethodConst.asToken(testName, templates)
+ ));
+
+ // Some test cases can not be reliably verified due to varying numbers of loads generated from run to run.
+ var testCaseConstNoVerify = Template.make("testName", "tmp", (String testName, TestTemplates templates) -> scope(
+ runTestConst.asToken(testName),
+ """
+ @Test
+ """,
+ testMethodConst.asToken(testName, templates)
+ ));
+
+ // For methods with a parametrized offset into src, only validate the correctness of the return value.
+ var testCaseIdx = Template.make("testName", "tmp", (String testName, TestTemplates templates) -> scope(
+ runTestIdx.asToken(testName),
+ """
+ @Test
+ """,
+ testMethodIdx.asToken(testName, templates)
+ ));
+
+ // Generates tests with the clonebasic variant of the ArraycopyNode.
+ var testCaseClone = Template.make("testName", "loadCount", "tmp", (String testName, Integer loadCount, TestTemplates templates) -> scope(
+ let("typeAbbrev", pty.abbrev().equals("C") ? "US" : pty.abbrev()),
+ runTestConst.asToken(testName),
+ """
+ @Test
+ @IR(counts = { IRNode.LOAD_#{typeAbbrev}, "=#{loadCount}" },
+ applyIf = { "TieredCompilation", "true"})
+ """,
+ testMethodClone.asToken(testName, templates)
+ ));
+
+ var storeConst = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ """
+ src[WRITE_IDX] = WRITE_VAL_#{typeAbbrev};
+ """
+ ));
+
+ var storeIdx = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ """
+ src[idx + RETURN_IDX] = WRITE_VAL_#{typeAbbrev};
+ """
+ ));
+
+ var storeClone = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ """
+ src[RETURN_IDX] = WRITE_VAL_#{typeAbbrev};
+ """
+ ));
+
+ var storeAlias = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ """
+ alias[WRITE_IDX] = WRITE_VAL_#{typeAbbrev};
+ """
+ ));
+
+ var unstableTrap = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ """
+ if (flag) {
+ src[0] = WRITE_VAL_#{typeAbbrev};
+ }
+ """
+ ));
+
+ // This generates test with a store to the returned src element and a simple unstable if trap.
+ // Only one rematerialization load is in the common path.
+ var testStore = Template.make(() -> {
+ final String testName = "Store" + pty.abbrev();
+ return scope(
+ testCaseConst.asToken("Const" + testName, 2 * config.copyLen - 1, new TestTemplates(storeConst, unstableTrap)),
+ testCaseIdx.asToken("Idx" + testName, new TestTemplates(storeIdx, unstableTrap)),
+ testCaseClone.asToken("Clone" + testName, config.copyLen, new TestTemplates(storeClone, unstableTrap)),
+ testCaseConst.asToken("Alias" + testName, 3 * config.copyLen - 2, new TestTemplates(storeAlias, unstableTrap))
+ );
+ });
+
+ // This generates tests with multiple interfering stores forcing elements to be put into the common path.
+ var testMultiStore = Template.make(() -> {
+ final String testName = "MultiStore" + pty.abbrev();
+ final int numStores = RANDOM.nextInt(1, config.copyLen - 1);
+ // Get numStores different store locations.
+ final Set storeIdxs = new HashSet<>();
+ storeIdxs.add(config.returnIdx); // Always store at the WRITE_IDX to potentially trigger the bug.
+ while (storeIdxs.size() < numStores) {
+ storeIdxs.add(RANDOM.nextInt(0, config.copyLen));
+ }
+ var multiStoresConst = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ storeIdxs.stream()
+ .map(idx -> scope(let("idx", idx), "src[COPY_IDX + #idx] = WRITE_VAL_#{typeAbbrev};\n"))
+ .toList()
+ ));
+ var multiStoresIdx = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ storeIdxs.stream()
+ .map(idx -> scope(let("idx", idx), "src[idx + #idx] = WRITE_VAL_#{typeAbbrev};\n"))
+ .toList()
+ ));
+ var multiStoresClone = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ storeIdxs.stream()
+ .map(idx -> scope(let("idx", idx), "src[#idx] = WRITE_VAL_#{typeAbbrev};\n"))
+ .toList()
+ ));
+ var multiStoresAlias = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ storeIdxs.stream()
+ .map(idx -> scope(let("idx", idx), "alias[COPY_IDX + #idx] = WRITE_VAL_#{typeAbbrev};\n"))
+ .toList()
+ ));
+ return scope(
+ // Sometimes we get one more load depending on the position of the range checks of the different stores.
+ testCaseConstPlusOne.asToken("Const" + testName, 2 * config.copyLen - numStores, new TestTemplates(multiStoresConst, unstableTrap)),
+ testCaseIdx.asToken("Idx" + testName, new TestTemplates(multiStoresIdx, unstableTrap)),
+ testCaseClone.asToken("Clone" + testName, config.copyLen, new TestTemplates(multiStoresClone, unstableTrap)),
+ testCaseConstPlusOne.asToken("Alias" + testName, 3 * config.copyLen - 2 * numStores, new TestTemplates(multiStoresAlias, unstableTrap))
+ );
+ });
+
+ // This generates test with a store to the returned src element and the unstable if trap inside a loop.
+ // Only one rematerialization load is in the common path.
+ var testStoreTrapLoop = Template.make(() -> {
+ final String testName = "StoreTrapLoop" + pty.abbrev();
+ var trapTemplate = Template.make(() -> scope(
+ """
+ for (int i = 0; i < 1234; i++) {
+ """,
+ unstableTrap.asToken(),
+ """
+ }
+ """
+ ));
+ return scope(
+ testCaseConst.asToken("Const" + testName, 2 * config.copyLen - 1, new TestTemplates(storeConst, trapTemplate)),
+ testCaseIdx.asToken("Idx" + testName, new TestTemplates(storeIdx, trapTemplate)),
+ testCaseConst.asToken("Alias" + testName, 2 * config.copyLen - 1, new TestTemplates(storeAlias, trapTemplate))
+ );
+ });
+
+ // This generates tests with atomic operations (LoadStores) as polluting store.
+ // Only one rematerialization load is in the common path.
+ var testAtomics = Template.make(() -> {
+ var getAndSetStoreConst = Template.make(() -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("handle", handle),
+ """
+ #{handle}.getAndSet(src, WRITE_IDX, (#type) WRITE_VAL_#{typeAbbrev});
+ """
+ ));
+ var getAndSetStoreIdx = Template.make(() -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("handle", handle),
+ """
+ #{handle}.getAndSet(src, idx + RETURN_IDX, (#type) WRITE_VAL_#{typeAbbrev});
+ """
+ ));
+ var casStoreConst = Template.make(() -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("handle", handle),
+ """
+ #{handle}.compareAndSet(src, WRITE_IDX, (#type) SRC_VAL_#{typeAbbrev}, (#type) WRITE_VAL_#{typeAbbrev});
+ """
+ ));
+ var casStoreIdx = Template.make(() -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("handle", handle),
+ """
+ #{handle}.compareAndSet(src, idx + RETURN_IDX, (#type) SRC_VAL_#{typeAbbrev}, (#type) WRITE_VAL_#{typeAbbrev});
+ """
+ ));
+ return scope(let("type", pty),
+ let("handle", handle),
+ Hooks.CLASS_HOOK.insert(scope(
+ """
+ private static final VarHandle #handle = MethodHandles.arrayElementVarHandle(#type[].class);
+ """
+ )),
+ // We cannot look through MemBars emitted by the atomic operations, so all rematerialization loads are
+ // commoned up in the common path.
+ pty.abbrev().equals("S") || pty.abbrev().equals("B") || pty.abbrev().equals("C") ?
+ testCaseConstNoVerify.asToken("ConstGetAndSet" + pty.abbrev(), new TestTemplates(getAndSetStoreConst, unstableTrap)) :
+ testCaseConst.asToken("ConstGetAndSet" + pty.abbrev(), config.copyLen, new TestTemplates(getAndSetStoreConst, unstableTrap)),
+ testCaseIdx.asToken("IdxGetAndSet" + pty.abbrev(), new TestTemplates(getAndSetStoreIdx, unstableTrap)),
+ testCaseConst.asToken("ConstCompareAndSet" + pty.abbrev(), config.copyLen, new TestTemplates(casStoreConst, unstableTrap)),
+ testCaseIdx.asToken("IdxCompareAndSet" + pty.abbrev(), new TestTemplates(casStoreIdx, unstableTrap))
+ );
+ });
+
+ var testMemorySegments = Template.make(() -> {
+ final String layout = String.format("JAVA_%s", pty.toString().toUpperCase());
+ var memorySegmentCreation = Template.make(() -> scope(
+ """
+ MemorySegment srcMS = MemorySegment.ofArray(src);
+ """
+ ));
+ var memorySegmentCreationAlias = Template.make(() -> scope(
+ """
+ MemorySegment srcMS = MemorySegment.ofArray(alias);
+ """
+ ));
+ // Just write using a memory segment
+ var memorySegmentStoreConst = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ let("layout", layout),
+ """
+ srcMS.setAtIndex(ValueLayout.#{layout}, WRITE_IDX, WRITE_VAL_#{typeAbbrev});
+ """
+ ));
+ var memorySegmentStoreIdx = Template.make(() -> scope(
+ let("typeAbbrev", pty.abbrev()),
+ let("layout", layout),
+ """
+ srcMS.setAtIndex(ValueLayout.#{layout}, RETURN_IDX + idx, WRITE_VAL_#{typeAbbrev});
+ """
+ ));
+ // Write a single byte somewhere within the returned value.
+ var memorySegmentStoreSmallConst = Template.make(() -> scope(
+ let("offset", RANDOM.nextInt(pty.byteSize())),
+ let("byteSize", pty.byteSize()),
+ """
+ srcMS.set(ValueLayout.JAVA_BYTE, WRITE_IDX * #byteSize - #offset, (byte)-1);
+ """
+ ));
+ var memorySegmentStoreSmallIdx = Template.make(() -> scope(
+ let("offset", RANDOM.nextInt(pty.byteSize())),
+ let("byteSize", pty.byteSize()),
+ """
+ srcMS.set(ValueLayout.JAVA_BYTE, (RETURN_IDX + idx) * #byteSize - #offset, (byte)-1);
+ """
+ ));
+ // Write 8 bytes overlapping multiple array elements.
+ int offsetMut = RANDOM.nextInt(pty.byteSize());
+ if (config.writeIdx * pty.byteSize() - offsetMut + 8 >= config.srcSize * pty.byteSize()) {
+ // This will not fit. Adjust offset.
+ offsetMut = (config.writeIdx * pty.byteSize() + 8) - (config.srcSize * pty.byteSize());
+ }
+ final int offset = offsetMut;
+ var memorySegmentStoreOverlappingConst = Template.make(() -> scope(
+ let("offset", offset),
+ let("byteSize", pty.byteSize()),
+ """
+ srcMS.set(ValueLayout.JAVA_LONG_UNALIGNED, WRITE_IDX * #byteSize - #offset, -1);
+ """
+ ));
+ var memorySegmentStoreOverlappingIdx = Template.make(() -> scope(
+ let("offset", offset),
+ let("byteSize", pty.byteSize()),
+ """
+ srcMS.set(ValueLayout.JAVA_LONG_UNALIGNED, (RETURN_IDX + idx) * #byteSize - #offset, -1);
+ """
+ ));
+ return scope(
+ let("type", pty),
+ // The number of loads differs run to run (probably due to the amount of inlining going on), so
+ // we do not verify the number of loads in the uncommon path, even though only the polluted loads
+ // end up in the common path for all const cases.
+ testCaseConstNoVerify.asToken("MemorySegmentStoreConst" + pty.abbrev(), new TestTemplates(memorySegmentStoreConst, unstableTrap, memorySegmentCreation)),
+ testCaseIdx.asToken("MemorySegmentStoreIdx" + pty.abbrev(), new TestTemplates(memorySegmentStoreIdx, unstableTrap, memorySegmentCreation)),
+ testCaseConstNoVerify.asToken("MemorySegmentStoreAlias" + pty.abbrev(), new TestTemplates(memorySegmentStoreConst, unstableTrap, memorySegmentCreationAlias)),
+ testCaseConstNoVerify.asToken("MemorySegmentStoreSmallConst" + pty.abbrev(), new TestTemplates(memorySegmentStoreSmallConst, unstableTrap, memorySegmentCreation)),
+ testCaseIdx.asToken("MemorySegmentStoreSmallIdx" + pty.abbrev(), new TestTemplates(memorySegmentStoreSmallIdx, unstableTrap, memorySegmentCreation)),
+ testCaseConstNoVerify.asToken("MemorySegmentStoreSmallAlias" + pty.abbrev(), new TestTemplates(memorySegmentStoreSmallConst, unstableTrap, memorySegmentCreationAlias)),
+ testCaseConstNoVerify.asToken("MemorySegmentStoreOverlappingConst" + pty.abbrev(), new TestTemplates(memorySegmentStoreOverlappingConst, unstableTrap, memorySegmentCreation)),
+ testCaseIdx.asToken("MemorySegmentStoreOverlappingIdx" + pty.abbrev(), new TestTemplates(memorySegmentStoreOverlappingIdx, unstableTrap, memorySegmentCreation)),
+ testCaseConstNoVerify.asToken("MemorySegmentStoreOverlappingAlias" + pty.abbrev(), new TestTemplates(memorySegmentStoreOverlappingConst, unstableTrap, memorySegmentCreationAlias))
+ );
+ });
+
+ // C2 is not able to put any rematerialization load in the uncommon path for this test.
+ // Thus, we do not check the number of loads.
+ var testSwitch = Template.make(() -> {
+ final String testName = "Switch" + pty.abbrev();
+ return scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("testName", testName),
+ let("src", srcArray),
+ """
+ @Run(test = "test#testName")
+ @Warmup(10000)
+ static void run#testName(RunInfo info) {
+ Arrays.fill(#src, SRC_VAL_#{typeAbbrev});
+ #type res = test#testName(#src);
+ Asserts.assertEQ((#type) SRC_VAL_#{typeAbbrev}, res, "Wrong Result from " + info.getTest().getName());
+ }
+
+ @Test
+ static #type test#testName(#type[] src) {
+ #type[] dst = new #type[COPY_LEN];
+ System.arraycopy(src, COPY_IDX, dst, 0, COPY_LEN);
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 8; j++) {
+ switch (i) {
+ case -1 -> { /*nop*/ }
+ case 0 -> src[WRITE_IDX] = WRITE_VAL_#{typeAbbrev};
+ }
+ }
+ }
+
+ return dst[1];
+ }
+ """
+ );
+ });
+
+ // This generates tests where the polluting store to src is an arraycopy.
+ // In this case, the rematerialization loads for all elements that are written to
+ // have to be in the common path.
+ var testArraycopy = Template.make(() -> {
+ final String testName = "Arraycopy" + pty.abbrev();
+ final int arraycopyLen = RANDOM.nextInt(1, Math.min(config.copyLen, config.srcSize - config.writeIdx));
+ final int otherLen = RANDOM.nextInt(arraycopyLen + 1, arraycopyLen + 10);
+ final int copyOtherIdx = RANDOM.nextInt(0, otherLen - arraycopyLen);
+ var arraycopyStoreConst = Template.make(() -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("arraycopyLen", arraycopyLen),
+ let("other", "other" + pty.abbrev()),
+ let("otherLen", otherLen),
+ let("copyOtherIdx", copyOtherIdx),
+ Hooks.CLASS_HOOK.insert(scope(
+ """
+ private static final #type[] #other = new #type[#otherLen];
+ static { Arrays.fill(#other, WRITE_VAL_#{typeAbbrev}); }
+ """
+ )),
+ """
+ System.arraycopy(#other, #copyOtherIdx, src, WRITE_IDX, #arraycopyLen);
+ """
+ ));
+ var arraycopyStoreIdx = Template.make(() -> scope(
+ let("type", pty),
+ let("arraycopyLen", arraycopyLen),
+ let("other", "other" + pty.abbrev()),
+ let("copyOtherIdx", copyOtherIdx),
+ """
+ System.arraycopy(#other, #copyOtherIdx, src, idx + RETURN_IDX, #arraycopyLen);
+ """
+ ));
+ var arraycopyStoreAlias = Template.make(() -> scope(
+ let("type", pty),
+ let("arraycopyLen", arraycopyLen),
+ let("other", "other" + pty.abbrev()),
+ let("copyOtherIdx", copyOtherIdx),
+ """
+ System.arraycopy(#other, #copyOtherIdx, alias, WRITE_IDX, #arraycopyLen);
+ """
+ ));
+ // Unfortunately, it is not possible to validate the placement of rematerialization nodes because
+ // the number of uncomon traps is sensitive to changes in the profile, which leads to a bimodal count
+ // of load nodes.
+ return scope(
+ testCaseConstNoVerify.asToken("Const" + testName, new TestTemplates(arraycopyStoreConst, unstableTrap)),
+ testCaseIdx.asToken("Idx" + testName, new TestTemplates(arraycopyStoreIdx, unstableTrap)),
+ testCaseConstNoVerify.asToken("Alias" + testName, new TestTemplates(arraycopyStoreAlias, unstableTrap))
+ );
+ });
+
+ return Template.make(() -> scope(
+ let("type", pty),
+ let("typeAbbrev", pty.abbrev()),
+ let("writeVal", pty.con()),
+ let("rng", pty.callLibraryRNG()),
+ let("src", srcArray),
+ Hooks.CLASS_HOOK.insert(scope(
+ """
+ private static final #type SRC_VAL_#{typeAbbrev};
+ private static final #type WRITE_VAL_#{typeAbbrev} = #writeVal;
+ private static final #type[] #src = new #type[SRC_SIZE];
+
+ static {
+ #type srcVal;
+ do {
+ srcVal = #rng;
+ } while (srcVal == WRITE_VAL_#{typeAbbrev});
+ SRC_VAL_#{typeAbbrev} = srcVal;
+ }
+ """
+ )),
+ List.of(testStore,
+ testMultiStore,
+ testStoreTrapLoop,
+ testAtomics,
+ testMemorySegments,
+ testSwitch,
+ testArraycopy)
+ .stream()
+ .map(t -> t.asToken())
+ .collect(Collectors.toList())
+ )).asToken();
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java
new file mode 100644
index 00000000000..9be192d1f55
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package compiler.exceptions;
+
+import compiler.lib.ir_framework.CompLevel;
+import compiler.lib.ir_framework.Run;
+import compiler.lib.ir_framework.Test;
+import compiler.lib.ir_framework.TestFramework;
+
+import java.lang.classfile.Label;
+import java.lang.constant.ClassDesc;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import jdk.test.lib.Asserts;
+import test.java.lang.invoke.lib.InstructionHelper;
+
+/**
+ * @test
+ * @bug 8350208
+ * @summary Safepoints added during the processing of exception handlers need correct stack state
+ * @library /test/lib /test/jdk/java/lang/invoke/common /
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ *
+ * @run main/othervm ${test.main.class}
+ */
+public class TestDebugDuringExceptionCatching {
+
+ public static class V {
+ int v;
+ }
+
+ static final int ITERATIONS = 100;
+ static final RuntimeException EXCEPTION = new RuntimeException();
+
+ /**
+ * Construct something that looks like this:
+ * {@code
+ * int snippet(V v) {
+ * int i = 0;
+ * LoopHead: {
+ * if (i >= 100) {
+ * goto LoopEnd;
+ * }
+ * i++;
+ * try {
+ * v.v = 1;
+ * } catch (Throwable) {
+ * // Not really, the LoopHead is the exception Handler
+ * goto LoopHead;
+ * }
+ * }
+ * LoopEnd:
+ * return i;
+ * }
+ * }
+ */
+ static final MethodHandle SNIPPET_HANDLE;
+ static final ClassDesc CLASS_DESC = TestDebugDuringExceptionCatching.class.describeConstable().get();
+ static {
+ SNIPPET_HANDLE = InstructionHelper.buildMethodHandle(MethodHandles.lookup(),
+ "snippet",
+ MethodType.methodType(int.class, V.class),
+ CODE -> {
+ Label loopHead = CODE.newLabel();
+ Label loopEnd = CODE.newLabel();
+ Label tryStart = CODE.newLabel();
+ Label tryEnd = CODE.newLabel();
+ CODE.
+ iconst_0().
+ istore(1).
+ // The loop head should have a RuntimeException as the sole element on the stack
+ getstatic(CLASS_DESC, "EXCEPTION", RuntimeException.class.describeConstable().get()).
+ labelBinding(loopHead).
+ pop().
+ iload(1).
+ ldc(ITERATIONS).
+ if_icmpge(loopEnd).
+ iinc(1, 1).
+ aload(0).
+ iconst_1().
+ labelBinding(tryStart).
+ putfield(V.class.describeConstable().get(), "v", int.class.describeConstable().get()).
+ labelBinding(tryEnd).
+ // The stack is empty here
+ labelBinding(loopEnd).
+ iload(1).
+ ireturn();
+ CODE.exceptionCatchAll(tryStart, tryEnd, loopHead);
+ });
+ }
+
+ @Test(compLevel = CompLevel.C2) // see JDK-8381786
+ private static int testBackwardHandler(V v) throws Throwable {
+ return (int) SNIPPET_HANDLE.invokeExact(v);
+ }
+
+ @Run(test = "testBackwardHandler")
+ public void run() throws Throwable {
+ Asserts.assertEQ(ITERATIONS, testBackwardHandler(null));
+ }
+
+ public static void main(String[] args) {
+ TestFramework.run();
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java
index 33be24a0367..dfb2ec1405c 100644
--- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java
+++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java
@@ -30,7 +30,7 @@
* @modules jdk.incubator.vector
* @library /test/lib /
* @compile ../lib/verify/Verify.java
- * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=10000 compiler.igvn.ExpressionFuzzer
+ * @run driver compiler.igvn.ExpressionFuzzer
*/
package compiler.igvn;
@@ -73,6 +73,10 @@
// - Some basic IR tests to ensure that the constraints / checksum mechanics work.
// We may even have to add some IGVN optimizations to be able to better observe things right.
// - Lower the CompileTaskTimeout, if possible. It is chosen conservatively (rather high) for now.
+// - I also had to exclude the compilation of the following method. It would lead to compilation
+// timeouts and even compilation memory limit reached. It is a really large method, so I'm not
+// sure if that is to be expected, or if we could still improve the situation.
+// compiler.lib.template_framework.library.Operations::generateVectorOperations
public class ExpressionFuzzer {
private static final Random RANDOM = Utils.getRandomInstance();
@@ -93,7 +97,8 @@ public static void main(String[] args) {
comp.invoke("compiler.igvn.templated.ExpressionFuzzerInnerTest", "main", new Object[] {new String[] {
"--add-modules=jdk.incubator.vector",
"--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED",
- "--add-opens", "java.base/java.lang=ALL-UNNAMED"
+ "--add-opens", "java.base/java.lang=ALL-UNNAMED",
+ "-XX:+IgnoreUnrecognizedVMOptions", "-XX:CompileTaskTimeout=10000"
}});
}
diff --git a/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java
index bfb617a0a3a..1c2d07d3255 100644
--- a/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java
+++ b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
* which is only read in a debug VM.
* @requires vm.jvmci
* @requires vm.debug
+ * @requires vm.flagless
* @library /test/lib /
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot
* jdk.internal.vm.ci/jdk.vm.ci.code
diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java
index b461e3e857f..33eba66cd8c 100644
--- a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java
+++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -132,6 +132,14 @@ public interface CodeGenerationDataNameType extends DataName.Type {
longs()
);
+ /**
+ * List of {@link PrimitiveType}s (int, long).
+ */
+ List INT_LONG_TYPES = List.of(
+ ints(),
+ longs()
+ );
+
/**
* List of all subword {@link PrimitiveType}s (byte, char, short).
*/
@@ -176,4 +184,72 @@ public interface CodeGenerationDataNameType extends DataName.Type {
booleans(),
float16()
);
+
+ List VECTOR_BYTE_VECTOR_TYPES = List.of(
+ VectorType.BYTE_64,
+ VectorType.BYTE_128,
+ VectorType.BYTE_256,
+ VectorType.BYTE_512
+ );
+
+ List VECTOR_SHORT_VECTOR_TYPES = List.of(
+ VectorType.SHORT_64,
+ VectorType.SHORT_128,
+ VectorType.SHORT_256,
+ VectorType.SHORT_512
+ );
+
+ List VECTOR_INT_VECTOR_TYPES = List.of(
+ VectorType.INT_64,
+ VectorType.INT_128,
+ VectorType.INT_256,
+ VectorType.INT_512
+ );
+
+ List VECTOR_LONG_VECTOR_TYPES = List.of(
+ VectorType.LONG_64,
+ VectorType.LONG_128,
+ VectorType.LONG_256,
+ VectorType.LONG_512
+ );
+
+ List VECTOR_FLOAT_VECTOR_TYPES = List.of(
+ VectorType.FLOAT_64,
+ VectorType.FLOAT_128,
+ VectorType.FLOAT_256,
+ VectorType.FLOAT_512
+ );
+
+ List VECTOR_DOUBLE_VECTOR_TYPES = List.of(
+ VectorType.DOUBLE_64,
+ VectorType.DOUBLE_128,
+ VectorType.DOUBLE_256,
+ VectorType.DOUBLE_512
+ );
+
+ List VECTOR_VECTOR_TYPES = Utils.concat(
+ VECTOR_BYTE_VECTOR_TYPES,
+ VECTOR_SHORT_VECTOR_TYPES,
+ VECTOR_INT_VECTOR_TYPES,
+ VECTOR_LONG_VECTOR_TYPES,
+ VECTOR_FLOAT_VECTOR_TYPES,
+ VECTOR_DOUBLE_VECTOR_TYPES
+ );
+
+ List VECTOR_MASK_TYPES =
+ VECTOR_VECTOR_TYPES.stream().map(t -> t.maskType).toList();
+
+ List VECTOR_SHUFFLE_TYPES =
+ VECTOR_VECTOR_TYPES.stream().map(t -> t.shuffleType).toList();
+
+ List VECTOR_TYPES = Utils.concat(
+ VECTOR_VECTOR_TYPES,
+ VECTOR_MASK_TYPES,
+ VECTOR_SHUFFLE_TYPES
+ );
+
+ List ALL_TYPES = Utils.concat(
+ SCALAR_NUMERIC_TYPES,
+ VECTOR_TYPES
+ );
}
diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java
index 37ad4debde6..f3e7b6ed0d3 100644
--- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java
+++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -332,6 +332,72 @@ public static Expression make(CodeGenerationDataNameType returnType,
return new Expression(returnType, List.of(t0, t1, t2, t3), List.of(s0, s1, s2, s3, s4), info);
}
+ /**
+ * Creates a new Expression with 5 arguments.
+ *
+ * @param returnType The return type of the {@link Expression}.
+ * @param s0 The first string, to be placed before {@code t0}.
+ * @param t0 The type of the first argument.
+ * @param s1 The second string, to be placed before {@code t1}.
+ * @param t1 The type of the second argument.
+ * @param s2 The third string, to be placed before {@code t2}.
+ * @param t2 The type of the third argument.
+ * @param s3 The fourth string, to be placed before {@code t3}.
+ * @param t3 The type of the fourth argument.
+ * @param s4 The fifth string, to be placed before {@code t4}.
+ * @param t4 The type of the fifth argument.
+ * @param s5 The last string, finishing the {@link Expression}.
+ * @return the new {@link Expression}.
+ */
+ public static Expression make(CodeGenerationDataNameType returnType,
+ String s0,
+ CodeGenerationDataNameType t0,
+ String s1,
+ CodeGenerationDataNameType t1,
+ String s2,
+ CodeGenerationDataNameType t2,
+ String s3,
+ CodeGenerationDataNameType t3,
+ String s4,
+ CodeGenerationDataNameType t4,
+ String s5) {
+ return make(returnType, s0, t0, s1, t1, s2, t2, s3, t3, s4, t4, s5, new Info());
+ }
+
+ /**
+ * Creates a new Expression with 5 arguments.
+ *
+ * @param returnType The return type of the {@link Expression}.
+ * @param s0 The first string, to be placed before {@code t0}.
+ * @param t0 The type of the first argument.
+ * @param s1 The second string, to be placed before {@code t1}.
+ * @param t1 The type of the second argument.
+ * @param s2 The third string, to be placed before {@code t2}.
+ * @param t2 The type of the third argument.
+ * @param s3 The fourth string, to be placed before {@code t3}.
+ * @param t3 The type of the fourth argument.
+ * @param s4 The fifth string, to be placed before {@code t4}.
+ * @param t4 The type of the fifth argument.
+ * @param s5 The last string, finishing the {@link Expression}.
+ * @param info Additional information about the {@link Expression}.
+ * @return the new {@link Expression}.
+ */
+ public static Expression make(CodeGenerationDataNameType returnType,
+ String s0,
+ CodeGenerationDataNameType t0,
+ String s1,
+ CodeGenerationDataNameType t1,
+ String s2,
+ CodeGenerationDataNameType t2,
+ String s3,
+ CodeGenerationDataNameType t3,
+ String s4,
+ CodeGenerationDataNameType t4,
+ String s5,
+ Info info) {
+ return new Expression(returnType, List.of(t0, t1, t2, t3, t4), List.of(s0, s1, s2, s3, s4, s5), info);
+ }
+
/**
* Creates a {@link TemplateToken} for the use in a {@link Template} by applying the
* {@code arguments} to the {@link Expression}. It is the users responsibility to
diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java
index 2ea251cc5e5..4c598506e70 100644
--- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java
+++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java
@@ -24,11 +24,8 @@
package compiler.lib.template_framework.library;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static compiler.lib.template_framework.library.PrimitiveType.BYTES;
import static compiler.lib.template_framework.library.PrimitiveType.SHORTS;
@@ -39,6 +36,10 @@
import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES;
import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS;
import static compiler.lib.template_framework.library.Float16Type.FLOAT16;
+import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES;
+import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_TYPES;
+import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_TYPES;
+import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_TYPES;
/**
* This class provides various lists of {@link Expression}s, that represent Java operators or library
@@ -51,27 +52,8 @@ private Operations() {}
private static Expression.Info WITH_ARITHMETIC_EXCEPTION = new Expression.Info().withExceptions(Set.of("ArithmeticException"));
private static Expression.Info WITH_NONDETERMINISTIC_RESULT = new Expression.Info().withNondeterministicResult();
-
-
- /**
- * Provides a lits of operations on {@link PrimitiveType}s, such as arithmetic, logical,
- * and cast operations.
- */
- public static final List PRIMITIVE_OPERATIONS = generatePrimitiveOperations();
-
- public static final List FLOAT16_OPERATIONS = generateFloat16Operations();
-
- public static final List SCALAR_NUMERIC_OPERATIONS = concat(
- PRIMITIVE_OPERATIONS,
- FLOAT16_OPERATIONS
- );
-
- @SafeVarargs
- private static List concat(List... lists) {
- return Arrays.stream(lists)
- .flatMap(List::stream)
- .collect(Collectors.toList());
- }
+ private static Expression.Info WITH_ILLEGAL_ARGUMENT_EXCEPTION = new Expression.Info().withExceptions(Set.of("IllegalArgumentException"));
+ private static Expression.Info WITH_OUT_OF_BOUNDS_EXCEPTION = new Expression.Info().withExceptions(Set.of("IndexOutOfBoundsException"));
private static void addComparisonOperations(List ops, String operatorName, CodeGenerationDataNameType type) {
for (String mask : List.of("==", "!=", "<", ">", "<=", ">=")) {
@@ -332,4 +314,522 @@ private static List generateFloat16Operations() {
// Make sure the list is not modifiable.
return List.copyOf(ops);
}
+
+ private enum VOPType {
+ UNARY,
+ BINARY,
+ ASSOCIATIVE, // Binary and associative - safe for reductions of any type
+ INTEGRAL_ASSOCIATIVE, // Binary - but only safe for integral reductions
+ TERNARY
+ }
+ private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) {
+ VOP(String name, VOPType type, List elementTypes) {
+ this(name, type, elementTypes, true);
+ }
+ }
+
+ // TODO: consider enforcing precision instead of just blanket non-determinism
+ // We could annotate the exact ulp, and so if some test can use it: great
+ // But if a test is just interested in determinism, they are still
+ // non-deterministic.
+ private static final List VECTOR_OPS = List.of(
+ new VOP("ABS", VOPType.UNARY, PRIMITIVE_TYPES),
+ new VOP("ACOS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("ASHR", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("ASIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("ATAN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("ATAN2", VOPType.BINARY, FLOATING_TYPES, false), // 2 ulp
+ new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_TYPES),
+ new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_TYPES),
+ new VOP("CBRT", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_TYPES),
+ new VOP("COS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("COSH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp
+ new VOP("DIV", VOPType.BINARY, FLOATING_TYPES),
+ new VOP("EXP", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_TYPES),
+ new VOP("EXPM1", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("FMA", VOPType.TERNARY, FLOATING_TYPES),
+ new VOP("HYPOT", VOPType.BINARY, FLOATING_TYPES, false), // 1.5 ulp
+ new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES),
+ new VOP("LOG", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("LOG10", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("LOG1P", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("LSHL", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("LSHR", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("MIN", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("MAX", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("NEG", VOPType.UNARY, PRIMITIVE_TYPES),
+ new VOP("NOT", VOPType.UNARY, INTEGRAL_TYPES),
+ new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("POW", VOPType.BINARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("REVERSE", VOPType.UNARY, INTEGRAL_TYPES),
+ new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_TYPES),
+ new VOP("ROL", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("ROR", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("SADD", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("SIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp
+ new VOP("SINH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp
+ new VOP("SQRT", VOPType.UNARY, FLOATING_TYPES),
+ new VOP("SSUB", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("SUADD", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("SUB", VOPType.BINARY, PRIMITIVE_TYPES),
+ new VOP("SUSUB", VOPType.BINARY, INTEGRAL_TYPES),
+ new VOP("TAN", VOPType.UNARY, FLOATING_TYPES, false), // 1.25 ulp
+ new VOP("TANH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp
+ new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES),
+ new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("ZOMO", VOPType.UNARY, INTEGRAL_TYPES)
+ );
+
+ private static final List VECTOR_CMP = List.of(
+ new VOP("EQ", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("GE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("GT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("LE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("LT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("NE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES),
+ new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES),
+ new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES)
+ );
+
+ private static final List