From 14cb5ddfc561a248206eeb54cbbc554c6889aaca Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Tue, 24 Feb 2026 07:23:41 +0000 Subject: [PATCH 001/636] 8376291: [IR Framework] Create classes for separate Test VM messages Reviewed-by: mchevalier, dfenacci, epeter --- .../ir_framework/driver/TestVMProcess.java | 1 + .../driver/irmatching/irmethod/IRMethod.java | 2 +- .../parser/ApplicableIRRulesParser.java | 23 +--- .../irmatching/parser/TestClassParser.java | 2 +- .../irmatching/parser/VMInfoParser.java | 22 +-- .../driver/network/TestVMData.java | 64 ++------- .../network/testvm/TestVmMessageReader.java | 20 ++- .../network/testvm/java/ExecutedTests.java | 55 ++++++++ .../network/testvm/java/JavaMessage.java | 35 +++++ .../testvm/java/JavaMessageParser.java | 125 ++++++++++++++++++ .../network/testvm/java/JavaMessages.java | 42 ++++-- .../network/testvm/java/MethodTimes.java | 85 ++++++++++++ .../network/testvm/java/StdoutMessages.java | 54 ++++++++ .../test/ApplicableIRRulesPrinter.java | 15 +-- .../lib/ir_framework/test/TestVM.java | 20 +-- .../lib/ir_framework/test/VMInfoPrinter.java | 9 +- .../ir_framework/test/network/MessageTag.java | 1 + .../test/network/TestVmSocket.java | 15 +++ 18 files changed, 444 insertions(+), 146 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ExecutedTests.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessage.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/MethodTimes.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/StdoutMessages.java diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java index 9a55db01fa0..00a9b93d124 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java @@ -75,6 +75,7 @@ public class TestVMProcess { checkTestVMExitCode(); String hotspotPidFileName = String.format("hotspot_pid%d.log", oa.pid()); testVmData = socket.testVmData(hotspotPidFileName, allowNotCompilable); + testVmData.printJavaMessages(); } public String getCommandLine() { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java index 893312c5196..5fbe90f0e72 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java @@ -98,7 +98,7 @@ public class IRMethod implements IRMethodMatchable { List match = matcher.match(); long endTime = System.nanoTime(); long duration = (endTime - startTime); - System.out.println("Verifying IR rules for " + name() + ": " + duration + " ns = " + (duration / 1000000) + " ms"); + System.out.println("Verifying IR rules for " + name() + ": " + duration + " ns = " + (duration / 1_000_000) + " ms"); return new IRMethodMatchResult(method, match); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java index be44c3f3d91..d251c574e47 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java @@ -36,8 +36,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Class to parse the Applicable IR Rules emitted by the Test VM and creating {@link TestMethod} objects for each entry. @@ -45,11 +43,6 @@ import java.util.regex.Pattern; * @see TestMethod */ public class ApplicableIRRulesParser { - - private static final boolean PRINT_APPLICABLE_IR_RULES = Boolean.parseBoolean(System.getProperty("PrintApplicableIRRules", "false")); - private static final Pattern APPLICABLE_IR_RULES_PATTERN = - Pattern.compile("(?<=" + ApplicableIRRulesPrinter.START + "\r?\n).*\\R([\\s\\S]*)(?=" + ApplicableIRRulesPrinter.END + ")"); - private final Map testMethods; private final Class testClass; @@ -63,10 +56,6 @@ public class ApplicableIRRulesParser { * entry for each method that needs to be IR matched on. */ public TestMethods parse(String applicableIRRules) { - if (TestFramework.VERBOSE || PRINT_APPLICABLE_IR_RULES) { - System.out.println("Read Applicable IR Rules from Test VM:"); - System.out.println(applicableIRRules); - } createTestMethodMap(applicableIRRules, testClass); // We could have found format errors in @IR annotations. Report them now with an exception. TestFormat.throwIfAnyFailures(); @@ -105,15 +94,11 @@ public class ApplicableIRRulesParser { * Parse the Applicable IR Rules lines without header, explanation line and footer and return them in an array. */ private String[] getApplicableIRRulesLines(String applicableIRRules) { - Matcher matcher = APPLICABLE_IR_RULES_PATTERN.matcher(applicableIRRules); - TestFramework.check(matcher.find(), "Did not find Applicable IR Rules in:" + - System.lineSeparator() + applicableIRRules); - String lines = matcher.group(1).trim(); - if (lines.isEmpty()) { + if (applicableIRRules.isEmpty()) { // Nothing to IR match. return new String[0]; } - return lines.split("\\R"); + return applicableIRRules.split("\\R"); } /** @@ -148,7 +133,7 @@ public class ApplicableIRRulesParser { private void validateIRRuleIds(Method m, IR[] irAnnos, IRRuleIds irRuleIds) { TestFramework.check(irRuleIds != null, "Should find method name in validIrRulesMap for " + m); TestFramework.check(!irRuleIds.isEmpty(), "Did not find any rule indices for " + m); - TestFramework.check((irRuleIds.first() >= 1 || irRuleIds.first() == ApplicableIRRulesPrinter.NO_RULE_APPLIED) + TestFramework.check((irRuleIds.first() >= 1 || irRuleIds.first() == ApplicableIRRulesPrinter.NO_RULES) && irRuleIds.last() <= irAnnos.length, "Invalid IR rule index found in validIrRulesMap for " + m); } @@ -157,6 +142,6 @@ public class ApplicableIRRulesParser { * Does the list of IR rules contain any applicable IR rules for the given conditions? */ private boolean hasAnyApplicableIRRules(IRRuleIds irRuleIds) { - return irRuleIds.first() != ApplicableIRRulesPrinter.NO_RULE_APPLIED; + return irRuleIds.first() != ApplicableIRRulesPrinter.NO_RULES; } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java index 6c899af2116..ca36a3e9f72 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java @@ -57,7 +57,7 @@ public class TestClassParser { public Matchable parse(TestVMData testVmData) { ApplicableIRRulesParser applicableIRRulesParser = new ApplicableIRRulesParser(testClass); TestMethods testMethods = applicableIRRulesParser.parse(testVmData.applicableIRRules()); - VMInfo vmInfo = VMInfoParser.parseVMInfo(testVmData.applicableIRRules()); + VMInfo vmInfo = VMInfoParser.parseVMInfo(testVmData.vmInfo()); if (testMethods.hasTestMethods()) { HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); LoggedMethods loggedMethods = hotSpotPidFileParser.parse(testVmData.hotspotPidFileName()); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java index 2b17303f1a7..44013839754 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java @@ -23,14 +23,10 @@ package compiler.lib.ir_framework.driver.irmatching.parser; -import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.shared.TestFrameworkException; -import compiler.lib.ir_framework.test.VMInfoPrinter; import java.util.HashMap; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Class to parse the VMInfo emitted by the Test VM and creating {@link VMInfo} objects for each entry. @@ -39,15 +35,12 @@ import java.util.regex.Pattern; */ public class VMInfoParser { - private static final Pattern VM_INFO_PATTERN = - Pattern.compile("(?<=" + VMInfoPrinter.START_VM_INFO + "\r?\n).*\\R([\\s\\S]*)(?=" + VMInfoPrinter.END_VM_INFO + ")"); - /** - * Extract VMInfo from the applicableIRRules. + * Create a new VMInfo object from the vmInfo string. */ - public static VMInfo parseVMInfo(String applicableIRRules) { + public static VMInfo parseVMInfo(String vmInfo) { Map map = new HashMap<>(); - String[] lines = getVMInfoLines(applicableIRRules); + String[] lines = getVMInfoLines(vmInfo); for (String s : lines) { String line = s.trim(); String[] splitLine = line.split(":", 2); @@ -64,14 +57,11 @@ public class VMInfoParser { /** * Extract the VMInfo from the applicableIRRules string, strip away the header and return the individual key-value lines. */ - private static String[] getVMInfoLines(String applicableIRRules) { - Matcher matcher = VM_INFO_PATTERN.matcher(applicableIRRules); - TestFramework.check(matcher.find(), "Did not find VMInfo in:" + System.lineSeparator() + applicableIRRules); - String lines = matcher.group(1).trim(); - if (lines.isEmpty()) { + private static String[] getVMInfoLines(String vmInfo) { + if (vmInfo.isEmpty()) { // Nothing to IR match. return new String[0]; } - return lines.split("\\R"); + return vmInfo.split("\\R"); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java index daa6a590ddf..413cf3347d8 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java @@ -26,25 +26,30 @@ package compiler.lib.ir_framework.driver.network; import compiler.lib.ir_framework.driver.irmatching.IRMatcher; import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages; import compiler.lib.ir_framework.shared.TestFrameworkSocket; -import compiler.lib.ir_framework.test.network.MessageTag; - -import java.util.Scanner; /** * This class collects all the parsed data received over the {@link TestFrameworkSocket}. This data is required later * in the {@link IRMatcher}. */ public class TestVMData { + private final JavaMessages javaMessages; private final boolean allowNotCompilable; private final String hotspotPidFileName; - private final String applicableIRRules; public TestVMData(JavaMessages javaMessages, String hotspotPidFileName, boolean allowNotCompilable) { - this.applicableIRRules = processOutput(javaMessages); + this.javaMessages = javaMessages; this.hotspotPidFileName = hotspotPidFileName; this.allowNotCompilable = allowNotCompilable; } + public String applicableIRRules() { + return javaMessages.applicableIRRules(); + } + + public String vmInfo() { + return javaMessages.vmInfo(); + } + public String hotspotPidFileName() { return hotspotPidFileName; } @@ -53,52 +58,7 @@ public class TestVMData { return allowNotCompilable; } - public String applicableIRRules() { - return applicableIRRules; - } - - /** - * Process the socket output: All prefixed lines are dumped to the standard output while the remaining lines - * represent the Applicable IR Rules used for IR matching later. - */ - private String processOutput(JavaMessages javaMessages) { - String output = javaMessages.output(); - if (javaMessages.hasStdOut()) { - StringBuilder testListBuilder = new StringBuilder(); - StringBuilder messagesBuilder = new StringBuilder(); - StringBuilder nonStdOutBuilder = new StringBuilder(); - Scanner scanner = new Scanner(output); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith(MessageTag.STDOUT)) { - // Exclude [STDOUT] from message. - line = line.substring(MessageTag.STDOUT.length()); - if (line.startsWith(MessageTag.TEST_LIST)) { - // Exclude [TEST_LIST] from message for better formatting. - line = "> " + line.substring(MessageTag.TEST_LIST.length() + 1); - testListBuilder.append(line).append(System.lineSeparator()); - } else { - messagesBuilder.append(line).append(System.lineSeparator()); - } - } else { - nonStdOutBuilder.append(line).append(System.lineSeparator()); - } - } - System.out.println(); - if (!testListBuilder.isEmpty()) { - System.out.println("Run flag defined test list"); - System.out.println("--------------------------"); - System.out.println(testListBuilder); - System.out.println(); - } - if (!messagesBuilder.isEmpty()) { - System.out.println("Messages from Test VM"); - System.out.println("---------------------"); - System.out.println(messagesBuilder); - } - return nonStdOutBuilder.toString(); - } else { - return output; - } + public void printJavaMessages() { + javaMessages.print(); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/TestVmMessageReader.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/TestVmMessageReader.java index b438794964d..a203fd367f7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/TestVmMessageReader.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/TestVmMessageReader.java @@ -23,10 +23,10 @@ package compiler.lib.ir_framework.driver.network.testvm; +import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessageParser; import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages; import compiler.lib.ir_framework.shared.TestFrameworkException; import compiler.lib.ir_framework.shared.TestFrameworkSocket; -import compiler.lib.ir_framework.test.network.MessageTag; import java.io.BufferedReader; import java.net.Socket; @@ -35,33 +35,29 @@ import java.util.concurrent.Future; /** * Dedicated reader for Test VM messages received by the {@link TestFrameworkSocket}. The reader is used as a task - * wrapped in a {@link Future}. The received messages are returned in a new {@link JavaMessages} wrapper. Once the - * Test VM is terminated, the client connection is closed and the parsed messages can be fetched with - * {@link Future#get()} which calls {@link #call()}. + * wrapped in a {@link Future}. The received messages are parsed with the {@link JavaMessageParser}. Once the Test VM + * is terminated, client connection is closed and the parsed messages can be fetched with {@link Future#get()} which + * calls {@link #call()}. */ public class TestVmMessageReader implements Callable { private final Socket socket; private final BufferedReader reader; - private boolean receivedStdOut; + private final JavaMessageParser messageParser; public TestVmMessageReader(Socket socket, BufferedReader reader) { this.socket = socket; this.reader = reader; - this.receivedStdOut = false; + this.messageParser = new JavaMessageParser(); } @Override public JavaMessages call() { try (socket; reader) { - StringBuilder builder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { - builder.append(line).append(System.lineSeparator()); - if (line.startsWith(MessageTag.STDOUT)) { - receivedStdOut = true; - } + messageParser.parseLine(line); } - return new JavaMessages(builder.toString(), receivedStdOut); + return messageParser.output(); } catch (Exception e) { throw new TestFrameworkException("Error while reading Test VM socket messages", e); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ExecutedTests.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ExecutedTests.java new file mode 100644 index 00000000000..f17d7eb5d74 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ExecutedTests.java @@ -0,0 +1,55 @@ +/* + * 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.lib.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.test.network.MessageTag; + +import java.util.List; + +/** + * Class to collect all Java Messages sent with tag {@link MessageTag#TEST_LIST}. These are only generated when the + * user runs with {@code -DTest=myTest} and represent the executed tests. + */ +class ExecutedTests implements JavaMessage { + private final List tests; + + public ExecutedTests(List tests) { + this.tests = tests; + } + + @Override + public void print() { + if (tests.isEmpty()) { + return; + } + + System.out.println(); + System.out.println("Executed Subset of Tests"); + System.out.println("------------------------"); + for (String test : tests) { + System.out.println("- " + test); + } + System.out.println(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessage.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessage.java new file mode 100644 index 00000000000..7eef84dbbbc --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessage.java @@ -0,0 +1,35 @@ +/* + * 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.lib.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.shared.TestFrameworkSocket; +import compiler.lib.ir_framework.test.network.MessageTag; + +/** + * Interface for a message sent from Java code to the Driver VM via the {@link TestFrameworkSocket}. We differentiate + * between different messages depending on the leading {@link MessageTag} of a received message. + */ +public interface JavaMessage { + void print(); +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java new file mode 100644 index 00000000000..896aef38f1f --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java @@ -0,0 +1,125 @@ +/* + * 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.lib.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.shared.TestFrameworkException; +import compiler.lib.ir_framework.test.network.MessageTag; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static compiler.lib.ir_framework.test.network.MessageTag.*; + +/** + * Dedicated parser for {@link JavaMessages} received from the Test VM. Depending on the parsed {@link MessageTag}, the + * message is parsed differently. + */ +public class JavaMessageParser { + private static final Pattern TAG_PATTERN = Pattern.compile("^(\\[[^]]+])\\s*(.*)$"); + + private final List stdoutMessages; + private final List executedTests; + private final Map methodTimes; + private final StringBuilder vmInfoBuilder; + private final StringBuilder applicableIrRules; + + private StringBuilder currentBuilder; + + public JavaMessageParser() { + this.stdoutMessages = new ArrayList<>(); + this.methodTimes = new HashMap<>(); + this.executedTests = new ArrayList<>(); + this.vmInfoBuilder = new StringBuilder(); + this.applicableIrRules = new StringBuilder(); + this.currentBuilder = null; + } + + public void parseLine(String line) { + line = line.trim(); + Matcher tagLineMatcher = TAG_PATTERN.matcher(line); + if (tagLineMatcher.matches()) { + // New tag + assertNoActiveParser(); + parseTagLine(tagLineMatcher); + return; + } + + assertActiveParser(); + if (line.equals(END_MARKER)) { + // End tag + parseEndTag(); + return; + } + + // Multi-line message for single tag. + currentBuilder.append(line).append(System.lineSeparator()); + } + + private void assertNoActiveParser() { + TestFramework.check(currentBuilder == null, "Unexpected new tag while parsing block"); + } + + private void parseTagLine(Matcher tagLineMatcher) { + String tag = tagLineMatcher.group(1); + String message = tagLineMatcher.group(2); + switch (tag) { + case STDOUT -> stdoutMessages.add(message); + case TEST_LIST -> executedTests.add(message); + case PRINT_TIMES -> parsePrintTimes(message); + case VM_INFO -> currentBuilder = vmInfoBuilder; + case APPLICABLE_IR_RULES -> currentBuilder = applicableIrRules; + default -> throw new TestFrameworkException("unknown tag"); + } + } + + private void parsePrintTimes(String message) { + String[] split = message.split(","); + TestFramework.check(split.length == 2, "unexpected format"); + String methodName = split[0]; + try { + long duration = Long.parseLong(split[1]); + methodTimes.put(methodName, duration); + } catch (NumberFormatException e) { + throw new TestFrameworkException("invalid duration", e); + } + } + + private void assertActiveParser() { + TestFramework.check(currentBuilder != null, "Received non-tag line outside of any tag block"); + } + + private void parseEndTag() { + currentBuilder = null; + } + + public JavaMessages output() { + return new JavaMessages(new StdoutMessages(stdoutMessages), + new ExecutedTests(executedTests), + new MethodTimes(methodTimes), + applicableIrRules.toString(), + vmInfoBuilder.toString()); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java index b8a3f39c637..e47ecff4b2a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java @@ -23,28 +23,44 @@ package compiler.lib.ir_framework.driver.network.testvm.java; -import compiler.lib.ir_framework.test.network.MessageTag; +import compiler.lib.ir_framework.TestFramework; /** * Class to collect all Java messages sent from the Test VM to the Driver VM. */ public class JavaMessages { - private final String output; - private final boolean receivedStdOut; + private static final boolean PRINT_APPLICABLE_IR_RULES = Boolean.parseBoolean(System.getProperty("PrintApplicableIRRules", "false")); - public JavaMessages(String output, boolean receivedStdOut) { - this.output = output; - this.receivedStdOut = receivedStdOut; + private final StdoutMessages stdoutMessages; + private final ExecutedTests executedTests; + private final MethodTimes methodTimes; + private final String applicableIrRules; + private final String vmInfo; + + JavaMessages(StdoutMessages stdoutMessages, ExecutedTests executedTests, MethodTimes methodTimes, + String applicableIrRules, String vmInfo) { + this.stdoutMessages = stdoutMessages; + this.executedTests = executedTests; + this.methodTimes = methodTimes; + this.applicableIrRules = applicableIrRules; + this.vmInfo = vmInfo; } - public String output() { - return output; + public String applicableIRRules() { + return applicableIrRules; } - /** - * Return whether Test VM sent messages to be put on stdout (starting with {@link MessageTag#STDOUT}). - */ - public boolean hasStdOut() { - return receivedStdOut; + public String vmInfo() { + return vmInfo; + } + + public void print() { + stdoutMessages.print(); + methodTimes.print(); + executedTests.print(); + if (TestFramework.VERBOSE || PRINT_APPLICABLE_IR_RULES) { + System.out.println("Read Applicable IR Rules from Test VM:"); + System.out.println(applicableIrRules); + } } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/MethodTimes.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/MethodTimes.java new file mode 100644 index 00000000000..1b4cad52270 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/MethodTimes.java @@ -0,0 +1,85 @@ +/* + * 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.lib.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.test.network.MessageTag; + +import java.util.List; +import java.util.Map; + + +/** + * Class to collect all Java Messages sent with tag {@link MessageTag#PRINT_TIMES}. These are only generated when the + * user runs with {@code -DPrintTimes=true} and represent the execution times for methods. + */ +class MethodTimes implements JavaMessage { + private final Map methodTimes; + + public MethodTimes(Map methodTimes) { + this.methodTimes = methodTimes; + } + + @Override + public void print() { + if (methodTimes.isEmpty()) { + return; + } + + System.out.println(); + System.out.println("Test Execution Times"); + System.out.println("--------------------"); + + int maxWidthNames = maxMethodNameWidth(); + int maxDurationsWidth = maxDurationsWidth(); + List> sortedMethodTimes = sortByDurationAsc(); + + for (Map.Entry entry : sortedMethodTimes) { + System.out.printf("- %-" + (maxWidthNames + 3) + "s %" + maxDurationsWidth + "d ns%n", + entry.getKey() + ":", entry.getValue()); + } + + System.out.println(); + } + + private int maxMethodNameWidth() { + return methodTimes.keySet().stream() + .mapToInt(String::length) + .max() + .orElseThrow(); + } + + private int maxDurationsWidth() { + return methodTimes.values().stream() + .mapToInt(v -> Long.toString(v).length()) + .max() + .orElseThrow(); + } + + private List> sortByDurationAsc() { + return methodTimes.entrySet().stream() + .sorted(Map.Entry.comparingByValue()) + .toList(); + } + +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/StdoutMessages.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/StdoutMessages.java new file mode 100644 index 00000000000..11b23ad4237 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/StdoutMessages.java @@ -0,0 +1,54 @@ +/* + * 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.lib.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.test.network.MessageTag; + +import java.util.List; + + +/** + * Class to collect all Java Messages sent with tag {@link MessageTag#STDOUT}. These messages are generated at various + * places in the Test VM and are unconditionally shown in the Driver VM output. + */ +class StdoutMessages implements JavaMessage { + private final List messages; + + public StdoutMessages(List messages) { + this.messages = messages; + } + + @Override + public void print() { + if (messages.isEmpty()) { + return; + } + System.out.println(); + System.out.println("Test VM Messages"); + System.out.println("----------------"); + for (String message : messages) { + System.out.println("- " + message); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index 15d9a2e4e34..4b4ec0f8b26 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java @@ -47,9 +47,7 @@ import java.util.function.Function; * termination of the Test VM. IR rule indices start at 1. */ public class ApplicableIRRulesPrinter { - public static final String START = "##### ApplicableIRRules - used by TestFramework #####"; - public static final String END = "----- END -----"; - public static final int NO_RULE_APPLIED = -1; + public static final int NO_RULES = -1; private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); private static final List> LONG_GETTERS = Arrays.asList( @@ -131,11 +129,6 @@ public class ApplicableIRRulesPrinter { "zvkn" )); - public ApplicableIRRulesPrinter() { - output.append(START).append(System.lineSeparator()); - output.append(",{comma separated applied @IR rule ids}").append(System.lineSeparator()); - } - /** * Emits ",{ids}" where {ids} is either: * - indices of all @IR rules that should be applied, separated by a comma @@ -162,7 +155,7 @@ public class ApplicableIRRulesPrinter { if (irAnnos.length != 0) { output.append(m.getName()); if (validRules.isEmpty()) { - output.append("," + NO_RULE_APPLIED); + output.append("," + NO_RULES); } else { for (i = 0; i < validRules.size(); i++) { output.append(",").append(validRules.get(i)); @@ -524,8 +517,8 @@ public class ApplicableIRRulesPrinter { } public void emit() { - output.append(END); - TestVmSocket.sendWithTag(MessageTag.APPLICABLE_IR_RULES, output.toString()); + output.append(MessageTag.END_MARKER); + TestVmSocket.sendMultiLine(MessageTag.APPLICABLE_IR_RULES, output.toString()); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java index c5e94c32c08..ac52bcaaa98 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java @@ -594,8 +594,8 @@ public class TestVM { "Cannot overload @Test methods, but method " + m + " has " + overloads.size() + " overload" + (overloads.size() == 1 ? "" : "s") + ":" + overloads.stream().map(String::valueOf).collect(Collectors.joining("\n - ", "\n - ", "")) ); - TestFormat.check(!testMethodMap.containsKey(m.getName()), - "Cannot overload two @Test methods: " + m + ", " + testMethodMap.get(m.getName())); + TestFramework.check(!testMethodMap.containsKey(m.getName()), + "Cannot overload two @Test methods: " + m + ", " + testMethodMap.get(m.getName())); TestFormat.check(testAnno != null, m + " must be a method with a @Test annotation"); Check checkAnno = getAnnotation(m, Check.class); @@ -836,7 +836,6 @@ public class TestVM { * Once all framework tests are collected, they are run in this method. */ private void runTests() { - TreeMap durations = PRINT_TIMES ? new TreeMap<>() : null; long startTime = System.nanoTime(); List testList; boolean testFilterPresent = testFilterPresent(); @@ -865,7 +864,7 @@ public class TestVM { System.out.println("Run " + test.toString()); } if (testFilterPresent) { - TestVmSocket.send(MessageTag.TEST_LIST + "Run " + test.toString()); + TestVmSocket.sendWithTag(MessageTag.TEST_LIST, "Run " + test.toString()); } try { test.run(); @@ -880,10 +879,11 @@ public class TestVM { if (PRINT_TIMES) { long endTime = System.nanoTime(); long duration = (endTime - startTime); - durations.put(duration, test.getName()); if (VERBOSE) { - System.out.println("Done " + test.getName() + ": " + duration + " ns = " + (duration / 1000000) + " ms"); + System.out.println("Done " + test.getName() + ": " + duration + " ns = " + (duration / 1_000_000) + " ms"); } + // Will be correctly formatted later. + TestVmSocket.sendWithTag(MessageTag.PRINT_TIMES, test.getName() + "," + duration); } if (GC_AFTER) { System.out.println("doing GC"); @@ -891,14 +891,6 @@ public class TestVM { } } - // Print execution times - if (PRINT_TIMES) { - TestVmSocket.send(MessageTag.PRINT_TIMES + " Test execution times:"); - for (Map.Entry entry : durations.entrySet()) { - TestVmSocket.send(MessageTag.PRINT_TIMES + String.format("%-25s%15d ns%n", entry.getValue() + ":", entry.getKey())); - } - } - if (failures > 0) { // Finally, report all occurred exceptions in a nice format. String msg = System.lineSeparator() + System.lineSeparator() + "Test Failures (" + failures + ")" diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java index 2227c7760d5..8fc61ee9bad 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java @@ -31,15 +31,10 @@ import jdk.test.whitebox.WhiteBox; * Prints some Test VM info to the socket. */ public class VMInfoPrinter { - public static final String START_VM_INFO = "##### IRMatchingVMInfo - used by TestFramework #####"; - public static final String END_VM_INFO = "----- END VMInfo -----"; - private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); public static void emit() { StringBuilder vmInfo = new StringBuilder(); - vmInfo.append(START_VM_INFO).append(System.lineSeparator()); - vmInfo.append(":").append(System.lineSeparator()); // CPU feature independent info String cpuFeatures = WHITE_BOX.getCPUFeatures(); @@ -65,7 +60,7 @@ public class VMInfoPrinter { .append(useAVXIsDefault ? 1 : 0) .append(System.lineSeparator()); - vmInfo.append(END_VM_INFO); - TestVmSocket.sendWithTag(MessageTag.VM_INFO, vmInfo.toString()); + vmInfo.append(MessageTag.END_MARKER); + TestVmSocket.sendMultiLine(MessageTag.VM_INFO, vmInfo.toString()); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/MessageTag.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/MessageTag.java index 2981f203e65..66b89dc122d 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/MessageTag.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/MessageTag.java @@ -29,4 +29,5 @@ public class MessageTag { public static final String PRINT_TIMES = "[PRINT_TIMES]"; public static final String VM_INFO = "[VM_INFO]"; public static final String APPLICABLE_IR_RULES = "[APPLICABLE_IR_RULES]"; + public static final String END_MARKER = "#END#"; } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/TestVmSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/TestVmSocket.java index c1065c84320..37e59b9dcae 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/TestVmSocket.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/TestVmSocket.java @@ -25,6 +25,7 @@ package compiler.lib.ir_framework.test.network; import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.shared.TestRunException; +import compiler.lib.ir_framework.test.TestVM; import java.io.IOException; import java.io.PrintWriter; @@ -46,6 +47,20 @@ public class TestVmSocket { sendWithTag(MessageTag.STDOUT, message); } + /** + * Send a message with multiple lines to the Driver VM with a {@link MessageTag}. Not all messages are shown by + * default in the Driver VM output and require setting some property flags first like {@code -DPrintTimes=true}. + */ + public static void sendMultiLine(String tag, String message) { + if (REPRODUCE) { + // Debugging Test VM: Skip writing due to -DReproduce; + return; + } + + TestFramework.check(socket != null, "must be connected"); + writer.println(tag + System.lineSeparator() + message); + } + /** * Send a message to the Driver VM with a {@link MessageTag}. Not all messages are shown by default in the * Driver VM output and require setting some property flags first like {@code -DPrintTimes=true}. From d9f19b3d9e18186454f9e5dd4126ffc9a11e2171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Maillard?= Date: Tue, 24 Feb 2026 07:56:59 +0000 Subject: [PATCH 002/636] 8373251: C2: Ideal() returns nullptr for shift nodes after having modified the shift amount input Reviewed-by: chagedorn, mchevalier --- src/hotspot/share/opto/mulnode.cpp | 59 +++++++----- .../TestIdealReturnReplaceShiftAmount.java | 92 +++++++++++++++++++ .../TestMissingOptReplaceShiftAmount.java | 66 +++++++++++++ 3 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/igvn/TestIdealReturnReplaceShiftAmount.java create mode 100644 test/hotspot/jtreg/compiler/c2/igvn/TestMissingOptReplaceShiftAmount.java diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index ac7d1925667..9bdaa3b9f34 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -902,27 +902,28 @@ static bool mask_shift_amount(PhaseGVN* phase, const Node* shift_node, uint nBit } // Use this in ::Ideal only with shiftNode == this! -// Returns the masked shift amount if constant or 0 if not constant. -static uint mask_and_replace_shift_amount(PhaseGVN* phase, Node* shift_node, uint nBits) { +// Sets masked_shift to the masked shift amount if constant or 0 if not constant. +// Returns shift_node if the shift amount input node was modified, nullptr otherwise. +static Node* mask_and_replace_shift_amount(PhaseGVN* phase, Node* shift_node, uint nBits, uint& masked_shift) { int real_shift; - uint masked_shift; if (mask_shift_amount(phase, shift_node, nBits, real_shift, masked_shift)) { if (masked_shift == 0) { // Let Identity() handle 0 shift count. - return 0; + return nullptr; } if (real_shift != (int)masked_shift) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - if (igvn != nullptr) { - igvn->_worklist.push(shift_node); - } shift_node->set_req(2, phase->intcon(masked_shift)); // Replace shift count with masked value. + + // We need to notify the caller that the graph was reshaped, as Ideal needs + // to return the root of the reshaped graph if any change was made. + return shift_node; } - return masked_shift; + } else { + // Not a shift by a constant. + masked_shift = 0; } - // Not a shift by a constant. - return 0; + return nullptr; } // Called with @@ -974,7 +975,8 @@ Node* LShiftINode::Identity(PhaseGVN* phase) { } Node* LShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { - uint con = mask_and_replace_shift_amount(phase, this, bits_per_java_integer(bt)); + uint con; + Node* progress = mask_and_replace_shift_amount(phase, this, bits_per_java_integer(bt), con); if (con == 0) { return nullptr; } @@ -1113,7 +1115,7 @@ Node* LShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { return doubleShift; } - return nullptr; + return progress; } //------------------------------Ideal------------------------------------------ @@ -1274,7 +1276,9 @@ Node* RShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { if (t1 == nullptr) { return NodeSentinel; // Left input is an integer } - int shift = mask_and_replace_shift_amount(phase, this, bits_per_java_integer(bt)); + + uint shift; + Node* progress = mask_and_replace_shift_amount(phase, this, bits_per_java_integer(bt), shift); if (shift == 0) { return NodeSentinel; } @@ -1284,7 +1288,7 @@ Node* RShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { // Such expressions arise normally from shift chains like (byte)(x >> 24). const Node* and_node = in(1); if (and_node->Opcode() != Op_And(bt)) { - return nullptr; + return progress; } const TypeInteger* mask_t = phase->type(and_node->in(2))->isa_integer(bt); if (mask_t != nullptr && mask_t->is_con()) { @@ -1293,7 +1297,8 @@ Node* RShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { Node* shr_nomask = phase->transform(RShiftNode::make(and_node->in(1), in(2), bt)); return MulNode::make_and(shr_nomask, phase->integercon(maskbits >> shift, bt), bt); } - return nullptr; + + return progress; } Node* RShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) { @@ -1304,18 +1309,19 @@ Node* RShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) { if (progress != nullptr) { return progress; } - int shift = mask_and_replace_shift_amount(phase, this, BitsPerJavaInteger); + uint shift; + progress = mask_and_replace_shift_amount(phase, this, BitsPerJavaInteger, shift); assert(shift != 0, "handled by IdealIL"); // Check for "(short[i] <<16)>>16" which simply sign-extends const Node *shl = in(1); if (shl->Opcode() != Op_LShiftI) { - return nullptr; + return progress; } const TypeInt* left_shift_t = phase->type(shl->in(2))->isa_int(); if (left_shift_t == nullptr) { - return nullptr; + return progress; } if (shift == 16 && left_shift_t->is_con(16)) { Node *ld = shl->in(1); @@ -1347,7 +1353,7 @@ Node* RShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) { } } - return nullptr; + return progress; } const Type* RShiftNode::ValueIL(PhaseGVN* phase, BasicType bt) const { @@ -1503,7 +1509,8 @@ Node* URShiftINode::Identity(PhaseGVN* phase) { //------------------------------Ideal------------------------------------------ Node* URShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) { - int con = mask_and_replace_shift_amount(phase, this, BitsPerJavaInteger); + uint con; + Node* progress = mask_and_replace_shift_amount(phase, this, BitsPerJavaInteger, con); if (con == 0) { return nullptr; } @@ -1585,7 +1592,7 @@ Node* URShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) { } } - return nullptr; + return progress; } //------------------------------Value------------------------------------------ @@ -1675,7 +1682,8 @@ Node* URShiftLNode::Identity(PhaseGVN* phase) { //------------------------------Ideal------------------------------------------ Node* URShiftLNode::Ideal(PhaseGVN* phase, bool can_reshape) { - int con = mask_and_replace_shift_amount(phase, this, BitsPerJavaLong); + uint con; + Node* progress = mask_and_replace_shift_amount(phase, this, BitsPerJavaLong, con); if (con == 0) { return nullptr; } @@ -1739,7 +1747,8 @@ Node* URShiftLNode::Ideal(PhaseGVN* phase, bool can_reshape) { return new URShiftLNode(in11, phase->intcon(63)); } } - return nullptr; + + return progress; } //------------------------------Value------------------------------------------ diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestIdealReturnReplaceShiftAmount.java b/test/hotspot/jtreg/compiler/c2/igvn/TestIdealReturnReplaceShiftAmount.java new file mode 100644 index 00000000000..589da988606 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestIdealReturnReplaceShiftAmount.java @@ -0,0 +1,92 @@ +/* + * 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.c2.igvn; + +/* + * @test + * @bug 8373251 + * @summary In Ideal of shift nodes, we call mask_and_replace_shift_amount to reduce the + * shift amount. We need to make sure that Ideal returns something if this is + * the only modification taking place. Use -XX:VerifyIterativeGVN=100000 to + * verify the return value of Ideal if the hash has changed. + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * -XX:VerifyIterativeGVN=100000 + * ${test.main.class} + * @run main ${test.main.class} + * + */ + +public class TestIdealReturnReplaceShiftAmount { + static long lFld; + static int iFld; + + static void testLShiftI() { + // we need the loop so that the shift amount replacement happens + // during IGVN and not directly at parsing + for (int i = 0; i < 5; i++) { + iFld <<= (i + 32); + } + } + + static void testLShiftL() { + for (int i = 0; i < 5; i++) { + lFld <<= (i + 64); + } + } + + static void testRShiftI() { + for (int i = 0; i < 5; i++) { + iFld >>= (i + 32); + } + } + + static void testRShiftL() { + for (int i = 0; i < 5; i++) { + lFld >>= (i + 64); + } + } + + static void testURShiftI() { + for (int i = 0; i < 5; i++) { + iFld >>>= (i + 32); + } + } + + static void testURShiftL() { + for (int i = 0; i < 5; i++) { + lFld >>>= (i + 64); + } + } + + public static void main(String[] args) { + testLShiftI(); + testLShiftL(); + testRShiftI(); + testRShiftL(); + testURShiftI(); + testURShiftL(); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestMissingOptReplaceShiftAmount.java b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingOptReplaceShiftAmount.java new file mode 100644 index 00000000000..a2d6505c311 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingOptReplaceShiftAmount.java @@ -0,0 +1,66 @@ +/* + * 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.c2.igvn; + +/* + * @test + * @bug 8373251 + * @summary In Ideal of shift nodes, we call mask_and_replace_shift_amount to reduce the + * shift amount. We need to make sure that the updates are propagated if this is + * the only modification taking place. Use -XX:VerifyIterativeGVN=1110 to + * catch missing optimizations. + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:-TieredCompilation + * -XX:+StressIGVN -XX:StressSeed=342568167 + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * -XX:VerifyIterativeGVN=1110 + * ${test.main.class} + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:-TieredCompilation + * -XX:+StressIGVN + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * -XX:VerifyIterativeGVN=1110 + * ${test.main.class} + * @run main ${test.main.class} + */ + +public class TestMissingOptReplaceShiftAmount { + static long lFld; + static boolean flag; + + static void test() { + int x = 67; + for (int i = 0; i < 20; i++) { + if (flag) { + x = 0; + } + lFld <<= x; + lFld >>>= x; + } + } + + public static void main(String[] args) { + test(); + } +} From 827239f5f890a7eff7014f27d25537ae7a2f7faf Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 24 Feb 2026 08:14:38 +0000 Subject: [PATCH 003/636] 8378266: Update atomicAccess include after Atomic changes Reviewed-by: ayang, kbarrett --- src/hotspot/share/gc/epsilon/epsilonHeap.cpp | 3 +-- src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp | 1 - src/hotspot/share/gc/g1/g1FullCollector.inline.hpp | 1 - src/hotspot/share/gc/g1/g1HeapRegionManager.cpp | 1 - src/hotspot/share/gc/parallel/parMarkBitMap.cpp | 3 +-- src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp | 3 ++- src/hotspot/share/gc/parallel/psParallelCompact.cpp | 1 - src/hotspot/share/gc/parallel/spaceCounters.cpp | 3 +-- src/hotspot/share/gc/shared/parallelCleaning.cpp | 1 - 9 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index 004a36147fc..e32daa3d79e 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp @@ -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. * Copyright (c) 2017, 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -34,7 +34,6 @@ #include "memory/metaspaceUtils.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/globals.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp index c14c5658127..7da0066e2f1 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp @@ -23,7 +23,6 @@ */ #include "gc/g1/g1ConcurrentRefineStats.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/timer.hpp" void G1ConcurrentRefineStats::add_atomic(G1ConcurrentRefineStats* other) { diff --git a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp index 0c201f0e43f..ef6344d349d 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp @@ -30,7 +30,6 @@ #include "gc/g1/g1FullGCHeapRegionAttr.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "oops/oopsHierarchy.hpp" -#include "runtime/atomicAccess.hpp" bool G1FullCollector::is_compacting(oop obj) const { return _region_attr_table.is_compacting(cast_from_oop(obj)); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index 44897c8a277..fdd3b919590 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -34,7 +34,6 @@ #include "jfr/jfrEvents.hpp" #include "logging/logStream.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/orderAccess.hpp" #include "utilities/bitMap.inline.hpp" diff --git a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp index c613c8615f0..bbd6669df8d 100644 --- a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp +++ b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp @@ -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 @@ -28,7 +28,6 @@ #include "memory/memoryReserver.hpp" #include "nmt/memTracker.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index 7515031f01f..ff7a0aee088 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -30,6 +30,7 @@ #include "gc/shared/gcPolicyCounters.hpp" #include "gc/shared/gcUtil.hpp" #include "logging/log.hpp" +#include "runtime/atomicAccess.hpp" #include "runtime/timer.hpp" #include "utilities/align.hpp" diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index 4c6ea01e45f..d03bc3cda45 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -84,7 +84,6 @@ #include "oops/methodData.hpp" #include "oops/objArrayKlass.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" #include "runtime/safepoint.hpp" diff --git a/src/hotspot/share/gc/parallel/spaceCounters.cpp b/src/hotspot/share/gc/parallel/spaceCounters.cpp index 8f24373abcb..e41b36c9ed2 100644 --- a/src/hotspot/share/gc/parallel/spaceCounters.cpp +++ b/src/hotspot/share/gc/parallel/spaceCounters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -25,7 +25,6 @@ #include "gc/parallel/spaceCounters.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/shared/parallelCleaning.cpp b/src/hotspot/share/gc/shared/parallelCleaning.cpp index 1a0d536f3b3..8e275564a9f 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.cpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp @@ -28,7 +28,6 @@ #include "gc/shared/parallelCleaning.hpp" #include "logging/log.hpp" #include "oops/klass.inline.hpp" -#include "runtime/atomicAccess.hpp" CodeCacheUnloadingTask::CodeCacheUnloadingTask(bool unloading_occurred) : _unloading_occurred(unloading_occurred), From 5ed7b3ed750ae35a6369fb472e24ebc78f7d0be9 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Tue, 24 Feb 2026 08:32:39 +0000 Subject: [PATCH 004/636] 8378439: Remove unused methods in HSpaceCounters Reviewed-by: tschatzl --- src/hotspot/share/gc/shared/hSpaceCounters.cpp | 11 ----------- src/hotspot/share/gc/shared/hSpaceCounters.hpp | 7 ------- 2 files changed, 18 deletions(-) diff --git a/src/hotspot/share/gc/shared/hSpaceCounters.cpp b/src/hotspot/share/gc/shared/hSpaceCounters.cpp index 818d7422fba..a873bc2f45c 100644 --- a/src/hotspot/share/gc/shared/hSpaceCounters.cpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.cpp @@ -81,14 +81,3 @@ void HSpaceCounters::update_all(size_t capacity, size_t used) { update_capacity(capacity); update_used(used); } - -DEBUG_ONLY( - // for security reasons, we do not allow arbitrary reads from - // the counters as they may live in shared memory. - jlong HSpaceCounters::used() { - return _used->get_value(); - } - jlong HSpaceCounters::capacity() { - return _used->get_value(); - } -) diff --git a/src/hotspot/share/gc/shared/hSpaceCounters.hpp b/src/hotspot/share/gc/shared/hSpaceCounters.hpp index b9a6011bd65..cd008ac6dd3 100644 --- a/src/hotspot/share/gc/shared/hSpaceCounters.hpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.hpp @@ -54,13 +54,6 @@ class HSpaceCounters: public CHeapObj { void update_all(size_t capacity, size_t used); - DEBUG_ONLY( - // for security reasons, we do not allow arbitrary reads from - // the counters as they may live in shared memory. - jlong used(); - jlong capacity(); - ) - const char* name_space() const { return _name_space; } }; #endif // SHARE_GC_SHARED_HSPACECOUNTERS_HPP From 35ed56afc73a83fd7eb856279550cc3e5546a13a Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 24 Feb 2026 08:52:09 +0000 Subject: [PATCH 005/636] 8378347: AIX version checks for 7.1 and 5.X are obsolete Reviewed-by: mdoerr, asteiner --- src/hotspot/os/aix/libodm_aix.cpp | 14 ++++++------- src/hotspot/os/aix/libodm_aix.hpp | 6 +++--- src/hotspot/os/aix/os_aix.cpp | 35 +++++++++++-------------------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/hotspot/os/aix/libodm_aix.cpp b/src/hotspot/os/aix/libodm_aix.cpp index 38e8067181a..57eee47c098 100644 --- a/src/hotspot/os/aix/libodm_aix.cpp +++ b/src/hotspot/os/aix/libodm_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015, 2019 SAP SE. All rights reserved. + * Copyright (c) 2015, 2026 SAP SE. 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 @@ -63,13 +63,12 @@ dynamicOdm::~dynamicOdm() { void odmWrapper::clean_data() { if (_data) { permit_forbidden_function::free(_data); _data = nullptr; } } -int odmWrapper::class_offset(const char *field, bool is_aix_5) +int odmWrapper::class_offset(const char *field) { assert(has_class(), "initialization"); for (int i = 0; i < odm_class()->nelem; i++) { if (strcmp(odm_class()->elem[i].elemname, field) == 0) { int offset = odm_class()->elem[i].offset; - if (is_aix_5) { offset += LINK_VAL_OFFSET; } return offset; } } @@ -88,11 +87,10 @@ void odmWrapper::determine_os_kernel_version(uint32_t* p_ver) { return; } int voff, roff, moff, foff; - bool is_aix_5 = (major_aix_version == 5); - voff = odm.class_offset("ver", is_aix_5); - roff = odm.class_offset("rel", is_aix_5); - moff = odm.class_offset("mod", is_aix_5); - foff = odm.class_offset("fix", is_aix_5); + voff = odm.class_offset("ver"); + roff = odm.class_offset("rel"); + moff = odm.class_offset("mod"); + foff = odm.class_offset("fix"); if (voff == -1 || roff == -1 || moff == -1 || foff == -1) { trcVerbose("try_determine_os_kernel_version: could not get offsets"); return; diff --git a/src/hotspot/os/aix/libodm_aix.hpp b/src/hotspot/os/aix/libodm_aix.hpp index 924ccaf8c51..11e67a4f5ae 100644 --- a/src/hotspot/os/aix/libodm_aix.hpp +++ b/src/hotspot/os/aix/libodm_aix.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015, 2024 SAP SE. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026 SAP SE. 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 @@ -82,7 +82,7 @@ class odmWrapper : private dynamicOdm { CLASS_SYMBOL odm_class() { return _odm_class; } bool has_class() { return odm_class() != (CLASS_SYMBOL)-1; } - int class_offset(const char *field, bool is_aix_5); + int class_offset(const char *field); char* data() { return _data; } char* retrieve_obj(const char* name = nullptr) { diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 61d3f36683c..19cdd6333f8 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -122,12 +122,6 @@ extern "C" int mread_real_time(timebasestruct_t *t, size_t size_of_timebasestruct_t); -#if !defined(_AIXVERSION_610) -extern "C" int getthrds64(pid_t, struct thrdentry64*, int, tid64_t*, int); -extern "C" int getprocs64(procentry64*, int, fdsinfo*, int, pid_t*, int); -extern "C" int getargs(procsinfo*, int, char*, int); -#endif - #define MAX_PATH (2 * K) // for multipage initialization error analysis (in 'g_multipage_error') @@ -216,7 +210,7 @@ static address g_brk_at_startup = nullptr; // shmctl(). Different shared memory regions can have different page // sizes. // -// More information can be found at AIBM info center: +// More information can be found at IBM info center: // http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=/com.ibm.aix.prftungd/doc/prftungd/multiple_page_size_app_support.htm // static struct { @@ -2530,23 +2524,18 @@ void os::Aix::initialize_os_info() { assert(minor > 0, "invalid OS release"); _os_version = (major << 24) | (minor << 16); char ver_str[20] = {0}; - const char* name_str = "unknown OS"; - if (strcmp(uts.sysname, "AIX") == 0) { - // We run on AIX. We do not support versions older than AIX 7.1. - // Determine detailed AIX version: Version, Release, Modification, Fix Level. - odmWrapper::determine_os_kernel_version(&_os_version); - if (os_version_short() < 0x0701) { - log_warning(os)("AIX releases older than AIX 7.1 are not supported."); - assert(false, "AIX release too old."); - } - name_str = "AIX"; - jio_snprintf(ver_str, sizeof(ver_str), "%u.%u.%u.%u", - major, minor, (_os_version >> 8) & 0xFF, _os_version & 0xFF); - } else { - assert(false, "%s", name_str); + // We do not support versions older than AIX 7.2 TL 5. + // Determine detailed AIX version: Version, Release, Modification, Fix Level. + odmWrapper::determine_os_kernel_version(&_os_version); + if (_os_version < 0x07020500) { + log_warning(os)("AIX releases older than AIX 7.2 TL 5 are not supported."); + assert(false, "AIX release too old."); } - log_info(os)("We run on %s %s", name_str, ver_str); + + jio_snprintf(ver_str, sizeof(ver_str), "%u.%u.%u.%u", + major, minor, (_os_version >> 8) & 0xFF, _os_version & 0xFF); + log_info(os)("We run on AIX %s", ver_str); } guarantee(_os_version, "Could not determine AIX release"); From bc9c6c6af98fdbe17be4f7dad1270d350cb9dacb Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Tue, 24 Feb 2026 09:15:23 +0000 Subject: [PATCH 006/636] 8377996: [REDO] NMT: Consolidate [Virtual/Committed/Reserved]Regions into one structure Reviewed-by: phubner, jsjolen --- src/hotspot/share/nmt/memBaseline.cpp | 7 +- src/hotspot/share/nmt/memMapPrinter.cpp | 4 +- src/hotspot/share/nmt/memReporter.cpp | 38 ++-- src/hotspot/share/nmt/memReporter.hpp | 4 +- src/hotspot/share/nmt/regionsTree.cpp | 6 +- src/hotspot/share/nmt/regionsTree.hpp | 22 ++- src/hotspot/share/nmt/regionsTree.inline.hpp | 18 +- .../share/nmt/virtualMemoryTracker.cpp | 48 ++--- .../share/nmt/virtualMemoryTracker.hpp | 150 +++++---------- test/hotspot/gtest/nmt/test_regions_tree.cpp | 26 +-- .../runtime/test_committed_virtualmemory.cpp | 37 ++-- .../runtime/test_virtualMemoryTracker.cpp | 172 +++++++++--------- 12 files changed, 239 insertions(+), 293 deletions(-) diff --git a/src/hotspot/share/nmt/memBaseline.cpp b/src/hotspot/share/nmt/memBaseline.cpp index 118e3ec64c0..65168fd4e09 100644 --- a/src/hotspot/share/nmt/memBaseline.cpp +++ b/src/hotspot/share/nmt/memBaseline.cpp @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -168,12 +168,13 @@ int compare_allocation_site(const VirtualMemoryAllocationSite& s1, } bool MemBaseline::aggregate_virtual_memory_allocation_sites() { + SortedLinkedList allocation_sites; VirtualMemoryAllocationSite* site; bool failed_oom = false; - _vma_allocations->visit_reserved_regions([&](ReservedMemoryRegion& rgn) { - VirtualMemoryAllocationSite tmp(*rgn.call_stack(), rgn.mem_tag()); + _vma_allocations->visit_reserved_regions([&](VirtualMemoryRegion& rgn) { + VirtualMemoryAllocationSite tmp(*rgn.reserved_call_stack(), rgn.mem_tag()); site = allocation_sites.find(tmp); if (site == nullptr) { LinkedListNode* node = diff --git a/src/hotspot/share/nmt/memMapPrinter.cpp b/src/hotspot/share/nmt/memMapPrinter.cpp index 9a2fe166d3d..639e06292fc 100644 --- a/src/hotspot/share/nmt/memMapPrinter.cpp +++ b/src/hotspot/share/nmt/memMapPrinter.cpp @@ -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. * Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -149,7 +149,7 @@ public: } } - bool do_allocation_site(const ReservedMemoryRegion* rgn) override { + bool do_allocation_site(const VirtualMemoryRegion* rgn) override { // Cancel iteration if we run out of memory (add returns false); return add(rgn->base(), rgn->end(), rgn->mem_tag()); } diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index 772bda2885b..27a94ec7bc0 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -395,14 +395,14 @@ int MemDetailReporter::report_virtual_memory_allocation_sites() { void MemDetailReporter::report_virtual_memory_map() { // Virtual memory map always in base address order output()->print_cr("Virtual memory map:"); - _baseline.virtual_memory_allocations()->visit_reserved_regions([&](ReservedMemoryRegion& rgn) { + _baseline.virtual_memory_allocations()->visit_reserved_regions([&](VirtualMemoryRegion& rgn) { report_virtual_memory_region(&rgn); return true; }); } -void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* reserved_rgn) { - assert(reserved_rgn != nullptr, "null pointer"); +void MemDetailReporter::report_virtual_memory_region(const VirtualMemoryRegion* rgn) { + assert(rgn != nullptr, "null pointer"); // We don't bother about reporting peaks here. // That is because peaks - in the context of virtual memory, peak of committed areas - make little sense @@ -414,16 +414,16 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* // usage *by callsite*. // Don't report if size is too small. - if (amount_in_current_scale(reserved_rgn->size()) == 0) return; + if (amount_in_current_scale(rgn->size()) == 0) return; outputStream* out = output(); const char* scale = current_scale(); - const NativeCallStack* stack = reserved_rgn->call_stack(); - bool all_committed = reserved_rgn->size() == _baseline.virtual_memory_allocations()->committed_size(*reserved_rgn); + const NativeCallStack* stack = rgn->reserved_call_stack(); + bool all_committed = rgn->size() == _baseline.virtual_memory_allocations()->committed_size(*rgn); const char* region_type = (all_committed ? "reserved and committed" : "reserved"); out->cr(); - print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size()); - out->print(" for %s", NMTUtil::tag_to_name(reserved_rgn->mem_tag())); + print_virtual_memory_region(region_type, rgn->base(), rgn->size()); + out->print(" for %s", NMTUtil::tag_to_name(rgn->mem_tag())); if (stack->is_empty()) { out->cr(); } else { @@ -433,9 +433,9 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* if (all_committed) { bool reserved_and_committed = false; - _baseline.virtual_memory_allocations()->visit_committed_regions(*reserved_rgn, - [&](CommittedMemoryRegion& committed_rgn) { - if (committed_rgn.equals(*reserved_rgn)) { + _baseline.virtual_memory_allocations()->visit_committed_regions(*rgn, + [&](VirtualMemoryRegion& committed_rgn) { + if (committed_rgn.equals(*rgn)) { // One region spanning the entire reserved region, with the same stack trace. // Don't print this regions because the "reserved and committed" line above // already indicates that the region is committed. @@ -450,13 +450,13 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* } } - auto print_committed_rgn = [&](const CommittedMemoryRegion& crgn) { + auto print_committed_rgn = [&](const VirtualMemoryRegion& rgn) { // Don't report if size is too small - if (amount_in_current_scale(crgn.size()) == 0) return; - stack = crgn.call_stack(); + if (amount_in_current_scale(rgn.size()) == 0) return; + stack = rgn.committed_call_stack(); out->cr(); INDENT_BY(8, - print_virtual_memory_region("committed", crgn.base(), crgn.size()); + print_virtual_memory_region("committed", rgn.base(), rgn.size()); if (stack->is_empty()) { out->cr(); } else { @@ -466,9 +466,9 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* ) }; - _baseline.virtual_memory_allocations()->visit_committed_regions(*reserved_rgn, - [&](CommittedMemoryRegion& crgn) { - print_committed_rgn(crgn); + _baseline.virtual_memory_allocations()->visit_committed_regions(*rgn, + [&](VirtualMemoryRegion& committed_rgn) { + print_committed_rgn(committed_rgn); return true; }); } diff --git a/src/hotspot/share/nmt/memReporter.hpp b/src/hotspot/share/nmt/memReporter.hpp index bab8de138d0..0d7e7344608 100644 --- a/src/hotspot/share/nmt/memReporter.hpp +++ b/src/hotspot/share/nmt/memReporter.hpp @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -178,7 +178,7 @@ class MemDetailReporter : public MemSummaryReporter { int report_virtual_memory_allocation_sites(); // Report a virtual memory region - void report_virtual_memory_region(const ReservedMemoryRegion* rgn); + void report_virtual_memory_region(const VirtualMemoryRegion* rgn); }; /* diff --git a/src/hotspot/share/nmt/regionsTree.cpp b/src/hotspot/share/nmt/regionsTree.cpp index 83306cbc14f..1a87d051928 100644 --- a/src/hotspot/share/nmt/regionsTree.cpp +++ b/src/hotspot/share/nmt/regionsTree.cpp @@ -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 @@ -58,9 +58,9 @@ void RegionsTree::print_on(outputStream* st) { } #endif -size_t RegionsTree::committed_size(const ReservedMemoryRegion& rgn) { +size_t RegionsTree::committed_size(const VirtualMemoryRegion& rgn) { size_t result = 0; - visit_committed_regions(rgn, [&](CommittedMemoryRegion& crgn) { + visit_committed_regions(rgn, [&](VirtualMemoryRegion& crgn) { result += crgn.size(); return true; }); diff --git a/src/hotspot/share/nmt/regionsTree.hpp b/src/hotspot/share/nmt/regionsTree.hpp index 2e1b37d0c1a..4b27423db8c 100644 --- a/src/hotspot/share/nmt/regionsTree.hpp +++ b/src/hotspot/share/nmt/regionsTree.hpp @@ -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 @@ -29,8 +29,7 @@ #include "nmt/vmatree.hpp" -class ReservedMemoryRegion; -class CommittedMemoryRegion; +class VirtualMemoryRegion; // RegionsTree extends VMATree to add some more specific API and also defines a helper // for processing the tree nodes in a shorter and more meaningful way. class RegionsTree : public VMATree { @@ -46,7 +45,7 @@ class RegionsTree : public VMATree { _with_storage(other._with_storage) {} RegionsTree& operator=(const RegionsTree& other) = delete; - ReservedMemoryRegion find_reserved_region(address addr); + VirtualMemoryRegion find_reserved_region(address addr); void commit_region(address addr, size_t size, const NativeCallStack& stack, SummaryDiff& diff); void uncommit_region(address addr, size_t size, SummaryDiff& diff); @@ -71,6 +70,7 @@ class RegionsTree : public VMATree { return position() - other.position(); } inline NativeCallStackStorage::StackIndex out_stack_index() const { return _node->val().out.reserved_stack(); } + inline NativeCallStackStorage::StackIndex out_committed_stack_index() const { return _node->val().out.committed_stack(); } inline MemTag in_tag() const { return _node->val().in.mem_tag(); } inline MemTag out_tag() const { return _node->val().out.mem_tag(); } inline void set_in_tag(MemTag tag) { _node->val().in.set_tag(tag); } @@ -81,7 +81,7 @@ class RegionsTree : public VMATree { DEBUG_ONLY(void print_on(outputStream* st);) template - void visit_committed_regions(const ReservedMemoryRegion& rgn, F func); + void visit_committed_regions(const VirtualMemoryRegion& rgn, F func); template void visit_reserved_regions(F func); @@ -90,7 +90,7 @@ class RegionsTree : public VMATree { return RegionData(_ncs_storage.push(ncs), tag); } - inline const NativeCallStack stack(NodeHelper& node) { + inline const NativeCallStack reserved_stack(NodeHelper& node) { if (!_with_storage) { return NativeCallStack::empty_stack(); } @@ -98,7 +98,15 @@ class RegionsTree : public VMATree { return _ncs_storage.get(si); } - size_t committed_size(const ReservedMemoryRegion& rgn); + inline const NativeCallStack committed_stack(NodeHelper& node) { + if (!_with_storage) { + return NativeCallStack::empty_stack(); + } + NativeCallStackStorage::StackIndex si = node.out_committed_stack_index(); + return _ncs_storage.get(si); + } + + size_t committed_size(const VirtualMemoryRegion& rgn); }; #endif // NMT_REGIONSTREE_HPP diff --git a/src/hotspot/share/nmt/regionsTree.inline.hpp b/src/hotspot/share/nmt/regionsTree.inline.hpp index 98cfa0e7f2c..793a5c5f1fa 100644 --- a/src/hotspot/share/nmt/regionsTree.inline.hpp +++ b/src/hotspot/share/nmt/regionsTree.inline.hpp @@ -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 @@ -29,7 +29,7 @@ #include "nmt/virtualMemoryTracker.hpp" template -void RegionsTree::visit_committed_regions(const ReservedMemoryRegion& rgn, F func) { +void RegionsTree::visit_committed_regions(const VirtualMemoryRegion& rgn, F func) { position start = (position)rgn.base(); size_t end = reinterpret_cast(rgn.end()) + 1; size_t comm_size = 0; @@ -38,8 +38,12 @@ void RegionsTree::visit_committed_regions(const ReservedMemoryRegion& rgn, F fun visit_range_in_order(start, end, [&](Node* node) { NodeHelper curr(node); if (prev.is_valid() && prev.is_committed_begin()) { - CommittedMemoryRegion cmr((address)prev.position(), curr.distance_from(prev), stack(prev)); - if (!func(cmr)) { + VirtualMemoryRegion rgn((address)prev.position(), + curr.distance_from(prev), + reserved_stack(prev), + committed_stack(prev), + prev.out_tag()); + if (!func(rgn)) { return false; } } @@ -63,13 +67,13 @@ void RegionsTree::visit_reserved_regions(F func) { } prev = curr; if (curr.is_released_begin() || begin_node.out_tag() != curr.out_tag()) { - auto st = stack(begin_node); + auto st = reserved_stack(begin_node); if (rgn_size == 0) { prev.clear_node(); return true; } - ReservedMemoryRegion rmr((address)begin_node.position(), rgn_size, st, begin_node.out_tag()); - if (!func(rmr)) { + VirtualMemoryRegion rgn((address)begin_node.position(), rgn_size, st, begin_node.out_tag()); + if (!func(rgn)) { return false; } rgn_size = 0; diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index d676d93e040..4e4138f81a2 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, 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 @@ -193,14 +193,14 @@ bool VirtualMemoryTracker::Instance::print_containing_region(const void* p, outp } bool VirtualMemoryTracker::print_containing_region(const void* p, outputStream* st) { - ReservedMemoryRegion rmr = tree()->find_reserved_region((address)p); - if (!rmr.contain_address((address)p)) { + VirtualMemoryRegion rgn = tree()->find_reserved_region((address)p); + if (!rgn.is_valid() || !rgn.contain_address((address)p)) { return false; } st->print_cr(PTR_FORMAT " in mmap'd memory region [" PTR_FORMAT " - " PTR_FORMAT "], tag %s", - p2i(p), p2i(rmr.base()), p2i(rmr.end()), NMTUtil::tag_to_enum_name(rmr.mem_tag())); + p2i(p), p2i(rgn.base()), p2i(rgn.end()), NMTUtil::tag_to_enum_name(rgn.mem_tag())); if (MemTracker::tracking_level() == NMT_detail) { - rmr.call_stack()->print_on(st); + rgn.reserved_call_stack()->print_on(st); } st->cr(); return true; @@ -213,7 +213,7 @@ bool VirtualMemoryTracker::Instance::walk_virtual_memory(VirtualMemoryWalker* wa bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) { bool ret = true; - tree()->visit_reserved_regions([&](ReservedMemoryRegion& rgn) { + tree()->visit_reserved_regions([&](VirtualMemoryRegion& rgn) { if (!walker->do_allocation_site(&rgn)) { ret = false; return false; @@ -223,29 +223,29 @@ bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) { return ret; } -size_t VirtualMemoryTracker::committed_size(const ReservedMemoryRegion* rmr) { +size_t VirtualMemoryTracker::committed_size(const VirtualMemoryRegion* rgn) { size_t result = 0; - tree()->visit_committed_regions(*rmr, [&](CommittedMemoryRegion& crgn) { + tree()->visit_committed_regions(*rgn, [&](VirtualMemoryRegion& crgn) { result += crgn.size(); return true; }); return result; } -size_t VirtualMemoryTracker::Instance::committed_size(const ReservedMemoryRegion* rmr) { +size_t VirtualMemoryTracker::Instance::committed_size(const VirtualMemoryRegion* rgn) { assert(_tracker != nullptr, "Sanity check"); - return _tracker->committed_size(rmr); + return _tracker->committed_size(rgn); } -address VirtualMemoryTracker::Instance::thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr) { +address VirtualMemoryTracker::Instance::thread_stack_uncommitted_bottom(const VirtualMemoryRegion* rgn) { assert(_tracker != nullptr, "Sanity check"); - return _tracker->thread_stack_uncommitted_bottom(rmr); + return _tracker->thread_stack_uncommitted_bottom(rgn); } -address VirtualMemoryTracker::thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr) { - address bottom = rmr->base(); - address top = rmr->end(); - tree()->visit_committed_regions(*rmr, [&](CommittedMemoryRegion& crgn) { +address VirtualMemoryTracker::thread_stack_uncommitted_bottom(const VirtualMemoryRegion* rgn) { + address bottom = rgn->base(); + address top = rgn->end(); + tree()->visit_committed_regions(*rgn, [&](VirtualMemoryRegion& crgn) { address committed_top = crgn.base() + crgn.size(); if (committed_top < top) { // committed stack guard pages, skip them @@ -299,7 +299,7 @@ class SnapshotThreadStackWalker : public VirtualMemoryWalker { public: SnapshotThreadStackWalker() {} - bool do_allocation_site(const ReservedMemoryRegion* rgn) { + bool do_allocation_site(const VirtualMemoryRegion* rgn) { if (MemTracker::NmtVirtualMemoryLocker::is_safe_to_use()) { assert_lock_strong(NmtVirtualMemory_lock); } @@ -340,19 +340,19 @@ void VirtualMemoryTracker::Instance::snapshot_thread_stacks() { walk_virtual_memory(&walker); } -ReservedMemoryRegion RegionsTree::find_reserved_region(address addr) { - ReservedMemoryRegion rmr; - auto contain_region = [&](ReservedMemoryRegion& region_in_tree) { +VirtualMemoryRegion RegionsTree::find_reserved_region(address addr) { + VirtualMemoryRegion rgn; + auto contain_region = [&](VirtualMemoryRegion& region_in_tree) { if (region_in_tree.contain_address(addr)) { - rmr = region_in_tree; + rgn = region_in_tree; return false; } return true; }; visit_reserved_regions(contain_region); - return rmr; + return rgn; } -bool CommittedMemoryRegion::equals(const ReservedMemoryRegion& rmr) const { - return size() == rmr.size() && call_stack()->equals(*(rmr.call_stack())); +bool VirtualMemoryRegion::equals_including_stacks(const VirtualMemoryRegion& rgn) const { + return size() == rgn.size() && committed_call_stack()->equals(*(rgn.reserved_call_stack())); } diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index c51b53194e6..3ed8bf93e03 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, 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 @@ -194,15 +194,38 @@ class VirtualMemorySummary : AllStatic { */ class VirtualMemoryRegion { private: - address _base_address; - size_t _size; + address _base_address; + size_t _size; + MemTag _mem_tag; + NativeCallStack _reserved_stack; + NativeCallStack _committed_stack; public: + VirtualMemoryRegion() : + _base_address(nullptr), _size(0), _mem_tag(mtNone), + _reserved_stack(NativeCallStack::empty_stack()) , + _committed_stack(NativeCallStack::empty_stack()) {} + VirtualMemoryRegion(address addr, size_t size) : - _base_address(addr), _size(size) { + _base_address(addr), _size(size), _mem_tag(mtNone), + _reserved_stack(NativeCallStack::empty_stack()) , + _committed_stack(NativeCallStack::empty_stack()) { assert(addr != nullptr, "Invalid address"); assert(size > 0, "Invalid size"); - } + } + + VirtualMemoryRegion(address addr, size_t size, const NativeCallStack& reserved_stack, const NativeCallStack& committed_stack, MemTag mem_tag = mtNone) : + _base_address(addr), _size(size), _mem_tag(mem_tag), + _reserved_stack(reserved_stack), + _committed_stack(committed_stack) { + assert(addr != nullptr, "Invalid address"); + assert(size > 0, "Invalid size"); + } + + VirtualMemoryRegion(address addr, size_t size, const NativeCallStack& stack, MemTag mem_tag = mtNone) + : _base_address(addr), _size(size), _mem_tag(mem_tag), + _reserved_stack(stack), + _committed_stack(NativeCallStack::empty_stack()) {} inline address base() const { return _base_address; } inline address end() const { return base() + size(); } @@ -211,48 +234,18 @@ class VirtualMemoryRegion { inline bool is_empty() const { return size() == 0; } inline bool contain_address(address addr) const { + assert(is_valid(), "sanity"); return (addr >= base() && addr < end()); } - - inline bool contain_region(address addr, size_t size) const { - return contain_address(addr) && contain_address(addr + size - 1); - } - - inline bool same_region(address addr, size_t sz) const { - return (addr == base() && sz == size()); - } - - + private: inline bool overlap_region(address addr, size_t sz) const { assert(sz > 0, "Invalid size"); assert(size() > 0, "Invalid size"); + assert(is_valid(), "sanity"); return MAX2(addr, base()) < MIN2(addr + sz, end()); } - inline bool adjacent_to(address addr, size_t sz) const { - return (addr == end() || (addr + sz) == base()); - } - - void exclude_region(address addr, size_t sz) { - assert(contain_region(addr, sz), "Not containment"); - assert(addr == base() || addr + sz == end(), "Can not exclude from middle"); - size_t new_size = size() - sz; - - if (addr == base()) { - set_base(addr + sz); - } - set_size(new_size); - } - - void expand_region(address addr, size_t sz) { - assert(adjacent_to(addr, sz), "Not adjacent regions"); - if (base() == addr + sz) { - set_base(addr); - } - set_size(size() + sz); - } - // Returns 0 if regions overlap; 1 if this region follows rgn; // -1 if this region precedes rgn. inline int compare(const VirtualMemoryRegion& rgn) const { @@ -266,86 +259,27 @@ class VirtualMemoryRegion { } } + public: // Returns true if regions overlap, false otherwise. inline bool equals(const VirtualMemoryRegion& rgn) const { return compare(rgn) == 0; } - protected: - void set_base(address base) { - assert(base != nullptr, "Sanity check"); - _base_address = base; - } + bool equals_including_stacks(const VirtualMemoryRegion& other) const; + inline const NativeCallStack* committed_call_stack() const { return &_committed_stack; } - void set_size(size_t size) { - assert(size > 0, "Sanity check"); - _size = size; - } -}; + bool is_valid() const { return base() != nullptr && size() != 0;} + inline const NativeCallStack* reserved_call_stack() const { return &_reserved_stack; } -class CommittedMemoryRegion : public VirtualMemoryRegion { - private: - NativeCallStack _stack; - - public: - CommittedMemoryRegion() - : VirtualMemoryRegion((address)1, 1), _stack(NativeCallStack::empty_stack()) { } - - CommittedMemoryRegion(address addr, size_t size, const NativeCallStack& stack) - : VirtualMemoryRegion(addr, size), _stack(stack) { } - - inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; } - inline const NativeCallStack* call_stack() const { return &_stack; } - bool equals(const ReservedMemoryRegion& other) const; -}; - -class ReservedMemoryRegion : public VirtualMemoryRegion { - private: - NativeCallStack _stack; - MemTag _mem_tag; - - public: - bool is_valid() { return base() != (address)1 && size() != 1;} - - ReservedMemoryRegion() - : VirtualMemoryRegion((address)1, 1), _stack(NativeCallStack::empty_stack()), _mem_tag(mtNone) { } - - ReservedMemoryRegion(address base, size_t size, const NativeCallStack& stack, - MemTag mem_tag = mtNone) - : VirtualMemoryRegion(base, size), _stack(stack), _mem_tag(mem_tag) { } - - - ReservedMemoryRegion(address base, size_t size) - : VirtualMemoryRegion(base, size), _stack(NativeCallStack::empty_stack()), _mem_tag(mtNone) { } - - // Copy constructor - ReservedMemoryRegion(const ReservedMemoryRegion& rr) - : VirtualMemoryRegion(rr.base(), rr.size()) { - *this = rr; - } - - inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; } - inline const NativeCallStack* call_stack() const { return &_stack; } - - inline MemTag mem_tag() const { return _mem_tag; } - - ReservedMemoryRegion& operator= (const ReservedMemoryRegion& other) { - set_base(other.base()); - set_size(other.size()); - - _stack = *other.call_stack(); - _mem_tag = other.mem_tag(); - - return *this; - } + inline MemTag mem_tag() const { return _mem_tag; } const char* tag_name() const { return NMTUtil::tag_to_name(_mem_tag); } }; class VirtualMemoryWalker : public StackObj { public: - virtual bool do_allocation_site(const ReservedMemoryRegion* rgn) { return false; } + virtual bool do_allocation_site(const VirtualMemoryRegion* rgn) { return false; } }; @@ -376,8 +310,8 @@ class VirtualMemoryTracker { // Snapshot current thread stacks void snapshot_thread_stacks(); void apply_summary_diff(VMATree::SummaryDiff diff); - size_t committed_size(const ReservedMemoryRegion* rmr); - address thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr); + size_t committed_size(const VirtualMemoryRegion* rgn); + address thread_stack_uncommitted_bottom(const VirtualMemoryRegion* rgn); RegionsTree* tree() { return &_tree; } @@ -401,9 +335,9 @@ class VirtualMemoryTracker { static bool print_containing_region(const void* p, outputStream* st); static void snapshot_thread_stacks(); static void apply_summary_diff(VMATree::SummaryDiff diff); - static size_t committed_size(const ReservedMemoryRegion* rmr); + static size_t committed_size(const VirtualMemoryRegion* rgn); // uncommitted thread stack bottom, above guard pages if there is any. - static address thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr); + static address thread_stack_uncommitted_bottom(const VirtualMemoryRegion* rgn); static RegionsTree* tree() { return _tracker->tree(); } }; diff --git a/test/hotspot/gtest/nmt/test_regions_tree.cpp b/test/hotspot/gtest/nmt/test_regions_tree.cpp index 7465c84aa72..a17a3fbb945 100644 --- a/test/hotspot/gtest/nmt/test_regions_tree.cpp +++ b/test/hotspot/gtest/nmt/test_regions_tree.cpp @@ -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 @@ -104,15 +104,15 @@ TEST_VM_F(NMTRegionsTreeTest, FindReservedRegion) { rt.reserve_mapping(1200, 50, rd, not_used); rt.reserve_mapping(1300, 50, rd, not_used); rt.reserve_mapping(1400, 50, rd, not_used); - ReservedMemoryRegion rmr; - rmr = rt.find_reserved_region((address)1205); - EXPECT_EQ(rmr.base(), (address)1200); - rmr = rt.find_reserved_region((address)1305); - EXPECT_EQ(rmr.base(), (address)1300); - rmr = rt.find_reserved_region((address)1405); - EXPECT_EQ(rmr.base(), (address)1400); - rmr = rt.find_reserved_region((address)1005); - EXPECT_EQ(rmr.base(), (address)1000); + VirtualMemoryRegion rgn; + rgn = rt.find_reserved_region((address)1205); + EXPECT_EQ(rgn.base(), (address)1200); + rgn = rt.find_reserved_region((address)1305); + EXPECT_EQ(rgn.base(), (address)1300); + rgn = rt.find_reserved_region((address)1405); + EXPECT_EQ(rgn.base(), (address)1400); + rgn = rt.find_reserved_region((address)1005); + EXPECT_EQ(rgn.base(), (address)1000); } TEST_VM_F(NMTRegionsTreeTest, VisitReservedRegions) { @@ -124,7 +124,7 @@ TEST_VM_F(NMTRegionsTreeTest, VisitReservedRegions) { rt.reserve_mapping(1300, 50, rd, not_used); rt.reserve_mapping(1400, 50, rd, not_used); - rt.visit_reserved_regions([&](const ReservedMemoryRegion& rgn) { + rt.visit_reserved_regions([&](const VirtualMemoryRegion& rgn) { EXPECT_EQ(((size_t)rgn.base()) % 100, 0UL); EXPECT_EQ(rgn.size(), 50UL); return true; @@ -144,9 +144,9 @@ TEST_VM_F(NMTRegionsTreeTest, VisitCommittedRegions) { rt.commit_region((address)1020, 5UL, ncs, not_used); rt.commit_region((address)1030, 5UL, ncs, not_used); rt.commit_region((address)1040, 5UL, ncs, not_used); - ReservedMemoryRegion rmr((address)1000, 50); + VirtualMemoryRegion rgn((address)1000, 50); size_t count = 0; - rt.visit_committed_regions(rmr, [&](CommittedMemoryRegion& crgn) { + rt.visit_committed_regions(rgn, [&](VirtualMemoryRegion& crgn) { count++; EXPECT_EQ((((size_t)crgn.base()) % 100) / 10, count); EXPECT_EQ(crgn.size(), 5UL); diff --git a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp index 5b78a66a3ae..8cf62fb9ea5 100644 --- a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp +++ b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -45,15 +45,14 @@ public: VirtualMemoryTracker::Instance::snapshot_thread_stacks(); } - ReservedMemoryRegion rmr_found; + VirtualMemoryRegion rgn_found; { MemTracker::NmtVirtualMemoryLocker vml; - rmr_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region(stack_end); + rgn_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region(stack_end); } - ASSERT_TRUE(rmr_found.is_valid()); - ASSERT_EQ(rmr_found.base(), stack_end); - + ASSERT_TRUE(rgn_found.is_valid()); + ASSERT_EQ(rgn_found.base(), stack_end); int i = 0; address i_addr = (address)&i; @@ -64,12 +63,12 @@ public: bool found_stack_top = false; { MemTracker::NmtVirtualMemoryLocker vml; - VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr_found, [&](const CommittedMemoryRegion& cmr) { - if (cmr.base() + cmr.size() == stack_top) { - EXPECT_TRUE(cmr.size() <= stack_size); + VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rgn_found, [&](const VirtualMemoryRegion& rgn) { + if (rgn.base() + rgn.size() == stack_top) { + EXPECT_TRUE(rgn.size() <= stack_size); found_stack_top = true; } - if (i_addr < stack_top && i_addr >= cmr.base()) { + if (i_addr < stack_top && i_addr >= rgn.base()) { found_i_addr = true; } i++; @@ -115,25 +114,25 @@ public: } // trigger the test - ReservedMemoryRegion rmr_found; + VirtualMemoryRegion rgn_found; { MemTracker::NmtVirtualMemoryLocker nvml; VirtualMemoryTracker::Instance::snapshot_thread_stacks(); - rmr_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region((address)base); + rgn_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region((address)base); } - ASSERT_TRUE(rmr_found.is_valid()); - ASSERT_EQ(rmr_found.base(), (address)base); + ASSERT_TRUE(rgn_found.is_valid()); + ASSERT_EQ(rgn_found.base(), (address)base); bool precise_tracking_supported = false; { MemTracker::NmtVirtualMemoryLocker nvml; - VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr_found, [&](const CommittedMemoryRegion& cmr){ - if (cmr.size() == size) { + VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rgn_found, [&](const VirtualMemoryRegion& rgn){ + if (rgn.size() == size) { return false; } else { precise_tracking_supported = true; - check_covered_pages(cmr.base(), cmr.size(), (address)base, touch_pages, page_num); + check_covered_pages(rgn.base(), rgn.size(), (address)base, touch_pages, page_num); } return true; }); @@ -151,9 +150,9 @@ public: { MemTracker::NmtVirtualMemoryLocker nvml; VirtualMemoryTracker::Instance::remove_released_region((address)base, size); - rmr_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region((address)base); + rgn_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region((address)base); } - ASSERT_TRUE(!rmr_found.is_valid()); + ASSERT_TRUE(!rgn_found.is_valid()); } static void test_committed_region() { diff --git a/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp b/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp index 4242302997a..a7e4b273788 100644 --- a/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp +++ b/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -50,41 +50,41 @@ namespace { }; } -#define check(vmt, rmr, regions) check_inner((vmt), (rmr), (regions), ARRAY_SIZE(regions), __FILE__, __LINE__) +#define check(vmt, rgn, regions) check_inner((vmt), (rgn), (regions), ARRAY_SIZE(regions), __FILE__, __LINE__) -#define check_empty(vmt, rmr) \ +#define check_empty(vmt, rgn) \ do { \ - check_inner((vmt), (rmr), nullptr, 0, __FILE__, __LINE__); \ + check_inner((vmt), (rgn), nullptr, 0, __FILE__, __LINE__); \ } while (false) -static void diagnostic_print(VirtualMemoryTracker& vmt, const ReservedMemoryRegion& rmr) { - LOG("In reserved region " PTR_FORMAT ", size %X:", p2i(rmr.base()), rmr.size()); - vmt.tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) { - LOG(" committed region: " PTR_FORMAT ", size %X", p2i(region.base()), region.size()); +static void diagnostic_print(VirtualMemoryTracker& vmt, const VirtualMemoryRegion& rgn) { + LOG("In reserved region " PTR_FORMAT ", size %X:", p2i(rgn.base()), rgn.size()); + vmt.tree()->visit_committed_regions(rgn, [&](VirtualMemoryRegion& crgn) { + LOG(" committed region: " PTR_FORMAT ", size %X", p2i(crgn.base()), crgn.size()); return true; }); } -static void check_inner(VirtualMemoryTracker& vmt, const ReservedMemoryRegion& rmr, R* regions, size_t regions_size, const char* file, int line) { +static void check_inner(VirtualMemoryTracker& vmt, const VirtualMemoryRegion& rgn, R* regions, size_t regions_size, const char* file, int line) { size_t i = 0; size_t size = 0; // Helpful log - diagnostic_print(vmt, rmr); + diagnostic_print(vmt, rgn); #define WHERE " from " << file << ":" << line - vmt.tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) { + vmt.tree()->visit_committed_regions(rgn, [&](VirtualMemoryRegion& crgn) { EXPECT_LT(i, regions_size) << WHERE; - EXPECT_EQ(region.base(), regions[i]._addr) << WHERE; - EXPECT_EQ(region.size(), regions[i]._size) << WHERE; - size += region.size(); + EXPECT_EQ(crgn.base(), regions[i]._addr) << WHERE; + EXPECT_EQ(crgn.size(), regions[i]._size) << WHERE; + size += crgn.size(); i++; return true; }); EXPECT_EQ(i, regions_size) << WHERE; - EXPECT_EQ(size, vmt.committed_size(&rmr)) << WHERE; + EXPECT_EQ(size, vmt.committed_size(&rgn)) << WHERE; } class VirtualMemoryTrackerTest { @@ -104,11 +104,11 @@ public: NativeCallStack stack(&frame1, 1); NativeCallStack stack2(&frame2, 1); - // Fetch the added RMR for the space - ReservedMemoryRegion rmr = rtree->find_reserved_region(addr); + // Fetch the added region for the space + VirtualMemoryRegion rgn = rtree->find_reserved_region(addr); - ASSERT_EQ(rmr.size(), size); - ASSERT_EQ(rmr.base(), addr); + ASSERT_EQ(rgn.size(), size); + ASSERT_EQ(rgn.base(), addr); // Commit Size Granularity const size_t cs = 0x1000; @@ -118,24 +118,24 @@ public: { // Commit one region rtree->commit_region(addr + cs, cs, stack, diff); R r[] = { {addr + cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit adjacent - lower address rtree->commit_region(addr, cs, stack, diff); R r[] = { {addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit adjacent - higher address rtree->commit_region(addr + 2 * cs, cs, stack, diff); R r[] = { {addr, 3 * cs} }; - check(vmt,rmr, r); + check(vmt, rgn, r); } // Cleanup rtree->uncommit_region(addr, 3 * cs, diff); - ASSERT_EQ(vmt.committed_size(&rmr), 0u); + ASSERT_EQ(vmt.committed_size(&rgn), 0u); // Commit adjacent regions with different stacks @@ -143,14 +143,14 @@ public: { // Commit one region rtree->commit_region(addr + cs, cs, stack, diff); R r[] = { {addr + cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit adjacent - lower address rtree->commit_region(addr, cs, stack2, diff); R r[] = { {addr, cs}, {addr + cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit adjacent - higher address @@ -158,12 +158,12 @@ public: R r[] = { {addr, cs}, {addr + cs, cs}, {addr + 2 * cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // Cleanup rtree->uncommit_region(addr, 3 * cs, diff); - ASSERT_EQ(vmt.committed_size(&rmr), 0u); + ASSERT_EQ(vmt.committed_size(&rgn), 0u); } static void test_add_committed_region_adjacent_overlapping() { @@ -180,11 +180,11 @@ public: NativeCallStack stack(&frame1, 1); NativeCallStack stack2(&frame2, 1); - // Fetch the added RMR for the space - ReservedMemoryRegion rmr = rtree->find_reserved_region(addr); + // Fetch the added region for the space + VirtualMemoryRegion rgn = rtree->find_reserved_region(addr); - ASSERT_EQ(rmr.size(), size); - ASSERT_EQ(rmr.base(), addr); + ASSERT_EQ(rgn.size(), size); + ASSERT_EQ(rgn.base(), addr); // Commit Size Granularity const size_t cs = 0x1000; @@ -196,28 +196,28 @@ public: rtree->commit_region(addr + 3 * cs, 2 * cs, stack, diff); R r[] = { {addr, 2 * cs}, {addr + 3 * cs, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit adjacent and overlapping rtree->commit_region(addr + 2 * cs, 2 * cs, stack, diff); R r[] = { {addr, 5 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // revert to two non-adjacent regions rtree->uncommit_region(addr + 2 * cs, cs, diff); - ASSERT_EQ(vmt.committed_size(&rmr), 4 * cs); + ASSERT_EQ(vmt.committed_size(&rgn), 4 * cs); { // Commit overlapping and adjacent rtree->commit_region(addr + cs, 2 * cs, stack, diff); R r[] = { {addr, 5 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // Cleanup rtree->uncommit_region(addr, 5 * cs, diff); - ASSERT_EQ(vmt.committed_size(&rmr), 0u); + ASSERT_EQ(vmt.committed_size(&rgn), 0u); // Commit adjacent and overlapping regions with different stacks @@ -227,7 +227,7 @@ public: rtree->commit_region(addr + 3 * cs, 2 * cs, stack, diff); R r[] = { {addr, 2 * cs}, {addr + 3 * cs, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit adjacent and overlapping @@ -235,20 +235,20 @@ public: R r[] = { {addr, 2 * cs}, {addr + 2 * cs, 2 * cs}, {addr + 4 * cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // revert to two non-adjacent regions rtree->commit_region(addr, 5 * cs, stack, diff); rtree->uncommit_region(addr + 2 * cs, cs, diff); - ASSERT_EQ(vmt.committed_size(&rmr), 4 * cs); + ASSERT_EQ(vmt.committed_size(&rgn), 4 * cs); { // Commit overlapping and adjacent rtree->commit_region(addr + cs, 2 * cs, stack2, diff); R r[] = { {addr, cs}, {addr + cs, 2 * cs}, {addr + 3 * cs, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } rtree->tree().remove_all(); @@ -269,12 +269,12 @@ public: NativeCallStack stack(&frame1, 1); NativeCallStack stack2(&frame2, 1); - // Fetch the added RMR for the space - ReservedMemoryRegion rmr = rtree->find_reserved_region(addr); + // Fetch the added region for the space + VirtualMemoryRegion rgn = rtree->find_reserved_region(addr); - ASSERT_EQ(rmr.size(), size); - ASSERT_EQ(rmr.base(), addr); + ASSERT_EQ(rgn.size(), size); + ASSERT_EQ(rgn.base(), addr); // Commit Size Granularity const size_t cs = 0x1000; @@ -284,54 +284,54 @@ public: { // Commit one region rtree->commit_region(addr, cs, stack, diff); R r[] = { {addr, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit the same region rtree->commit_region(addr, cs, stack, diff); R r[] = { {addr, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit a succeeding region rtree->commit_region(addr + cs, cs, stack, diff); R r[] = { {addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit over two regions rtree->commit_region(addr, 2 * cs, stack, diff); R r[] = { {addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } {// Commit first part of a region rtree->commit_region(addr, cs, stack, diff); R r[] = { {addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit second part of a region rtree->commit_region(addr + cs, cs, stack, diff); R r[] = { {addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit a third part rtree->commit_region(addr + 2 * cs, cs, stack, diff); R r[] = { {addr, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit in the middle of a region rtree->commit_region(addr + 1 * cs, cs, stack, diff); R r[] = { {addr, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // Cleanup rtree->uncommit_region(addr, 3 * cs, diff); - ASSERT_EQ(vmt.committed_size(&rmr), 0u); + ASSERT_EQ(vmt.committed_size(&rgn), 0u); // With preceding region @@ -342,71 +342,71 @@ public: { R r[] = { {addr, cs}, {addr + 2 * cs, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } rtree->commit_region(addr + 3 * cs, cs, stack, diff); { R r[] = { {addr, cs}, {addr + 2 * cs, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } rtree->commit_region(addr + 4 * cs, cs, stack, diff); { R r[] = { {addr, cs}, {addr + 2 * cs, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // Cleanup rtree->uncommit_region(addr, 5 * cs, diff); - ASSERT_EQ(vmt.committed_size(&rmr), 0u); + ASSERT_EQ(vmt.committed_size(&rgn), 0u); // With different stacks { // Commit one region rtree->commit_region(addr, cs, stack, diff); R r[] = { {addr, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit the same region rtree->commit_region(addr, cs, stack2, diff); R r[] = { {addr, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit a succeeding region rtree->commit_region(addr + cs, cs, stack, diff); R r[] = { {addr, cs}, {addr + cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit over two regions rtree->commit_region(addr, 2 * cs, stack, diff); R r[] = { {addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } {// Commit first part of a region rtree->commit_region(addr, cs, stack2, diff); R r[] = { {addr, cs}, {addr + cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit second part of a region rtree->commit_region(addr + cs, cs, stack2, diff); R r[] = { {addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit a third part rtree->commit_region(addr + 2 * cs, cs, stack2, diff); R r[] = { {addr, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } { // Commit in the middle of a region @@ -414,7 +414,7 @@ public: R r[] = { {addr, cs}, {addr + cs, cs}, {addr + 2 * cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } rtree->tree().remove_all(); @@ -445,11 +445,11 @@ public: NativeCallStack stack(&frame1, 1); NativeCallStack stack2(&frame2, 1); - // Fetch the added RMR for the space - ReservedMemoryRegion rmr = rtree->find_reserved_region(addr); + // Fetch the added region for the space + VirtualMemoryRegion rgn = rtree->find_reserved_region(addr); - ASSERT_EQ(rmr.size(), size); - ASSERT_EQ(rmr.base(), addr); + ASSERT_EQ(rgn.size(), size); + ASSERT_EQ(rgn.base(), addr); // Commit Size Granularity const size_t cs = 0x1000; @@ -457,11 +457,11 @@ public: { // Commit regions rtree->commit_region(addr, 3 * cs, stack, diff); R r[] = { {addr, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); // Remove only existing rtree->uncommit_region(addr, 3 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } { @@ -473,7 +473,7 @@ public: rtree->uncommit_region(addr, cs, diff); R r[] = { {addr + 2 * cs, cs}, {addr + 4 * cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // add back @@ -483,7 +483,7 @@ public: rtree->uncommit_region(addr + 2 * cs, cs, diff); R r[] = { {addr + 0 * cs, cs}, {addr + 4 * cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } // add back @@ -493,17 +493,17 @@ public: rtree->uncommit_region(addr + 4 * cs, cs, diff); R r[] = { {addr + 0 * cs, cs}, {addr + 2 * cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); } rtree->uncommit_region(addr, 5 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } { // Remove larger region rtree->commit_region(addr + 1 * cs, cs, stack, diff); rtree->uncommit_region(addr, 3 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } { // Remove smaller region - in the middle @@ -511,50 +511,50 @@ public: rtree->uncommit_region(addr + 1 * cs, cs, diff); R r[] = { { addr + 0 * cs, cs}, { addr + 2 * cs, cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); rtree->uncommit_region(addr, 3 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } { // Remove smaller region - at the beginning rtree->commit_region(addr, 3 * cs, stack, diff); rtree->uncommit_region(addr + 0 * cs, cs, diff); R r[] = { { addr + 1 * cs, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); rtree->uncommit_region(addr, 3 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } { // Remove smaller region - at the end rtree->commit_region(addr, 3 * cs, stack, diff); rtree->uncommit_region(addr + 2 * cs, cs, diff); R r[] = { { addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); rtree->uncommit_region(addr, 3 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } { // Remove smaller, overlapping region - at the beginning rtree->commit_region(addr + 1 * cs, 4 * cs, stack, diff); rtree->uncommit_region(addr, 2 * cs, diff); R r[] = { { addr + 2 * cs, 3 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); rtree->uncommit_region(addr + 1 * cs, 4 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } { // Remove smaller, overlapping region - at the end rtree->commit_region(addr, 3 * cs, stack, diff); rtree->uncommit_region(addr + 2 * cs, 2 * cs, diff); R r[] = { { addr, 2 * cs} }; - check(vmt, rmr, r); + check(vmt, rgn, r); rtree->uncommit_region(addr, 3 * cs, diff); - check_empty(vmt, rmr); + check_empty(vmt, rgn); } rtree->tree().remove_all(); From bd99c627b170147a796512810c8ecd98db12781e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Tue, 24 Feb 2026 09:18:19 +0000 Subject: [PATCH 007/636] 8378319: Obsolete the MaxRAM flag Reviewed-by: ayang, tschatzl --- src/hotspot/share/gc/shared/gc_globals.hpp | 7 +-- src/hotspot/share/runtime/arguments.cpp | 24 ++++----- src/java.base/share/man/java.md | 38 ++++--------- test/hotspot/gtest/runtime/test_globals.cpp | 2 +- ...AMFlags.java => TestMaxRAMPercentage.java} | 54 ++++++++++++------- .../TestUseCompressedOopsFlagsWithUlimit.java | 19 +++---- .../jtreg/gc/g1/TestRegionAlignment.java | 10 ++-- .../jtreg/runtime/7167069/PrintAsFlag.java | 2 +- .../TestOptionsWithRanges.java | 1 - .../test/whitebox/vm_flags/Uint64Test.java | 2 +- 10 files changed, 72 insertions(+), 87 deletions(-) rename test/hotspot/jtreg/gc/arguments/{TestMaxRAMFlags.java => TestMaxRAMPercentage.java} (73%) diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 9235029f254..65c970435e5 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -262,18 +262,13 @@ product(bool, AlwaysActAsServerClassMachine, false, \ "(Deprecated) Always act like a server-class machine") \ \ - product(uint64_t, MaxRAM, 0, \ - "(Deprecated) Real memory size (in bytes) used to set maximum " \ - "heap size") \ - range(0, 0XFFFFFFFFFFFFFFFF) \ - \ product(bool, AggressiveHeap, false, \ "(Deprecated) Optimize heap options for long-running memory " \ "intensive apps") \ \ product(size_t, ErgoHeapSizeLimit, 0, \ "Maximum ergonomically set heap size (in bytes); zero means use " \ - "MaxRAM * MaxRAMPercentage / 100") \ + "(System RAM) * MaxRAMPercentage / 100") \ range(0, max_uintx) \ \ product(double, MaxRAMPercentage, 25.0, \ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index ca8ca0e9b8f..a1dae76f680 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -536,7 +536,6 @@ static SpecialFlag const special_jvm_flags[] = { #ifdef _LP64 { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, #endif - { "MaxRAM", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "AggressiveHeap", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "NeverActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "AlwaysActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, @@ -553,6 +552,7 @@ static SpecialFlag const special_jvm_flags[] = { { "PSChunkLargeArrays", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "ParallelRefProcEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "ParallelRefProcBalancingEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "MaxRAM", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, #ifdef ASSERT { "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() }, @@ -1510,25 +1510,19 @@ void Arguments::set_heap_size() { // Check if the user has configured any limit on the amount of RAM we may use. bool has_ram_limit = !FLAG_IS_DEFAULT(MaxRAMPercentage) || !FLAG_IS_DEFAULT(MinRAMPercentage) || - !FLAG_IS_DEFAULT(InitialRAMPercentage) || - !FLAG_IS_DEFAULT(MaxRAM); + !FLAG_IS_DEFAULT(InitialRAMPercentage); - if (FLAG_IS_DEFAULT(MaxRAM)) { - if (CompilerConfig::should_set_client_emulation_mode_flags()) { - // Limit the available memory if client emulation mode is enabled. - FLAG_SET_ERGO(MaxRAM, 1ULL*G); - } else { - // Use the available physical memory on the system. - FLAG_SET_ERGO(MaxRAM, os::physical_memory()); - } - } + // Limit the available memory if client emulation mode is enabled. + const size_t avail_mem = CompilerConfig::should_set_client_emulation_mode_flags() + ? 1ULL*G + : os::physical_memory(); // If the maximum heap size has not been set with -Xmx, then set it as // fraction of the size of physical memory, respecting the maximum and // minimum sizes of the heap. if (FLAG_IS_DEFAULT(MaxHeapSize)) { - uint64_t min_memory = (uint64_t)(((double)MaxRAM * MinRAMPercentage) / 100); - uint64_t max_memory = (uint64_t)(((double)MaxRAM * MaxRAMPercentage) / 100); + uint64_t min_memory = (uint64_t)(((double)avail_mem * MinRAMPercentage) / 100); + uint64_t max_memory = (uint64_t)(((double)avail_mem * MaxRAMPercentage) / 100); const size_t reasonable_min = clamp_by_size_t_max(min_memory); size_t reasonable_max = clamp_by_size_t_max(max_memory); @@ -1615,7 +1609,7 @@ void Arguments::set_heap_size() { reasonable_minimum = limit_heap_by_allocatable_memory(reasonable_minimum); if (InitialHeapSize == 0) { - uint64_t initial_memory = (uint64_t)(((double)MaxRAM * InitialRAMPercentage) / 100); + uint64_t initial_memory = (uint64_t)(((double)avail_mem * InitialRAMPercentage) / 100); size_t reasonable_initial = clamp_by_size_t_max(initial_memory); reasonable_initial = limit_heap_by_allocatable_memory(reasonable_initial); diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 956a6aa144b..8cfccff5abe 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -2454,8 +2454,8 @@ Java HotSpot VM. [`-XX:InitialRAMPercentage=`]{#-XX_InitialRAMPercentage}*percent* : Sets the initial amount of memory that the JVM will use for the Java heap - before applying ergonomics heuristics as a percentage of the maximum amount - determined as described in the `-XX:MaxRAM` option. + before applying ergonomics heuristics as a percentage of the available memory + to the JVM process. The following example shows how to set the percentage of the initial amount of memory used for the Java heap: @@ -2575,9 +2575,8 @@ Java HotSpot VM. [`-XX:MaxRAMPercentage=`]{#-XX_MaxRAMPercentage}*percent* : Sets the maximum amount of memory that the JVM may use for the Java heap - before applying ergonomics heuristics as a percentage of the maximum amount - determined as described in the `-XX:MaxRAM` option. The default value is 25 - percent. + before applying ergonomics heuristics as a percentage of the available memory + to the JVM process. The default value is 25 percent. Specifying this option disables automatic use of compressed oops if the combined result of this and other options influencing the maximum amount @@ -2591,9 +2590,9 @@ Java HotSpot VM. [`-XX:MinRAMPercentage=`]{#-XX_MinRAMPercentage}*percent* : Sets the maximum amount of memory that the JVM may use for the Java heap - before applying ergonomics heuristics as a percentage of the maximum amount - determined as described in the `-XX:MaxRAM` option for small heaps. A small - heap is a heap of approximately 125 MB. The default value is 50 percent. + before applying ergonomics heuristics as a percentage of the available memory + to the JVM process for small heaps. A small heap is a heap of approximately + 125 MB. The default value is 50 percent. The following example shows how to set the percentage of the maximum amount of memory used for the Java heap for small heaps: @@ -2939,25 +2938,6 @@ they're used. (`-XX:+UseParallelGC` or `-XX:+UseG1GC`). Other collectors employing multiple threads always perform reference processing in parallel. -[`-XX:MaxRAM=`]{#-XX_MaxRAM}*size* -: Sets the maximum amount of memory that the JVM may use for the Java heap - before applying ergonomics heuristics. The default value is the amount of - available memory to the JVM process. - - The maximum amount of available memory to the JVM process is the minimum - of the machine's physical memory and any constraints set by the environment - (e.g. container). - - Specifying this option disables automatic use of compressed oops if - the combined result of this and other options influencing the maximum amount - of memory is larger than the range of memory addressable by compressed oops. - See `-XX:UseCompressedOops` for further information about compressed oops. - - The following example shows how to set the maximum amount of available - memory for sizing the Java heap to 2 GB: - - > `-XX:MaxRAM=2G` - [`-XX:+AggressiveHeap`]{#-XX__AggressiveHeap} : Enables Java heap optimization. This sets various parameters to be optimal for long-running jobs with intensive memory allocation, based on @@ -2967,8 +2947,8 @@ they're used. [`-XX:+NeverActAsServerClassMachine`]{#-XX__NeverActAsServerClassMachine} : Enable the "Client VM emulation" mode which only uses the C1 JIT compiler, a 32Mb CodeCache and the Serial GC. The maximum amount of memory that the - JVM may use (controlled by the `-XX:MaxRAM=n` flag) is set to 1GB by default. - The string "emulated-client" is added to the JVM version string. + JVM may use is set to 1GB by default. The string "emulated-client" is added + to the JVM version string. By default the flag is set to `true` only on Windows in 32-bit mode and `false` in all other cases. diff --git a/test/hotspot/gtest/runtime/test_globals.cpp b/test/hotspot/gtest/runtime/test_globals.cpp index 84a62732056..9ef5bd6a5af 100644 --- a/test/hotspot/gtest/runtime/test_globals.cpp +++ b/test/hotspot/gtest/runtime/test_globals.cpp @@ -58,7 +58,7 @@ TEST_VM(FlagGuard, size_t_flag) { } TEST_VM(FlagGuard, uint64_t_flag) { - TEST_FLAG(MaxRAM, uint64_t, 1337); + TEST_FLAG(ErrorLogTimeout, uint64_t, 1337); } TEST_VM(FlagGuard, double_flag) { diff --git a/test/hotspot/jtreg/gc/arguments/TestMaxRAMFlags.java b/test/hotspot/jtreg/gc/arguments/TestMaxRAMPercentage.java similarity index 73% rename from test/hotspot/jtreg/gc/arguments/TestMaxRAMFlags.java rename to test/hotspot/jtreg/gc/arguments/TestMaxRAMPercentage.java index e7b2f371ed6..0cf34a0d239 100644 --- a/test/hotspot/jtreg/gc/arguments/TestMaxRAMFlags.java +++ b/test/hotspot/jtreg/gc/arguments/TestMaxRAMPercentage.java @@ -24,10 +24,9 @@ package gc.arguments; /* - * @test TestMaxRAMFlags + * @test TestMaxRAMPercentage * @bug 8222252 - * @summary Verify correct MaxHeapSize and UseCompressedOops when MaxRAM and MaxRAMPercentage - * are specified. + * @summary Verify correct MaxHeapSize and UseCompressedOops when MaxRAMPercentage is specified * @library /test/lib * @library / * @requires vm.bits == "64" @@ -35,8 +34,11 @@ package gc.arguments; * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run driver gc.arguments.TestMaxRAMFlags - * @author bob.vandette@oracle.com + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * gc.arguments.TestMaxRAMPercentage */ import java.util.regex.Matcher; @@ -45,14 +47,17 @@ import java.util.regex.Pattern; import java.util.ArrayList; import java.util.Arrays; +import jdk.test.whitebox.WhiteBox; import jdk.test.lib.process.OutputAnalyzer; +import jtreg.SkippedException; -public class TestMaxRAMFlags { +public class TestMaxRAMPercentage { - private static void checkMaxRAMSize(long maxram, int maxrampercent, boolean forcecoop, long expectheap, boolean expectcoop) throws Exception { + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + + private static void checkMaxRAMSize(double maxrampercent, boolean forcecoop, long expectheap, boolean expectcoop) throws Exception { ArrayList args = new ArrayList(); - args.add("-XX:MaxRAM=" + maxram); args.add("-XX:MaxRAMPercentage=" + maxrampercent); if (forcecoop) { args.add("-XX:+UseCompressedOops"); @@ -107,21 +112,32 @@ public class TestMaxRAMFlags { } public static void main(String args[]) throws Exception { - // Tests - // 1. Verify that MaxRAMPercentage overrides UseCompressedOops Ergo - // 2. Verify that UseCompressedOops forces compressed oops limit even - // when other flags are specified. - - long oneG = 1L * 1024L * 1024L * 1024L; - // Hotspot startup logic reduces MaxHeapForCompressedOops by HeapBaseMinAddress // in order to get zero based compressed oops offsets. long heapbaseminaddr = getHeapBaseMinAddress(); long maxcoopheap = TestUseCompressedOopsErgoTools.getMaxHeapForCompressedOops(new String [0]) - heapbaseminaddr; - // Args: MaxRAM , MaxRAMPercentage, forcecoop, expect heap, expect coop - checkMaxRAMSize(maxcoopheap - oneG, 100, false, maxcoopheap - oneG, true); - checkMaxRAMSize(maxcoopheap + oneG, 100, false, maxcoopheap + oneG, false); - checkMaxRAMSize(maxcoopheap + oneG, 100, true, maxcoopheap, true); + // The headroom is used to get/not get compressed oops from the maxcoopheap size + long M = 1L * 1024L * 1024L; + long headroom = 64 * M; + + long requiredHostMemory = maxcoopheap + headroom; + + // Get host memory + long hostMemory = wb.hostPhysicalMemory(); + + System.out.println("hostMemory: " + hostMemory + ", requiredHostMemory: " + requiredHostMemory); + + if (hostMemory < requiredHostMemory) { + throw new SkippedException("Not enough RAM on machine to run. Test skipped!"); + } + + double MaxRAMPercentage = ((double)maxcoopheap / hostMemory) * 100.0; + double headroomPercentage = ((double)headroom / hostMemory) * 100.0; + + // Args: MaxRAMPercentage, forcecoop, expectheap, expectcoop + checkMaxRAMSize(MaxRAMPercentage - headroomPercentage, false, maxcoopheap - (long)headroom, true); + checkMaxRAMSize(MaxRAMPercentage + headroomPercentage, false, maxcoopheap + (long)headroom, false); + checkMaxRAMSize(MaxRAMPercentage, true, maxcoopheap, true); } } diff --git a/test/hotspot/jtreg/gc/arguments/TestUseCompressedOopsFlagsWithUlimit.java b/test/hotspot/jtreg/gc/arguments/TestUseCompressedOopsFlagsWithUlimit.java index c9cf7291aaa..10c85c30c33 100644 --- a/test/hotspot/jtreg/gc/arguments/TestUseCompressedOopsFlagsWithUlimit.java +++ b/test/hotspot/jtreg/gc/arguments/TestUseCompressedOopsFlagsWithUlimit.java @@ -26,8 +26,7 @@ package gc.arguments; /* * @test TestUseCompressedOopsFlagsWithUlimit * @bug 8280761 - * @summary Verify correct UseCompressedOops when MaxRAM and MaxRAMPercentage - * are specified with ulimit -v. + * @summary Verify that ergonomic setting of UseCompressedOops adheres to ulimit -v * @library /test/lib * @library / * @requires vm.bits == "64" @@ -50,10 +49,9 @@ import jdk.test.lib.process.ProcessTools; public class TestUseCompressedOopsFlagsWithUlimit { - private static void checkFlag(long ulimit, long maxram, int maxrampercent, boolean expectcoop) throws Exception { + private static void checkFlag(long ulimit, int maxrampercent, boolean expectcoop) throws Exception { ArrayList args = new ArrayList(); - args.add("-XX:MaxRAM=" + maxram); args.add("-XX:MaxRAMPercentage=" + maxrampercent); args.add("-XX:+PrintFlagsFinal"); @@ -74,7 +72,7 @@ public class TestUseCompressedOopsFlagsWithUlimit { boolean actualcoop = getFlagBoolValue("UseCompressedOops", stdout); if (actualcoop != expectcoop) { throw new RuntimeException("UseCompressedOops set to " + actualcoop + - ", expected " + expectcoop + " when running with the following flags: " + Arrays.asList(args).toString()); + ", expected " + expectcoop + " when running with the following flags: " + Arrays.asList(args).toString() + ", and ulimit: " + ulimit); } } @@ -91,10 +89,13 @@ public class TestUseCompressedOopsFlagsWithUlimit { // Verify that UseCompressedOops Ergo follows ulimit -v setting. long oneG = 1L * 1024L * 1024L * 1024L; + long ulimit = 10 * oneG; - // Args: ulimit, max_ram, max_ram_percent, expected_coop - // Setting MaxRAMPercentage explicitly to make the test more resilient. - checkFlag(10 * oneG, 32 * oneG, 100, true); - checkFlag(10 * oneG, 128 * oneG, 100, true); + // Regardless of how much memory that is available on the machine, we should + // always get compressed oops if we have set a ulimit below the COOPS limit. + // We set MaxRAMPercentage explicitly to make the test more resilient. + + // Args: ulimit, maxrampercent, expectedcoop + checkFlag(ulimit, 100, true); } } diff --git a/test/hotspot/jtreg/gc/g1/TestRegionAlignment.java b/test/hotspot/jtreg/gc/g1/TestRegionAlignment.java index 2bc25079068..3555fad56a8 100644 --- a/test/hotspot/jtreg/gc/g1/TestRegionAlignment.java +++ b/test/hotspot/jtreg/gc/g1/TestRegionAlignment.java @@ -28,12 +28,12 @@ package gc.g1; * @bug 8013791 * @requires vm.gc.G1 * @summary Make sure that G1 ergonomics pick a heap size that is aligned with the region size - * @run main/othervm -XX:+UseG1GC -XX:G1HeapRegionSize=32m -XX:MaxRAM=555m gc.g1.TestRegionAlignment - * - * When G1 ergonomically picks a maximum heap size it must be aligned to the region size. - * This test tries to get the VM to pick a small and unaligned heap size (by using MaxRAM=555) and a - * large region size (by using -XX:G1HeapRegionSize=32m). This will fail without the fix for 8013791. + * @comment When G1 ergonomically picks a maximum heap size it must be aligned to the region size. + * This test tries to get the VM to pick a small and unaligned heap size (by using MaxRAM=555) and a + * large region size (by using -XX:G1HeapRegionSize=32m). This will fail without the fix for 8013791. + * @run main/othervm -XX:+UseG1GC -XX:G1HeapRegionSize=32m -Xmx140m gc.g1.TestRegionAlignment */ + public class TestRegionAlignment { public static void main(String[] args) { } } diff --git a/test/hotspot/jtreg/runtime/7167069/PrintAsFlag.java b/test/hotspot/jtreg/runtime/7167069/PrintAsFlag.java index 9948ef1738f..cd03af20b34 100644 --- a/test/hotspot/jtreg/runtime/7167069/PrintAsFlag.java +++ b/test/hotspot/jtreg/runtime/7167069/PrintAsFlag.java @@ -27,7 +27,7 @@ * * @test PrintAsFlag * @summary verify that Flag::print_as_flag() works correctly. This is used by "jinfo -flag" and -XX:+PrintCommandLineFlags. - * @run main/othervm -XX:+PrintCommandLineFlags -XX:-ShowMessageBoxOnError -XX:ParallelGCThreads=4 -XX:MaxRAM=1G -XX:ErrorFile="file" PrintAsFlag + * @run main/othervm -XX:+PrintCommandLineFlags -XX:-ShowMessageBoxOnError -XX:ParallelGCThreads=4 -XX:ErrorLogTimeout=12345 -XX:ErrorFile="file" PrintAsFlag */ public class PrintAsFlag { diff --git a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java index 0ac19c4a487..eb0fd5f8be5 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java +++ b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java @@ -237,7 +237,6 @@ public class TestOptionsWithRanges { excludeTestMaxRange("G1ConcRefinementThreads"); excludeTestMaxRange("InitialHeapSize"); excludeTestMaxRange("MaxHeapSize"); - excludeTestMaxRange("MaxRAM"); excludeTestMaxRange("NewSize"); excludeTestMaxRange("ParallelGCThreads"); excludeTestMaxRange("TLABSize"); diff --git a/test/lib-test/jdk/test/whitebox/vm_flags/Uint64Test.java b/test/lib-test/jdk/test/whitebox/vm_flags/Uint64Test.java index 64dd7544ebb..bf8bf0104ac 100644 --- a/test/lib-test/jdk/test/whitebox/vm_flags/Uint64Test.java +++ b/test/lib-test/jdk/test/whitebox/vm_flags/Uint64Test.java @@ -35,7 +35,7 @@ */ public class Uint64Test { - private static final String FLAG_NAME = "MaxRAM"; + private static final String FLAG_NAME = "StringDeduplicationHashSeed"; private static final Long[] TESTS = {0L, 100L, (long) Integer.MAX_VALUE, -1L, Long.MAX_VALUE, Long.MIN_VALUE}; From 92fa4f13c6aec8a7958867ce67d89b778c87de7b Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Tue, 24 Feb 2026 09:19:11 +0000 Subject: [PATCH 008/636] 8372231: Test gtest/NMTGtests.java#nmt-summary crashed Reviewed-by: phubner, jsjolen --- .../test_nmt_buffer_overflow_detection.cpp | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp index c65808d3f4d..e3b03440141 100644 --- a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2022 SAP SE. All rights reserved. - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -47,7 +47,6 @@ "fake message ignore this - " expected_assertion_message); \ } \ } - /////// #if !INCLUDE_ASAN @@ -86,31 +85,6 @@ DEFINE_TEST(test_overwrite_back_long_unaligned_distance, "footer canary broken") /////// -static void test_double_free() { - address p = (address) os::malloc(1, mtTest); - os::free(p); - // Now a double free. Note that this is susceptible to concurrency issues should - // a concurrent thread have done a malloc and gotten the same address after the - // first free. To decrease chance of this happening, we repeat the double free - // several times. - for (int i = 0; i < 100; i ++) { - os::free(p); - } -} - -// What assertion message we will see depends on whether the VM wipes the memory-to-be-freed -// on the first free(), and whether the libc uses the freed memory to store bookkeeping information. -// If the death marker in the header is still intact after the first free, we will recognize this as -// double free; if it got wiped, we should at least see a broken header canary. -// The message would be either -// - "header canary broken" or -// - "header canary dead (double free?)". -// However, since gtest regex expressions do not support unions (a|b), I search for a reasonable -// subset here. -DEFINE_TEST(test_double_free, "header canary") - -/////// - static void test_invalid_block_address() { // very low, like the result of an overflow or of accessing a null this pointer os::free((void*)0x100); From c16ac37d501c0c04bd68be8f500ea3dc24b28fa4 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Tue, 24 Feb 2026 09:21:34 +0000 Subject: [PATCH 009/636] 8376892: Allow conversion warnings in subsets of the code base Reviewed-by: kbarrett, erikj, azafari --- make/autoconf/flags-cflags.m4 | 4 ++ make/autoconf/spec.gmk.template | 3 +- make/common/MakeBase.gmk | 67 ++++++++++++++++++++++++++++++++- make/hotspot/lib/CompileJvm.gmk | 4 +- 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 0cdf02c61c6..2d39d84f52e 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -214,6 +214,7 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS], WARNINGS_ENABLE_ADDITIONAL_CXX="" WARNINGS_ENABLE_ADDITIONAL_JVM="" DISABLED_WARNINGS="4800 5105" + CFLAGS_CONVERSION_WARNINGS= ;; gcc) @@ -239,6 +240,7 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS], if test "x$OPENJDK_TARGET_CPU_ARCH" = "xppc"; then DISABLED_WARNINGS="$DISABLED_WARNINGS psabi" fi + CFLAGS_CONVERSION_WARNINGS="-Wconversion -Wno-float-conversion" ;; clang) @@ -258,6 +260,7 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS], # These warnings will never be turned on, since they generate too many # false positives. DISABLED_WARNINGS="unknown-warning-option unused-parameter" + CFLAGS_CONVERSION_WARNINGS="-Wimplicit-int-conversion" ;; esac WARNINGS_ENABLE_ALL="$WARNINGS_ENABLE_ALL_NORMAL $WARNINGS_ENABLE_ADDITIONAL" @@ -270,6 +273,7 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS], AC_SUBST(DISABLED_WARNINGS) AC_SUBST(DISABLED_WARNINGS_C) AC_SUBST(DISABLED_WARNINGS_CXX) + AC_SUBST(CFLAGS_CONVERSION_WARNINGS) ]) AC_DEFUN([FLAGS_SETUP_QUALITY_CHECKS], diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template index b3d58704c50..c4e5a23d31a 100644 --- a/make/autoconf/spec.gmk.template +++ b/make/autoconf/spec.gmk.template @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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 @@ -529,6 +529,7 @@ CFLAGS_WARNINGS_ARE_ERRORS := @CFLAGS_WARNINGS_ARE_ERRORS@ DISABLED_WARNINGS := @DISABLED_WARNINGS@ DISABLED_WARNINGS_C := @DISABLED_WARNINGS_C@ DISABLED_WARNINGS_CXX := @DISABLED_WARNINGS_CXX@ +CFLAGS_CONVERSION_WARNINGS := @CFLAGS_CONVERSION_WARNINGS@ # A global flag (true or false) determining if native warnings are considered errors. WARNINGS_AS_ERRORS := @WARNINGS_AS_ERRORS@ diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk index 97ef88932cb..45cc3c77dea 100644 --- a/make/common/MakeBase.gmk +++ b/make/common/MakeBase.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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 @@ -141,6 +141,66 @@ endef # Make sure logging is setup for everyone that includes MakeBase.gmk. $(eval $(call SetupLogging)) +################################################################################ +# Make does not have support for VARARGS, you can send variable amount +# of arguments, but you can for example not append a list at the end. +# It is therefore not easy to send the elements of a list of unknown +# length as argument to a function. This can somewhat be worked around +# by sending a list as an argument, and then interpreting each element +# of the list as an argument to the function. However, Make is +# limited, and using this method you can not easily send spaces. +# +# We need to quote strings for two reasons when sending them as +# "variable append packs": +# +# 1) variable appends can include spaces, and those must be preserved +# 2) variable appends can include assignment strings ":=", and those +# must be quoted to a form so that we can recognise the "append pack". +# We recognise an "append pack" by its lack of strict assignment ":=" + +Q := $(HASH) +SpaceQ := $(Q)s +AppendQ := $(Q)+ +AssignQ := $(Q)a +QQ := $(Q)$(Q) + +# $(call Quote,echo "#trala:=") -> echo#s"##trala#a" +Quote = $(subst :=,$(AssignQ),$(subst $(SPACE),$(SpaceQ),$(subst $(Q),$(QQ),$1))) + +# $(call Unquote,echo#s"##trala#a") -> echo "#trala:=" +Unquote = $(subst $(QQ),$(Q),$(subst $(SpaceQ),$(SPACE),$(subst $(AssignQ),:=,$1))) + +# $(call QuoteAppend,name,some value) -> name#+some#svalue +# $(call QuoteAppend,bad+=name,some value) -> error +QuoteAppend = $(if $(findstring +=,$1),$(error you can not have += in a variable name: "$1"),$(call Quote,$1)$(AppendQ)$(call Quote,$2)) + +# $(call UnquoteAppendIndex,name#+some#svalue,1) -> name +# $(call UnquoteAppendIndex,name#+some#svalue,2) -> some value +UnquoteAppendIndex = $(call Unquote,$(word $2,$(subst $(AppendQ),$(SPACE),$1))) + +# $(call FilterFiles,dir,%.cpp) -> file1.cpp file2.cpp (without path) +FilterFiles = $(filter $2,$(notdir $(call FindFiles,$1))) + +# $(call Unpack module_,file1.cpp_CXXFLAGS#+-Wconversion file2.cpp_CXXFLAGS#+-Wconversion) -> module_file1.cpp_CXXFLAGS += -Wconversion +# module_file2.cpp_CXXFLAGS += -Wconversion +Unpack = $(foreach pair,$2,$1$(call UnquoteAppendIndex,$(pair),1) += $(call UnquoteAppendIndex,$(pair),2)$(NEWLINE)) + +# This macro takes four arguments: +# $1: directory where to find files (striped), example: $(TOPDIR)/src/hotspot/share/gc/g1 +# $2: filter to match what to keep (striped), example: g1Concurrent%.cpp +# $3: what flags to override (striped), example: _CXXFLAGS +# $4: what value to append to the flag (striped), example: $(CFLAGS_CONVERSION_WARNINGS) +# +# The result will be a quoted string that can be unpacked to a list of +# variable appendings (see macro Unpack above). You do not need to take +# care of unpacking, it is done in NamedParamsMacroTemplate. +# +# This feature should only be used for warnings that we want to +# incrementally add to the rest of the code base. +# +# $(call ExtendFlags,dir,%.cpp,_CXXFLAGS,-Wconversion) -> file1.cpp_CXXFLAGS#+-Wconversion file2.cpp_CXXFLAGS#+-Wconversion +ExtendFlags = $(foreach file,$(call FilterFiles,$(strip $1),$(strip $2)),$(call QuoteAppend,$(file)$(strip $3),$(strip $4))) + ################################################################################ MAX_PARAMS := 96 @@ -166,7 +226,10 @@ define NamedParamsMacroTemplate Too many named arguments to macro, please update MAX_PARAMS in MakeBase.gmk)) # Iterate over 2 3 4... and evaluate the named parameters with $1_ as prefix $(foreach i, $(PARAM_SEQUENCE), $(if $(strip $($i)), \ - $(strip $1)_$(strip $(call EscapeHash, $(call DoubleDollar, $($i))))$(NEWLINE))) + $(if $(findstring :=,$($i)), \ + $(strip $1)_$(strip $(call EscapeHash, $(call DoubleDollar, $($i))))$(NEWLINE), \ + $(call Unpack,$(strip $1)_,$($i))))) + # Debug print all named parameter names and values $(if $(findstring $(LOG_LEVEL), trace), \ $(info $0 $(strip $1) $(foreach i, $(PARAM_SEQUENCE), \ diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index 39a549b7db0..76e0056658c 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2025, 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 @@ -190,6 +190,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ abstract_vm_version.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ whitebox.cpp_CXXFLAGS := $(CFLAGS_SHIP_DEBUGINFO), \ + $(call ExtendFlags, $(TOPDIR)/src/hotspot/share/gc/g1, \ + g1Numa.cpp, _CXXFLAGS, $(CFLAGS_CONVERSION_WARNINGS)), \ DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \ DISABLED_WARNINGS_gcc_ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp := nonnull, \ DISABLED_WARNINGS_gcc_bytecodeInterpreter.cpp := unused-label, \ From 32a619715e9d34e45b9bd6c954a03ead34be5a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20H=C3=BCbner?= Date: Tue, 24 Feb 2026 10:07:45 +0000 Subject: [PATCH 010/636] 8370044: TraceBytecodes shouldn't break up lines Reviewed-by: dholmes, coleenp, jsjolen --- src/hotspot/share/code/nmethod.cpp | 5 +- .../share/interpreter/bytecodeTracer.cpp | 4 +- .../share/interpreter/bytecodeTracer.hpp | 2 +- .../share/interpreter/interpreterRuntime.cpp | 4 +- src/hotspot/share/oops/method.hpp | 2 +- src/hotspot/share/runtime/java.cpp | 3 +- src/hotspot/share/runtime/vframe.inline.hpp | 5 +- .../CoherentBytecodeTraceTest.java | 158 ++++++++++++++++++ 8 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/interpreter/CoherentBytecodeTraceTest.java diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 13eb1ff1604..4c2f9157b99 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -938,7 +938,8 @@ address nmethod::continuation_for_implicit_exception(address pc, bool for_div0_c stringStream ss; ss.print_cr("implicit exception happened at " INTPTR_FORMAT, p2i(pc)); print_on(&ss); - method()->print_codes_on(&ss); + // Buffering to a stringStream, disable internal buffering so it's not done twice. + method()->print_codes_on(&ss, 0, false); print_code_on(&ss); print_pcs_on(&ss); tty->print("%s", ss.as_string()); // print all at once diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 69fc93b6c0f..21974218957 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -185,7 +185,9 @@ static Method* _method_currently_being_printed = nullptr; void BytecodeTracer::trace_interpreter(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) { if (TraceBytecodes && BytecodeCounter::counter_value() >= TraceBytecodesAt) { BytecodePrinter printer(AtomicAccess::load_acquire(&_method_currently_being_printed)); - printer.trace(method, bcp, tos, tos2, st); + stringStream buf; + printer.trace(method, bcp, tos, tos2, &buf); + st->print("%s", buf.freeze()); // Save method currently being printed to detect when method printing changes. AtomicAccess::release_store(&_method_currently_being_printed, method()); } diff --git a/src/hotspot/share/interpreter/bytecodeTracer.hpp b/src/hotspot/share/interpreter/bytecodeTracer.hpp index e199a2b7ea2..ab66030b6cd 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.hpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.hpp @@ -38,7 +38,7 @@ class outputStream; class BytecodeClosure; class BytecodeTracer: AllStatic { public: - NOT_PRODUCT(static void trace_interpreter(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st = tty);) + NOT_PRODUCT(static void trace_interpreter(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st);) static void print_method_codes(const methodHandle& method, int from, int to, outputStream* st, int flags, bool buffered = true); }; diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index b985d2af6ff..ca7174389cf 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -1522,7 +1522,7 @@ JRT_LEAF(intptr_t, InterpreterRuntime::trace_bytecode(JavaThread* current, intpt LastFrameAccessor last_frame(current); assert(last_frame.is_interpreted_frame(), "must be an interpreted frame"); methodHandle mh(current, last_frame.method()); - BytecodeTracer::trace_interpreter(mh, last_frame.bcp(), tos, tos2); + BytecodeTracer::trace_interpreter(mh, last_frame.bcp(), tos, tos2, tty); return preserve_this_value; JRT_END #endif // !PRODUCT diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index add8e59b2be..e7479671dcf 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -465,7 +465,7 @@ public: bool contains(address bcp) const { return constMethod()->contains(bcp); } // prints byte codes - void print_codes(int flags = 0) const { print_codes_on(tty, flags); } + void print_codes(int flags = 0, bool buffered = true) const { print_codes_on(tty, flags, buffered); } void print_codes_on(outputStream* st, int flags = 0, bool buffered = true) const; void print_codes_on(int from, int to, outputStream* st, int flags = 0, bool buffered = true) const; diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index ee4f776df06..758051a7351 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -151,7 +151,8 @@ static void print_method_profiling_data() { ss.fill_to(2); m->method_data()->parameters_type_data()->print_data_on(&ss); } - m->print_codes_on(&ss); + // Buffering to a stringStream, disable internal buffering so it's not done twice. + m->print_codes_on(&ss, 0, false); tty->print("%s", ss.as_string()); // print all at once total_size += m->method_data()->size_in_bytes(); } diff --git a/src/hotspot/share/runtime/vframe.inline.hpp b/src/hotspot/share/runtime/vframe.inline.hpp index 54dbb44ebd4..6bd83e7f7c9 100644 --- a/src/hotspot/share/runtime/vframe.inline.hpp +++ b/src/hotspot/share/runtime/vframe.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -176,7 +176,8 @@ inline void vframeStreamCommon::fill_from_compiled_frame(int decode_offset) { INTPTR_FORMAT " not found or invalid at %d", p2i(_frame.pc()), decode_offset); nm()->print_on(&ss); - nm()->method()->print_codes_on(&ss); + // Buffering to a stringStream, disable internal buffering so it's not done twice. + nm()->method()->print_codes_on(&ss, 0, false); nm()->print_code_on(&ss); nm()->print_pcs_on(&ss); tty->print("%s", ss.as_string()); // print all at once diff --git a/test/hotspot/jtreg/runtime/interpreter/CoherentBytecodeTraceTest.java b/test/hotspot/jtreg/runtime/interpreter/CoherentBytecodeTraceTest.java new file mode 100644 index 00000000000..e0fb8cf98ec --- /dev/null +++ b/test/hotspot/jtreg/runtime/interpreter/CoherentBytecodeTraceTest.java @@ -0,0 +1,158 @@ +/* + * 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 8370044 + * @library / /test/lib + * @summary Ensures that individual bytecodes are not broken apart. + * @requires vm.debug == true & vm.flagless + * @run driver CoherentBytecodeTraceTest + */ + +import module java.base; + +import jdk.test.lib.Utils; +import jdk.test.lib.process.*; + +public class CoherentBytecodeTraceTest { + private static final int NUM_THREADS = 3; + private static final String THE_METHOD = ""; + + public static void main(String[] args) + throws InterruptedException, IOException { + if (args.length == 1 && "worker".equals(args[0])) { + schedule(); + return; + } + // Create a VM process and trace its bytecodes. + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+TraceBytecodes", + // Make sure that work() is not compiled. If there is no compiler, + // the flag is still accepted. + "-XX:CompileCommand=exclude,CoherentBytecodeTraceTest.work", + CoherentBytecodeTraceTest.class.getName(), "worker" + ); + OutputAnalyzer oa = new OutputAnalyzer(pb.start()).shouldHaveExitValue(0); + analyze(oa.stdoutAsLines()); + } + + private static void schedule() throws InterruptedException { + Thread[] threads = + IntStream.range(0, NUM_THREADS) + .mapToObj(i -> { + Thread thread = new Thread( + CoherentBytecodeTraceTest::work + ); + thread.start(); + return thread; + }) + .toArray(Thread[]::new); + for (Thread thread : threads) { + thread.join(); + } + } + + // The analysis works by finding the invokeinterface bytecode when calling + // Strategy.foo. The trace should look something like the following: + // invokeinterface 116 + // The strategy is to find CoherentBytecodeTraceTest$Strategy.foo's index + // and then ensure the constant pool ref and opcode before are correct. + // This requires going through the file line-by-line. + private static void analyze(List lines) { + IO.println("Analyzing " + lines.size() + " lines"); + boolean foundAtLeastOne = false; + // Reverse regex for: 'invokeinterface \d+ '. This is needed to look + // back from the interface name to ensure that the thing that + // preceeds it is an invokeinterface with a constant pool reference. + // Use 'XXXX' to denote where we want to put \d+ or else it will get + // reversed and lose its semantics. + String searchRegex = reverseString("invokeinterface XXXX ") + .replace("XXXX", "\\d+"); + Pattern reverseFirstPart = Pattern.compile(searchRegex); + for (String line : lines) { + int fooCallIndex = line.indexOf(THE_METHOD); + if (fooCallIndex == -1) { + continue; + } + String untilFooCall = line.substring(0, fooCallIndex); + String beginningReverse = reverseString(untilFooCall); + // Use a Scanner to do a match for "invokeinterface XXXX " + // immediately before the constant pool reference. + Scanner scanner = new Scanner(beginningReverse); + // Scanner#hasNext would use the next token given by whitespace, + // but whitespace is part of the pattern. Use horizon instead, if + // this is null, then there is no match. + if (scanner.findWithinHorizon(reverseFirstPart, 0) == null) { + IO.println("Using regex: " + reverseFirstPart); + IO.println("Regex rejected: " + beginningReverse); + throw new RuntimeException( + "torn bytecode trace: " + line + ); + } + foundAtLeastOne = true; + } + // If there are no invokeinterface calls then something went wrong + // and the test probably needs to be updated. + if (!foundAtLeastOne) { + throw new RuntimeException( + "sanity failure: no invokeinterface found for " + THE_METHOD + ); + } + } + + // Performs some random work. + // The goal is to have this emit some bytecodes that contain other bytes. + public static void work() { + int x = 10; + int y = 30; + int sum = 123; + for (int i = 0; i < 10_000; i++) { + if (i == x) { + int modulo = y % i; + sum ^= modulo; + } else { + Strategy[] arr = new Strategy[] { new Outer(new Object()) }; + arr[0].foo(i); + x = y - sum; + } + } + } + + private static String reverseString(String input) { + return new StringBuilder(input).reverse().toString(); + } + + private record Outer(Object inner) implements Strategy { + @Override + public void foo(int i) { + if (i % 1000 == 0) { + IO.println("foo" + i); + } + } + } + + public interface Strategy { + void foo(int i); + } +} From b99d1f0a1723261718d68ffafbdb9efb315ccfc7 Mon Sep 17 00:00:00 2001 From: cdw200806 <56459974+cdw200806@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:50:59 +0000 Subject: [PATCH 011/636] 8378354: Faulty assertion in checkInvariants method of ConcurrentHashMap Reviewed-by: alanb, vklang --- .../share/classes/java/util/concurrent/ConcurrentHashMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index 9295f0deb59..c4e1839e9fb 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -3310,7 +3310,7 @@ public class ConcurrentHashMap extends AbstractMap return false; if (tr != null && (tr.parent != t || tr.hash < t.hash)) return false; - if (t.red && tl != null && tl.red && tr != null && tr.red) + if (t.red && (tl != null && tl.red || tr != null && tr.red)) return false; if (tl != null && !checkInvariants(tl)) return false; From b4c3629cbaf8733669043a45a1916c017f04e5f7 Mon Sep 17 00:00:00 2001 From: Saint Wesonga Date: Tue, 24 Feb 2026 11:27:16 +0000 Subject: [PATCH 012/636] 8377702: Disable AArch64 SpinPause tests on Windows Reviewed-by: dholmes, aph --- test/hotspot/gtest/aarch64/test_spin_pause.cpp | 4 +++- test/hotspot/jtreg/gtest/TestSpinPauseAArch64.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/hotspot/gtest/aarch64/test_spin_pause.cpp b/test/hotspot/gtest/aarch64/test_spin_pause.cpp index e220362eae9..98d05e030c2 100644 --- a/test/hotspot/gtest/aarch64/test_spin_pause.cpp +++ b/test/hotspot/gtest/aarch64/test_spin_pause.cpp @@ -21,7 +21,9 @@ * questions. */ -#if defined(AARCH64) && !defined(ZERO) +// Skip Windows to prevent GTestWrapper.java from failing because +// SpinPause is not implemented on Windows (and therefore returns 0) +#if defined(AARCH64) && !defined(ZERO) && !defined(_WINDOWS) #include "utilities/spinYield.hpp" #include "unittest.hpp" diff --git a/test/hotspot/jtreg/gtest/TestSpinPauseAArch64.java b/test/hotspot/jtreg/gtest/TestSpinPauseAArch64.java index 475c86d889f..6adf00af250 100644 --- a/test/hotspot/jtreg/gtest/TestSpinPauseAArch64.java +++ b/test/hotspot/jtreg/gtest/TestSpinPauseAArch64.java @@ -28,6 +28,7 @@ * @library /test/lib * @requires vm.flagless * @requires os.arch=="aarch64" + * @requires os.family != "windows" * @run main/native GTestWrapper --gtest_filter=SpinPause* * @run main/native GTestWrapper --gtest_filter=SpinPause* -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none * @run main/native GTestWrapper --gtest_filter=SpinPause* -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop @@ -41,6 +42,6 @@ * @summary Run SpinPause gtest using SB instruction for SpinPause * @library /test/lib * @requires vm.flagless - * @requires (os.arch=="aarch64" & vm.cpu.features ~= ".*sb.*") + * @requires (os.arch=="aarch64" & vm.cpu.features ~= ".*sb.*" & os.family != "windows") * @run main/native GTestWrapper --gtest_filter=SpinPause* -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=sb */ From 85d5688d37625b3dcb2e0163f5101054c59a9aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Tue, 24 Feb 2026 11:46:32 +0000 Subject: [PATCH 013/636] 8378330: Do not malloc the GrowableArrays in async_get_stack_trace Reviewed-by: dholmes, cnorrbin --- src/hotspot/share/classfile/javaClasses.cpp | 24 +++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index c6b0fcb90e0..ef1eeec14dd 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1925,16 +1925,13 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { public: const Handle _thread_h; int _depth; - GrowableArray* _methods; - GrowableArray* _bcis; + enum InitLength { len = 64 }; // Minimum length that covers most cases + GrowableArrayCHeap _methods; + GrowableArrayCHeap _bcis; GetStackTraceHandshakeClosure(Handle thread_h) : HandshakeClosure("GetStackTraceHandshakeClosure"), _thread_h(thread_h), _depth(0), - _methods(nullptr), _bcis(nullptr) { - } - ~GetStackTraceHandshakeClosure() { - delete _methods; - delete _bcis; + _methods(InitLength::len), _bcis(InitLength::len) { } void do_thread(Thread* th) { @@ -1950,11 +1947,6 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { const int max_depth = MaxJavaStackTraceDepth; const bool skip_hidden = !ShowHiddenFrames; - // Pick minimum length that will cover most cases - int init_length = 64; - _methods = new (mtInternal) GrowableArray(init_length, mtInternal); - _bcis = new (mtInternal) GrowableArray(init_length, mtInternal); - int total_count = 0; vframeStream vfst(java_thread != nullptr ? vframeStream(java_thread, false, false, vthread_carrier) // we don't process frames as we don't care about oops @@ -1968,8 +1960,8 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { continue; } - _methods->push(vfst.method()); - _bcis->push(vfst.bci()); + _methods.push(vfst.method()); + _bcis.push(vfst.bci()); total_count++; } @@ -2001,9 +1993,9 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { objArrayHandle trace = oopFactory::new_objArray_handle(k, gsthc._depth, CHECK_NULL); for (int i = 0; i < gsthc._depth; i++) { - methodHandle method(THREAD, gsthc._methods->at(i)); + methodHandle method(THREAD, gsthc._methods.at(i)); oop element = java_lang_StackTraceElement::create(method, - gsthc._bcis->at(i), + gsthc._bcis.at(i), CHECK_NULL); trace->obj_at_put(i, element); } From f1169f9d61f6f3eeb0ab3e2ddc0bbcdcaaceea04 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Tue, 24 Feb 2026 12:10:00 +0000 Subject: [PATCH 014/636] 8377309: Remove PhaseIterGVN::verify_Identity_for exclusion for Min/Max find identity op Reviewed-by: mhaessig, chagedorn --- src/hotspot/share/opto/phaseX.cpp | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 9a9a731a022..0699255a59d 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2007,27 +2007,6 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { case Op_ConvI2L: return; - // MaxNode::find_identity_operation - // Finds patterns like Max(A, Max(A, B)) -> Max(A, B) - // This can be a 2-hop search, so maybe notification is not - // good enough. - // - // Found with: - // compiler/codegen/TestBooleanVect.java - // -XX:VerifyIterativeGVN=1110 - case Op_MaxL: - case Op_MinL: - case Op_MaxI: - case Op_MinI: - case Op_MaxF: - case Op_MinF: - case Op_MaxHF: - case Op_MinHF: - case Op_MaxD: - case Op_MinD: - return; - - // AddINode::Identity // Converts (x-y)+y to x // Could be issue with notification From 0ed34913bac44f3f0895cd9ab15d4e7ff2d5f5c2 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Tue, 24 Feb 2026 12:12:14 +0000 Subject: [PATCH 015/636] 8377944: LowMemoryTest2.java#id1 intermittent fails OOME: Metaspace Reviewed-by: dholmes, cjplummer --- .../MemoryMXBean/LowMemoryTest2.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java b/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java index de1b22c4075..b093f086ba4 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java +++ b/test/jdk/java/lang/management/MemoryMXBean/LowMemoryTest2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -35,18 +35,14 @@ * @test * @bug 4982128 * @summary Test low memory detection of non-heap memory pool - * - * @run main/othervm/timeout=600 -Xnoclassgc -XX:MaxMetaspaceSize=32m - * LowMemoryTest2 - */ - -/* - * @test - * @bug 4982128 - * @summary Test low memory detection of non-heap memory pool - * - * @run main/othervm/timeout=600 -Xnoclassgc -XX:MaxMetaspaceSize=16m - * -XX:CompressedClassSpaceSize=4m LowMemoryTest2 + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm/timeout=600 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xnoclassgc -XX:MaxMetaspaceSize=32m LowMemoryTest2 + * @run main/othervm/timeout=600 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xnoclassgc -XX:MaxMetaspaceSize=16m + * -XX:CompressedClassSpaceSize=4m LowMemoryTest2 */ import java.lang.management.*; @@ -54,6 +50,8 @@ import javax.management.*; import javax.management.openmbean.CompositeData; import java.util.*; +import jdk.test.whitebox.WhiteBox; + public class LowMemoryTest2 { private static volatile boolean listenerInvoked = false; @@ -177,7 +175,7 @@ public class LowMemoryTest2 { // If we don't force a GC we may get an // OutOfMemoryException before the counters are updated. System.out.println("Force GC"); - System.gc(); + WhiteBox.getWhiteBox().fullGC(); } isThresholdCountSet = isAnyThresholdCountSet(pools); } From e452d47867ca76449365d14f61332d0eb1a096ac Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Tue, 24 Feb 2026 12:20:18 +0000 Subject: [PATCH 016/636] 8378164: test/jdk/java/net/httpclient/http3/*.java: convert tests that use ITestContext to JUnit Reviewed-by: vyazici --- .../net/httpclient/http3/GetHTTP3Test.java | 133 +++++++++-------- .../httpclient/http3/H3DataLimitsTest.java | 99 +++++++------ .../http3/H3MaxInitialTimeoutTest.java | 91 +++++++----- .../net/httpclient/http3/PostHTTP3Test.java | 136 +++++++++--------- 4 files changed, 254 insertions(+), 205 deletions(-) diff --git a/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java b/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java index 17fc33f3aa5..800a02eb2c4 100644 --- a/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java +++ b/test/jdk/java/net/httpclient/http3/GetHTTP3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -44,30 +44,31 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; import jdk.test.lib.Utils; import jdk.test.lib.net.SimpleSSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; -import org.testng.ITestContext; -import org.testng.SkipException; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.*; - +import static org.junit.jupiter.api.Assertions.*; import static java.lang.System.out; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestWatcher; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -76,7 +77,7 @@ import static java.lang.System.out; * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.Utils * @compile ../ReferenceTracker.java - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * GetHTTP3Test * @summary Basic HTTP/3 GET test @@ -92,8 +93,8 @@ public class GetHTTP3Test implements HttpServerAdapters { """; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer h3TestServer; // HTTP/2 ( h2 + h3) - String h3URI; + private static HttpTestServer h3TestServer; // HTTP/2 ( h2 + h3) + private static String h3URI; static final int ITERATION_COUNT = 4; // a shared executor helps reduce the amount of threads created by the test @@ -111,10 +112,10 @@ public class GetHTTP3Test implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - final Set sharedClientHasH3 = ConcurrentHashMap.newKeySet(); - private volatile HttpClient sharedClient; - private boolean directQuicConnectionSupported; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final Set sharedClientHasH3 = ConcurrentHashMap.newKeySet(); + private static volatile HttpClient sharedClient; + private static boolean directQuicConnectionSupported; static class TestExecutor implements Executor { final AtomicLong tasks = new AtomicLong(); @@ -140,21 +141,38 @@ public class GetHTTP3Test implements HttpServerAdapters { } } - protected boolean stopAfterFirstFailure() { + private static boolean stopAfterFirstFailure() { return Boolean.getBoolean("jdk.internal.httpclient.debug"); } - @BeforeMethod - void beforeMethod(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - var x = new SkipException("Skipping: some test failed"); - x.setStackTrace(new StackTraceElement[0]); - throw x; + static final class TestStopper implements TestWatcher, BeforeEachCallback { + final AtomicReference failed = new AtomicReference<>(); + TestStopper() { } + @Override + public void testFailed(ExtensionContext context, Throwable cause) { + if (stopAfterFirstFailure()) { + String msg = "Aborting due to: " + cause; + failed.compareAndSet(null, msg); + FAILURES.putIfAbsent(context.getDisplayName(), cause); + System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + } + } + + @Override + public void beforeEach(ExtensionContext context) { + String msg = failed.get(); + Assumptions.assumeTrue(msg == null, msg); } } - @AfterClass - final void printFailedTests() { + @RegisterExtension + static final TestStopper stopper = new TestStopper(); + + @AfterAll + static void printFailedTests() { out.println("\n========================="); try { out.printf("%n%sCreated %d servers and %d clients%n", @@ -174,24 +192,20 @@ public class GetHTTP3Test implements HttpServerAdapters { } } - private String[] uris() { + private static String[] uris() { return new String[] { h3URI, }; } - @DataProvider(name = "variants") - public Object[][] variants(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - return new Object[0][]; - } + public static Object[][] variants() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2 * 2 * 2][]; int i = 0; for (var version : List.of(Optional.empty(), Optional.of(HTTP_3))) { for (Version firstRequestVersion : List.of(HTTP_2, HTTP_3)) { for (boolean sameClient : List.of(false, true)) { - for (String uri : uris()) { + for (String uri : uris) { result[i++] = new Object[]{uri, firstRequestVersion, sameClient, version}; } } @@ -201,15 +215,6 @@ public class GetHTTP3Test implements HttpServerAdapters { return result; } - @DataProvider(name = "uris") - public Object[][] uris(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - return new Object[0][]; - } - Object[][] result = {{h3URI}}; - return result; - } - private HttpClient makeNewClient() { clientCount.incrementAndGet(); HttpClient client = newClientBuilderForH3() @@ -235,7 +240,8 @@ public class GetHTTP3Test implements HttpServerAdapters { } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsync(String uri, Version firstRequestVersion, boolean sameClient, Optional version) throws Exception { System.out.println("Request to " + uri +"/Async/*" + ", firstRequestVersion=" + firstRequestVersion + @@ -263,17 +269,17 @@ public class GetHTTP3Test implements HttpServerAdapters { } HttpResponse response1 = client.send(headBuilder.build(), BodyHandlers.ofString()); - assertEquals(response1.statusCode(), 200, "Unexpected first response code"); - assertEquals(response1.body(), "", "Unexpected first response body"); + assertEquals(200, response1.statusCode(), "Unexpected first response code"); + assertEquals("", response1.body(), "Unexpected first response body"); boolean expectH3 = sameClient && sharedClientHasH3.contains(headURI.getRawAuthority()); if (firstRequestVersion == HTTP_3) { if (expectH3) { out.println("Expecting HEAD response over HTTP_3"); - assertEquals(response1.version(), HTTP_3, "Unexpected first response version"); + assertEquals(HTTP_3, response1.version(), "Unexpected first response version"); } } else { out.println("Expecting HEAD response over HTTP_2"); - assertEquals(response1.version(), HTTP_2, "Unexpected first response version"); + assertEquals(HTTP_2, response1.version(), "Unexpected first response version"); } out.println("HEAD response version: " + response1.version()); if (response1.version() == HTTP_2) { @@ -329,10 +335,10 @@ public class GetHTTP3Test implements HttpServerAdapters { out.println("Checking response: " + u); var response = e.getValue().get(); out.println("Response is: " + response + ", [version: " + response.version() + "]"); - assertEquals(response.statusCode(), 200,"status for " + u); - assertEquals(response.body(), BODY,"body for " + u); + assertEquals(200, response.statusCode(), "status for " + u); + assertEquals(BODY, response.body(), "body for " + u); if (expectH3) { - assertEquals(response.version(), HTTP_3, "version for " + u); + assertEquals(HTTP_3, response.version(), "version for " + u); } if (response.version() == HTTP_3) { h3Count++; @@ -354,7 +360,8 @@ public class GetHTTP3Test implements HttpServerAdapters { System.out.println("test: DONE"); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("uris") public void testSync(String h3URI) throws Exception { HttpClient client = makeNewClient(); Builder builder = HttpRequest.newBuilder(URI.create(h3URI + "/Sync/GET/1")) @@ -374,22 +381,22 @@ public class GetHTTP3Test implements HttpServerAdapters { HttpResponse response = client.send(request, BodyHandlers.ofString()); out.println("Response #1: " + response); out.println("Version #1: " + response.version()); - assertEquals(response.statusCode(), 200, "first response status"); + assertEquals(200, response.statusCode(), "first response status"); if (directQuicConnectionSupported) { // TODO unreliable assertion //assertEquals(response.version(), HTTP_3, "Unexpected first response version"); } else { - assertEquals(response.version(), HTTP_2, "Unexpected first response version"); + assertEquals(HTTP_2, response.version(), "Unexpected first response version"); } - assertEquals(response.body(), BODY, "first response body"); + assertEquals(BODY, response.body(), "first response body"); request = builder.uri(URI.create(h3URI + "/Sync/GET/2")).build(); response = client.send(request, BodyHandlers.ofString()); out.println("Response #2: " + response); out.println("Version #2: " + response.version()); - assertEquals(response.statusCode(), 200, "second response status"); - assertEquals(response.version(), HTTP_3, "second response version"); - assertEquals(response.body(), BODY, "second response body"); + assertEquals(200, response.statusCode(), "second response status"); + assertEquals(HTTP_3, response.version(), "second response version"); + assertEquals(BODY, response.body(), "second response body"); var tracker = TRACKER.getTracker(client); client = null; @@ -398,8 +405,8 @@ public class GetHTTP3Test implements HttpServerAdapters { if (error != null) throw error; } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { final Http2TestServer h2WithAltService = new Http2TestServer("localhost", true, sslContext).enableH3AltServiceOnSamePort(); h3TestServer = HttpTestServer.of(h2WithAltService); @@ -410,8 +417,8 @@ public class GetHTTP3Test implements HttpServerAdapters { directQuicConnectionSupported = h2WithAltService.supportsH3DirectConnection(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { System.err.println("======================================================="); System.err.println(" Tearing down test"); System.err.println("======================================================="); @@ -453,7 +460,7 @@ public class GetHTTP3Test implements HttpServerAdapters { } try (InputStream is = t.getRequestBody(); OutputStream os = t.getResponseBody()) { - assertEquals(is.readAllBytes().length, 0); + assertEquals(0, is.readAllBytes().length); if (!"HEAD".equals(t.getRequestMethod())) { String[] body = BODY.split("\n"); for (String line : body) { diff --git a/test/jdk/java/net/httpclient/http3/H3DataLimitsTest.java b/test/jdk/java/net/httpclient/http3/H3DataLimitsTest.java index 9f509b5440a..8a83b51a174 100644 --- a/test/jdk/java/net/httpclient/http3/H3DataLimitsTest.java +++ b/test/jdk/java/net/httpclient/http3/H3DataLimitsTest.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 @@ -24,14 +24,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.ITestContext; -import org.testng.SkipException; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -44,18 +36,29 @@ import java.net.http.HttpRequest; import java.net.http.HttpRequest.Builder; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; -import java.time.Duration; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestWatcher; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @@ -64,7 +67,7 @@ import static org.testng.Assert.assertEquals; * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.quic.QuicStandaloneServer - * @run testng/othervm/timeout=480 -Djdk.internal.httpclient.debug=true + * @run junit/othervm/timeout=480 -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * -Djavax.net.debug=all * H3DataLimitsTest @@ -73,8 +76,8 @@ import static org.testng.Assert.assertEquals; public class H3DataLimitsTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer h3TestServer; - String h3URI; + private static HttpTestServer h3TestServer; + private static String h3URI; static final Executor executor = new TestExecutor(Executors.newCachedThreadPool()); static final ConcurrentMap FAILURES = new ConcurrentHashMap<>(); @@ -114,20 +117,37 @@ public class H3DataLimitsTest implements HttpServerAdapters { } } - protected boolean stopAfterFirstFailure() { + private static boolean stopAfterFirstFailure() { return Boolean.getBoolean("jdk.internal.httpclient.debug"); } - @BeforeMethod - void beforeMethod(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - var x = new SkipException("Skipping: some test failed"); - x.setStackTrace(new StackTraceElement[0]); - throw x; + static final class TestStopper implements TestWatcher, BeforeEachCallback { + final AtomicReference failed = new AtomicReference<>(); + TestStopper() { } + @Override + public void testFailed(ExtensionContext context, Throwable cause) { + if (stopAfterFirstFailure()) { + String msg = "Aborting due to: " + cause; + failed.compareAndSet(null, msg); + FAILURES.putIfAbsent(context.getDisplayName(), cause); + System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + } + } + + @Override + public void beforeEach(ExtensionContext context) { + String msg = failed.get(); + Assumptions.assumeTrue(msg == null, msg); } } - @AfterClass + @RegisterExtension + static final TestStopper stopper = new TestStopper(); + + @AfterAll static void printFailedTests() { out.println("\n========================="); try { @@ -148,13 +168,8 @@ public class H3DataLimitsTest implements HttpServerAdapters { } } - @DataProvider(name = "h3URIs") - public Object[][] versions(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - return new Object[0][]; - } - Object[][] result = {{h3URI}}; - return result; + public static Object[][] versions() { + return new Object[][] {{h3URI}}; } private HttpClient makeNewClient() { @@ -168,7 +183,8 @@ public class H3DataLimitsTest implements HttpServerAdapters { return client; } - @Test(dataProvider = "h3URIs") + @ParameterizedTest + @MethodSource("versions") public void testHugeResponse(final String h3URI) throws Exception { HttpClient client = makeNewClient(); URI uri = URI.create(h3URI + "?16000000"); @@ -180,17 +196,18 @@ public class H3DataLimitsTest implements HttpServerAdapters { HttpResponse response = client.send(request, BodyHandlers.ofString()); out.println("Response #1: " + response); out.println("Version #1: " + response.version()); - assertEquals(response.statusCode(), 200, "first response status"); - assertEquals(response.version(), HTTP_3, "first response version"); + assertEquals(200, response.statusCode(), "first response status"); + assertEquals(HTTP_3, response.version(), "first response version"); response = client.send(request, BodyHandlers.ofString()); out.println("Response #2: " + response); out.println("Version #2: " + response.version()); - assertEquals(response.statusCode(), 200, "second response status"); - assertEquals(response.version(), HTTP_3, "second response version"); + assertEquals(200, response.statusCode(), "second response status"); + assertEquals(HTTP_3, response.version(), "second response version"); } - @Test(dataProvider = "h3URIs") + @ParameterizedTest + @MethodSource("versions") public void testManySmallResponses(final String h3URI) throws Exception { HttpClient client = makeNewClient(); URI uri = URI.create(h3URI + "?160000"); @@ -203,13 +220,13 @@ public class H3DataLimitsTest implements HttpServerAdapters { HttpResponse response = client.send(request, BodyHandlers.ofString()); out.println("Response #" + i + ": " + response); out.println("Version #" + i + ": " + response.version()); - assertEquals(response.statusCode(), 200, "response status"); - assertEquals(response.version(), HTTP_3, "response version"); + assertEquals(200, response.statusCode(), "response status"); + assertEquals(HTTP_3, response.version(), "response version"); } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // An HTTP/3 server that only supports HTTP/3 h3TestServer = HttpTestServer.of(new Http3TestServer(sslContext)); final HttpTestHandler h3Handler = new Handler(); @@ -220,8 +237,8 @@ public class H3DataLimitsTest implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { System.err.println("======================================================="); System.err.println(" Tearing down test"); System.err.println("======================================================="); diff --git a/test/jdk/java/net/httpclient/http3/H3MaxInitialTimeoutTest.java b/test/jdk/java/net/httpclient/http3/H3MaxInitialTimeoutTest.java index 27eec672bcd..a93c6a37594 100644 --- a/test/jdk/java/net/httpclient/http3/H3MaxInitialTimeoutTest.java +++ b/test/jdk/java/net/httpclient/http3/H3MaxInitialTimeoutTest.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 @@ -39,25 +39,29 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.ITestContext; -import org.testng.SkipException; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestWatcher; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @@ -68,15 +72,15 @@ import static org.testng.Assert.assertEquals; * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.quic.QuicStandaloneServer - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors,quic:controls * -Djdk.httpclient.quic.maxInitialTimeout=1 * H3MaxInitialTimeoutTest - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors,quic:controls * -Djdk.httpclient.quic.maxInitialTimeout=2 * H3MaxInitialTimeoutTest - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors,quic:controls * -Djdk.httpclient.quic.maxInitialTimeout=2147483647 * H3MaxInitialTimeoutTest @@ -84,8 +88,8 @@ import static org.testng.Assert.assertEquals; public class H3MaxInitialTimeoutTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - DatagramChannel receiver; - String h3URI; + static DatagramChannel receiver; + static String h3URI; static final Executor executor = new TestExecutor(Executors.newVirtualThreadPerTaskExecutor()); static final ConcurrentMap FAILURES = new ConcurrentHashMap<>(); @@ -125,20 +129,37 @@ public class H3MaxInitialTimeoutTest implements HttpServerAdapters { } } - protected boolean stopAfterFirstFailure() { + private static boolean stopAfterFirstFailure() { return Boolean.getBoolean("jdk.internal.httpclient.debug"); } - @BeforeMethod - void beforeMethod(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - var x = new SkipException("Skipping: some test failed"); - x.setStackTrace(new StackTraceElement[0]); - throw x; + static final class TestStopper implements TestWatcher, BeforeEachCallback { + final AtomicReference failed = new AtomicReference<>(); + TestStopper() { } + @Override + public void testFailed(ExtensionContext context, Throwable cause) { + if (stopAfterFirstFailure()) { + String msg = "Aborting due to: " + cause; + failed.compareAndSet(null, msg); + FAILURES.putIfAbsent(context.getDisplayName(), cause); + System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + } + } + + @Override + public void beforeEach(ExtensionContext context) { + String msg = failed.get(); + Assumptions.assumeTrue(msg == null, msg); } } - @AfterClass + @RegisterExtension + static final TestStopper stopper = new TestStopper(); + + @AfterAll static void printFailedTests() { out.println("\n========================="); try { @@ -159,13 +180,8 @@ public class H3MaxInitialTimeoutTest implements HttpServerAdapters { } } - @DataProvider(name = "h3URIs") - public Object[][] versions(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - return new Object[0][]; - } - Object[][] result = {{h3URI}}; - return result; + public static Object[][] versions() { + return new Object[][] {{h3URI}}; } private HttpClient makeNewClient(long connectionTimeout) { @@ -180,7 +196,8 @@ public class H3MaxInitialTimeoutTest implements HttpServerAdapters { return client; } - @Test(dataProvider = "h3URIs") + @ParameterizedTest + @MethodSource("versions") public void testTimeout(final String h3URI) throws Exception { long timeout = Long.getLong("jdk.httpclient.quic.maxInitialTimeout", 30); long connectionTimeout = timeout == Integer.MAX_VALUE ? 2 : 10 * timeout; @@ -196,8 +213,8 @@ public class H3MaxInitialTimeoutTest implements HttpServerAdapters { HttpResponse response = client.send(request, BodyHandlers.ofString()); out.println("Response #1: " + response); out.println("Version #1: " + response.version()); - assertEquals(response.statusCode(), 200, "first response status"); - assertEquals(response.version(), HTTP_3, "first response version"); + assertEquals(200, response.statusCode(), "first response status"); + assertEquals(HTTP_3, response.version(), "first response version"); throw new AssertionError("Expected ConnectException not thrown"); } catch (ConnectException c) { String msg = c.getMessage(); @@ -222,8 +239,8 @@ public class H3MaxInitialTimeoutTest implements HttpServerAdapters { } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { receiver = DatagramChannel.open(); receiver.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); h3URI = URIBuilder.newBuilder() @@ -235,8 +252,8 @@ public class H3MaxInitialTimeoutTest implements HttpServerAdapters { .toString(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { System.err.println("======================================================="); System.err.println(" Tearing down test"); System.err.println("======================================================="); diff --git a/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java b/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java index 90f2c41f4e5..1f109e27ebc 100644 --- a/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java +++ b/test/jdk/java/net/httpclient/http3/PostHTTP3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -49,6 +49,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import javax.net.ssl.SSLContext; @@ -56,24 +57,25 @@ import jdk.test.lib.Utils; import jdk.test.lib.net.SimpleSSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; -import org.testng.ITestContext; -import org.testng.SkipException; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.*; - +import static org.junit.jupiter.api.Assertions.*; import static java.lang.System.out; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestWatcher; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* * @test * @library /test/lib /test/jdk/java/net/httpclient/lib @@ -81,7 +83,7 @@ import static java.lang.System.out; * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.Utils * @compile ../ReferenceTracker.java - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * PostHTTP3Test * @summary Basic HTTP/3 POST test @@ -97,8 +99,8 @@ public class PostHTTP3Test implements HttpServerAdapters { """; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer h3TestServer; // HTTP/2 ( h2 + h3) - String h3URI; + static HttpTestServer h3TestServer; // HTTP/2 ( h2 + h3) + static String h3URI; static final int ITERATION_COUNT = 4; // a shared executor helps reduce the amount of threads created by the test @@ -116,10 +118,10 @@ public class PostHTTP3Test implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - final Set sharedClientHasH3 = ConcurrentHashMap.newKeySet(); - private volatile HttpClient sharedClient; - private boolean directQuicConnectionSupported; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final Set sharedClientHasH3 = ConcurrentHashMap.newKeySet(); + private static volatile HttpClient sharedClient; + private static boolean directQuicConnectionSupported; static class TestExecutor implements Executor { final AtomicLong tasks = new AtomicLong(); @@ -145,20 +147,37 @@ public class PostHTTP3Test implements HttpServerAdapters { } } - protected boolean stopAfterFirstFailure() { + private static boolean stopAfterFirstFailure() { return Boolean.getBoolean("jdk.internal.httpclient.debug"); } - @BeforeMethod - void beforeMethod(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - var x = new SkipException("Skipping: some test failed"); - x.setStackTrace(new StackTraceElement[0]); - throw x; + static final class TestStopper implements TestWatcher, BeforeEachCallback { + final AtomicReference failed = new AtomicReference<>(); + TestStopper() { } + @Override + public void testFailed(ExtensionContext context, Throwable cause) { + if (stopAfterFirstFailure()) { + String msg = "Aborting due to: " + cause; + failed.compareAndSet(null, msg); + FAILURES.putIfAbsent(context.getDisplayName(), cause); + System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + } + } + + @Override + public void beforeEach(ExtensionContext context) { + String msg = failed.get(); + Assumptions.assumeTrue(msg == null, msg); } } - @AfterClass + @RegisterExtension + static final TestStopper stopper = new TestStopper(); + + @AfterAll static void printFailedTests() { out.println("\n========================="); try { @@ -179,24 +198,20 @@ public class PostHTTP3Test implements HttpServerAdapters { } } - private String[] uris() { + private static String[] uris() { return new String[] { h3URI, }; } - @DataProvider(name = "variants") - public Object[][] variants(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - return new Object[0][]; - } + public static Object[][] variants() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2 * 2 * 2][]; int i = 0; for (var version : List.of(Optional.empty(), Optional.of(HTTP_3))) { for (Version firstRequestVersion : List.of(HTTP_2, HTTP_3)) { for (boolean sameClient : List.of(false, true)) { - for (String uri : uris()) { + for (String uri : uris) { result[i++] = new Object[]{uri, firstRequestVersion, sameClient, version}; } } @@ -206,15 +221,6 @@ public class PostHTTP3Test implements HttpServerAdapters { return result; } - @DataProvider(name = "uris") - public Object[][] uris(ITestContext context) { - if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { - return new Object[0][]; - } - Object[][] result = {{h3URI}}; - return result; - } - private HttpClient makeNewClient() { clientCount.incrementAndGet(); HttpClient client = newClientBuilderForH3() @@ -259,7 +265,8 @@ public class PostHTTP3Test implements HttpServerAdapters { } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsync(String uri, Version firstRequestVersion, boolean sameClient, Optional version) throws Exception { System.out.println("Request to " + uri +"/Async/*" + ", firstRequestVersion=" + firstRequestVersion + @@ -287,17 +294,17 @@ public class PostHTTP3Test implements HttpServerAdapters { } HttpResponse response1 = client.send(headBuilder.build(), BodyHandlers.ofString()); - assertEquals(response1.statusCode(), 200, "Unexpected first response code"); - assertEquals(response1.body(), "", "Unexpected first response body"); + assertEquals(200, response1.statusCode(), "Unexpected first response code"); + assertEquals("", response1.body(), "Unexpected first response body"); boolean expectH3 = sameClient && sharedClientHasH3.contains(headURI.getRawAuthority()); if (firstRequestVersion == HTTP_3) { if (expectH3) { out.println("Expecting HEAD response over HTTP_3"); - assertEquals(response1.version(), HTTP_3, "Unexpected first response version"); + assertEquals(HTTP_3, response1.version(), "Unexpected first response version"); } } else { out.println("Expecting HEAD response over HTTP_2"); - assertEquals(response1.version(), HTTP_2, "Unexpected first response version"); + assertEquals(HTTP_2, response1.version(), "Unexpected first response version"); } out.println("HEAD response version: " + response1.version()); if (response1.version() == HTTP_2) { @@ -356,10 +363,10 @@ public class PostHTTP3Test implements HttpServerAdapters { out.println("Checking response: " + u); var response = e.getValue().get(); out.println("Response is: " + response + ", [version: " + response.version() + "]"); - assertEquals(response.statusCode(), 200,"status for " + u); - assertEquals(response.body(), BODY,"body for " + u); + assertEquals(200, response.statusCode(), "status for " + u); + assertEquals(BODY, response.body(), "body for " + u); if (expectH3) { - assertEquals(response.version(), HTTP_3, "version for " + u); + assertEquals(HTTP_3, response.version(), "version for " + u); } if (response.version() == HTTP_3) { h3Count++; @@ -381,7 +388,8 @@ public class PostHTTP3Test implements HttpServerAdapters { System.out.println("test: DONE"); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("uris") public void testSync(String h3URI) throws Exception { HttpClient client = makeNewClient(); Builder builder = HttpRequest.newBuilder(URI.create(h3URI + "/Sync/1")) @@ -403,14 +411,14 @@ public class PostHTTP3Test implements HttpServerAdapters { out.println("Response #1: " + response); out.println("Version #1: " + response.version()); out.println("Body #1:\n" + response.body().indent(4)); - assertEquals(response.statusCode(), 200, "first response status"); + assertEquals(200, response.statusCode(), "first response status"); if (directQuicConnectionSupported) { // TODO unreliable assertion //assertEquals(response.version(), HTTP_3, "Unexpected first response version"); } else { - assertEquals(response.version(), HTTP_2, "Unexpected first response version"); + assertEquals(HTTP_2, response.version(), "Unexpected first response version"); } - assertEquals(response.body(), BODY, "first response body"); + assertEquals(BODY, response.body(), "first response body"); request = builder.uri(URI.create(h3URI + "/Sync/2")) .POST(oflines(true, BODY.split("\n"))) @@ -419,9 +427,9 @@ public class PostHTTP3Test implements HttpServerAdapters { out.println("Response #2: " + response); out.println("Version #2: " + response.version()); out.println("Body #2:\n" + response.body().indent(4)); - assertEquals(response.statusCode(), 200, "second response status"); - assertEquals(response.version(), HTTP_3, "second response version"); - assertEquals(response.body(), BODY, "second response body"); + assertEquals(200, response.statusCode(), "second response status"); + assertEquals(HTTP_3, response.version(), "second response version"); + assertEquals(BODY, response.body(), "second response body"); request = builder.uri(URI.create(h3URI + "/Sync/3")) .POST(oflines(true, BODY.split("\n"))) @@ -430,9 +438,9 @@ public class PostHTTP3Test implements HttpServerAdapters { out.println("Response #3: " + response); out.println("Version #3: " + response.version()); out.println("Body #3:\n" + response.body().indent(4)); - assertEquals(response.statusCode(), 200, "third response status"); - assertEquals(response.version(), HTTP_3, "third response version"); - assertEquals(response.body(), BODY, "third response body"); + assertEquals(200, response.statusCode(), "third response status"); + assertEquals(HTTP_3, response.version(), "third response version"); + assertEquals(BODY, response.body(), "third response body"); var tracker = TRACKER.getTracker(client); client = null; @@ -441,8 +449,8 @@ public class PostHTTP3Test implements HttpServerAdapters { if (error != null) throw error; } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { final Http2TestServer h2WithAltService = new Http2TestServer("localhost", true, sslContext) .enableH3AltServiceOnSamePort(); h3TestServer = HttpTestServer.of(h2WithAltService); @@ -454,8 +462,8 @@ public class PostHTTP3Test implements HttpServerAdapters { directQuicConnectionSupported = h2WithAltService.supportsH3DirectConnection(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { System.err.println("======================================================="); System.err.println(" Tearing down test"); System.err.println("======================================================="); From 49f14eb9fca155319d5475019715189e9f65dffd Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Tue, 24 Feb 2026 13:57:01 +0000 Subject: [PATCH 017/636] 8378344: Refactor test/jdk/java/net/httpclient/*.java TestNG tests to JUnit Reviewed-by: vyazici --- .../AbstractConnectTimeoutHandshake.java | 25 +- .../java/net/httpclient/AbstractNoBody.java | 25 +- .../net/httpclient/AltServiceUsageTest.java | 81 +++--- .../net/httpclient/AsFileDownloadTest.java | 82 +++--- .../net/httpclient/AsyncExecutorShutdown.java | 80 +++--- .../java/net/httpclient/AsyncShutdownNow.java | 76 +++--- .../net/httpclient/AuthFilterCacheTest.java | 75 +++--- .../net/httpclient/BasicRedirectTest.java | 102 ++++---- .../net/httpclient/BodySubscribersTest.java | 47 ++-- .../BufferingSubscriberCancelTest.java | 30 ++- .../BufferingSubscriberErrorCompleteTest.java | 55 ++-- .../httpclient/BufferingSubscriberTest.java | 70 ++--- .../CancelledPartialResponseTest.java | 45 ++-- .../net/httpclient/CancelledResponse2.java | 47 ++-- .../net/httpclient/ConcurrentResponses.java | 56 ++-- .../net/httpclient/ConnectExceptionTest.java | 28 +- .../ConnectTimeoutHandshakeAsync.java | 11 +- .../ConnectTimeoutHandshakeSync.java | 11 +- .../httpclient/ContentLengthHeaderTest.java | 146 ++++++----- .../java/net/httpclient/CookieHeaderTest.java | 74 +++--- .../httpclient/CustomRequestPublisher.java | 70 ++--- .../httpclient/CustomResponseSubscriber.java | 59 ++--- .../net/httpclient/DependentActionsTest.java | 117 ++++----- .../DependentPromiseActionsTest.java | 96 +++---- .../net/httpclient/EncodedCharsInURI.java | 97 +++---- .../net/httpclient/EscapedOctetsInURI.java | 89 ++++--- .../java/net/httpclient/ExecutorShutdown.java | 76 +++--- .../java/net/httpclient/ExpectContinue.java | 57 ++-- .../net/httpclient/ExpectContinueTest.java | 48 ++-- .../net/httpclient/FilePublisherTest.java | 65 ++--- .../httpclient/FlowAdapterPublisherTest.java | 91 ++++--- .../httpclient/FlowAdapterSubscriberTest.java | 203 ++++++++------- .../net/httpclient/GZIPInputStreamTest.java | 80 +++--- test/jdk/java/net/httpclient/HeadTest.java | 53 ++-- .../jdk/java/net/httpclient/HeadersTest1.java | 16 +- .../net/httpclient/HttpClientBuilderTest.java | 115 ++++---- .../java/net/httpclient/HttpClientClose.java | 86 +++--- .../httpclient/HttpClientExceptionTest.java | 13 +- .../httpclient/HttpClientLocalAddrTest.java | 43 +-- .../net/httpclient/HttpClientShutdown.java | 76 +++--- .../java/net/httpclient/HttpHeadersOf.java | 171 ++++++------ .../java/net/httpclient/HttpRedirectTest.java | 143 +++++----- .../httpclient/HttpRequestNewBuilderTest.java | 150 ++++++----- .../HttpResponseInputStreamTest.java | 31 ++- .../java/net/httpclient/HttpVersionsTest.java | 93 +++---- .../httpclient/IdleConnectionTimeoutTest.java | 45 ++-- .../net/httpclient/ImmutableFlowItems.java | 60 ++--- ...InvalidInputStreamSubscriptionRequest.java | 85 +++--- .../net/httpclient/InvalidSSLContextTest.java | 48 ++-- .../InvalidSubscriptionRequest.java | 93 ++++--- .../net/httpclient/LineBodyHandlerTest.java | 246 ++++++++++-------- .../LineStreamsAndSurrogatesTest.java | 51 ++-- .../LineSubscribersAndSurrogatesTest.java | 59 ++--- .../httpclient/MappingResponseSubscriber.java | 60 ++--- test/jdk/java/net/httpclient/MaxStreams.java | 46 ++-- .../java/net/httpclient/NoBodyPartOne.java | 31 ++- .../java/net/httpclient/NoBodyPartThree.java | 44 ++-- .../java/net/httpclient/NoBodyPartTwo.java | 34 ++- .../net/httpclient/NonAsciiCharsInURI.java | 81 +++--- .../net/httpclient/RedirectMethodChange.java | 57 ++-- .../net/httpclient/RedirectTimeoutTest.java | 31 +-- .../net/httpclient/RedirectWithCookie.java | 59 ++--- .../java/net/httpclient/RequestBodyTest.java | 64 ++--- .../net/httpclient/RequestBuilderTest.java | 147 ++++++----- .../java/net/httpclient/Response1xxTest.java | 59 ++--- .../httpclient/ResponseBodyBeforeError.java | 64 ++--- .../net/httpclient/ResponsePublisher.java | 108 ++++---- test/jdk/java/net/httpclient/RetryPost.java | 46 ++-- .../java/net/httpclient/RetryWithCookie.java | 59 ++--- .../java/net/httpclient/SSLExceptionTest.java | 16 +- .../httpclient/SendResponseHeadersTest.java | 32 +-- .../java/net/httpclient/ServerCloseTest.java | 49 ++-- test/jdk/java/net/httpclient/ShutdownNow.java | 74 +++--- .../java/net/httpclient/StreamCloseTest.java | 26 +- .../httpclient/SubscriberAPIExceptions.java | 10 +- test/jdk/java/net/httpclient/TestKitTest.java | 48 ++-- .../java/net/httpclient/TlsContextTest.java | 48 ++-- .../java/net/httpclient/UnauthorizedTest.java | 66 +++-- .../java/net/httpclient/UserCookieTest.java | 71 ++--- 79 files changed, 2804 insertions(+), 2617 deletions(-) diff --git a/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java b/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java index 3febe7145b4..d60fe4f8bcd 100644 --- a/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java +++ b/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -46,24 +46,24 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import jdk.test.lib.net.URIBuilder; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.time.Duration.*; import static java.util.concurrent.TimeUnit.NANOSECONDS; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; abstract class AbstractConnectTimeoutHandshake { // The number of iterations each testXXXClient performs. static final int TIMES = 2; - Server server; - URI httpsURI; + private static Server server; + private static URI httpsURI; static final Duration NO_DURATION = null; @@ -79,8 +79,7 @@ abstract class AbstractConnectTimeoutHandshake { static final List METHODS = List.of("GET" , "POST"); static final List VERSIONS = List.of(HTTP_2, HTTP_1_1); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { List l = new ArrayList<>(); for (List timeouts : TIMEOUTS) { Duration connectTimeout = timeouts.get(0); @@ -198,8 +197,8 @@ abstract class AbstractConnectTimeoutHandshake { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { server = new Server(); httpsURI = URIBuilder.newBuilder() .scheme("https") @@ -210,8 +209,8 @@ abstract class AbstractConnectTimeoutHandshake { out.println("HTTPS URI: " + httpsURI); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { server.close(); out.printf("%n--- teardown ---%n"); diff --git a/test/jdk/java/net/httpclient/AbstractNoBody.java b/test/jdk/java/net/httpclient/AbstractNoBody.java index d0908a6e4cd..43847a2eda1 100644 --- a/test/jdk/java/net/httpclient/AbstractNoBody.java +++ b/test/jdk/java/net/httpclient/AbstractNoBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -38,9 +38,6 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; import static java.lang.System.err; import static java.lang.System.out; @@ -49,8 +46,16 @@ import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; + +// Use TestInstance.Lifecycle.PER_CLASS because we need access +// to this.getClass() in methods that are called from +// @BeforeAll and @AfterAll +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public abstract class AbstractNoBody implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); @@ -76,7 +81,6 @@ public abstract class AbstractNoBody implements HttpServerAdapters { // a shared executor helps reduce the amount of threads created by the test static final ExecutorService executor = Executors.newFixedThreadPool(ITERATION_COUNT * 2); static final ExecutorService serverExecutor = Executors.newFixedThreadPool(ITERATION_COUNT * 4); - static final AtomicLong serverCount = new AtomicLong(); static final AtomicLong clientCount = new AtomicLong(); static final long start = System.nanoTime(); public static String now() { @@ -87,7 +91,6 @@ public abstract class AbstractNoBody implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - @DataProvider(name = "variants") public Object[][] variants() { return new Object[][]{ { http3URI_fixed, false,}, @@ -145,8 +148,8 @@ public abstract class AbstractNoBody implements HttpServerAdapters { var request = newRequestBuilder(http3URI_head) .HEAD().version(HTTP_2).build(); var response = client.send(request, BodyHandlers.ofString()); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_2); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_2, response.version()); out.println("\n" + now() + "--- HEAD request succeeded ----\n"); err.println("\n" + now() + "--- HEAD request succeeded ----\n"); return response; @@ -182,7 +185,7 @@ public abstract class AbstractNoBody implements HttpServerAdapters { } } - @BeforeTest + @BeforeAll public void setup() throws Exception { printStamp(START, "setup"); HttpServerAdapters.enableServerLogging(); @@ -252,7 +255,7 @@ public abstract class AbstractNoBody implements HttpServerAdapters { printStamp(END,"setup"); } - @AfterTest + @AfterAll public void teardown() throws Exception { printStamp(START, "teardown"); sharedClient.close(); diff --git a/test/jdk/java/net/httpclient/AltServiceUsageTest.java b/test/jdk/java/net/httpclient/AltServiceUsageTest.java index 41f3aef9bd1..59ad2179b90 100644 --- a/test/jdk/java/net/httpclient/AltServiceUsageTest.java +++ b/test/jdk/java/net/httpclient/AltServiceUsageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -37,36 +37,37 @@ import javax.net.ssl.SSLContext; import jdk.test.lib.net.SimpleSSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + /* * @test * @summary Verifies that the HTTP client correctly handles various alt-svc usages * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters * - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * AltServiceUsageTest */ public class AltServiceUsageTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer originServer; - private HttpTestServer altServer; + private static HttpTestServer originServer; + private static HttpTestServer altServer; - private DatagramChannel udpNotResponding; + private static DatagramChannel udpNotResponding; - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { // attempt to create an HTTP/3 server, an HTTP/2 server, and a // DatagramChannel bound to the same port as the HTTP/2 server int count = 0; @@ -99,7 +100,7 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.err.println("**** All servers started. Test will start shortly ****"); } - private void createServers() throws IOException { + private static void createServers() throws IOException { altServer = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); altServer.addHandler(new All200OKHandler(), "/foo/"); altServer.addHandler(new RequireAltUsedHeader(), "/bar/"); @@ -114,8 +115,8 @@ public class AltServiceUsageTest implements HttpServerAdapters { originServer.start(); } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { safeStop(originServer); safeStop(altServer); safeClose(udpNotResponding); @@ -271,14 +272,14 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Issuing request " + reqURI); final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(response.body(), H3AltServicePublisher.RESPONSE_CONTENT, + Assertions.assertEquals(200, response.statusCode(), "Unexpected response code"); + Assertions.assertEquals(H3AltServicePublisher.RESPONSE_CONTENT, response.body(), "Unexpected response body"); final Optional altSvcHeader = response.headers().firstValue("alt-svc"); - Assert.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); + Assertions.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); System.out.println("Received alt-svc header value: " + altSvcHeader.get()); final String expectedHeader = "h3=\"" + toHostPort(altServer) + "\""; - Assert.assertTrue(altSvcHeader.get().contains(expectedHeader), + Assertions.assertTrue(altSvcHeader.get().contains(expectedHeader), "Unexpected alt-svc header value: " + altSvcHeader.get() + ", was expected to contain: " + expectedHeader); @@ -286,8 +287,8 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Again issuing request " + reqURI); final HttpResponse secondResponse = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(secondResponse.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(secondResponse.body(), All200OKHandler.RESPONSE_CONTENT, + Assertions.assertEquals(200, secondResponse.statusCode(), "Unexpected response code"); + Assertions.assertEquals(All200OKHandler.RESPONSE_CONTENT, secondResponse.body(), "Unexpected response body"); var TRACKER = ReferenceTracker.INSTANCE; var tracker = TRACKER.getTracker(client); @@ -318,14 +319,14 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Issuing request " + reqURI); final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(response.statusCode(), 421, "Unexpected response code"); - Assert.assertEquals(response.body(), H3AltServicePublisher.RESPONSE_CONTENT, + Assertions.assertEquals(421, response.statusCode(), "Unexpected response code"); + Assertions.assertEquals(H3AltServicePublisher.RESPONSE_CONTENT, response.body(), "Unexpected response body"); final Optional altSvcHeader = response.headers().firstValue("alt-svc"); - Assert.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); + Assertions.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); System.out.println("Received alt-svc header value: " + altSvcHeader.get()); final String expectedHeader = "h3=\"" + toHostPort(altServer) + "\""; - Assert.assertTrue(altSvcHeader.get().contains(expectedHeader), + Assertions.assertTrue(altSvcHeader.get().contains(expectedHeader), "Unexpected alt-svc header value: " + altSvcHeader.get() + ", was expected to contain: " + expectedHeader); @@ -333,8 +334,8 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Again issuing request " + reqURI); final HttpResponse secondResponse = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(secondResponse.statusCode(), 421, "Unexpected response code"); - Assert.assertEquals(response.body(), H3AltServicePublisher.RESPONSE_CONTENT, + Assertions.assertEquals(421, secondResponse.statusCode(), "Unexpected response code"); + Assertions.assertEquals(H3AltServicePublisher.RESPONSE_CONTENT, response.body(), "Unexpected response body"); var TRACKER = ReferenceTracker.INSTANCE; var tracker = TRACKER.getTracker(client); @@ -369,14 +370,14 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Issuing request " + reqURI); final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(response.body(), H3AltServicePublisher.RESPONSE_CONTENT, + Assertions.assertEquals(200, response.statusCode(), "Unexpected response code"); + Assertions.assertEquals(H3AltServicePublisher.RESPONSE_CONTENT, response.body(), "Unexpected response body"); final Optional altSvcHeader = response.headers().firstValue("alt-svc"); - Assert.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); + Assertions.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); System.out.println("Received alt-svc header value: " + altSvcHeader.get()); final String expectedHeader = "h3=\"" + toHostPort(altServer) + "\""; - Assert.assertTrue(altSvcHeader.get().contains(expectedHeader), + Assertions.assertTrue(altSvcHeader.get().contains(expectedHeader), "Unexpected alt-svc header value: " + altSvcHeader.get() + ", was expected to contain: " + expectedHeader); @@ -386,8 +387,8 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Again issuing request " + reqURI); final HttpResponse secondResponse = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(secondResponse.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(secondResponse.body(), RequireAltUsedHeader.RESPONSE_CONTENT, + Assertions.assertEquals(200, secondResponse.statusCode(), "Unexpected response code"); + Assertions.assertEquals(RequireAltUsedHeader.RESPONSE_CONTENT, secondResponse.body(), "Unexpected response body"); var TRACKER = ReferenceTracker.INSTANCE; var tracker = TRACKER.getTracker(client); @@ -424,14 +425,14 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Issuing request " + reqURI); final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(response.body(), H3AltServicePublisher.RESPONSE_CONTENT, + Assertions.assertEquals(200, response.statusCode(), "Unexpected response code"); + Assertions.assertEquals(H3AltServicePublisher.RESPONSE_CONTENT, response.body(), "Unexpected response body"); final Optional altSvcHeader = response.headers().firstValue("alt-svc"); - Assert.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); + Assertions.assertTrue(altSvcHeader.isPresent(), "alt-svc header is missing in response"); System.out.println("Received alt-svc header value: " + altSvcHeader.get()); final String expectedHeader = "h3=\"" + toHostPort(altServer) + "\""; - Assert.assertTrue(altSvcHeader.get().contains(expectedHeader), + Assertions.assertTrue(altSvcHeader.get().contains(expectedHeader), "Unexpected alt-svc header value: " + altSvcHeader.get() + ", was expected to contain: " + expectedHeader); @@ -439,8 +440,8 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Again issuing request " + reqURI); final HttpResponse secondResponse = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(secondResponse.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(secondResponse.body(), All200OKHandler.RESPONSE_CONTENT, + Assertions.assertEquals(200, secondResponse.statusCode(), "Unexpected response code"); + Assertions.assertEquals(All200OKHandler.RESPONSE_CONTENT, secondResponse.body(), "Unexpected response body"); // wait for alt-service to expire @@ -452,8 +453,8 @@ public class AltServiceUsageTest implements HttpServerAdapters { System.out.println("Issuing request for a third time " + reqURI); final HttpResponse thirdResponse = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(thirdResponse.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(thirdResponse.body(), H3AltServicePublisher.RESPONSE_CONTENT, + Assertions.assertEquals(200, thirdResponse.statusCode(), "Unexpected response code"); + Assertions.assertEquals(H3AltServicePublisher.RESPONSE_CONTENT, thirdResponse.body(), "Unexpected response body"); var TRACKER = ReferenceTracker.INSTANCE; var tracker = TRACKER.getTracker(client); diff --git a/test/jdk/java/net/httpclient/AsFileDownloadTest.java b/test/jdk/java/net/httpclient/AsFileDownloadTest.java index 88bf95dab02..6e9c4e676cf 100644 --- a/test/jdk/java/net/httpclient/AsFileDownloadTest.java +++ b/test/jdk/java/net/httpclient/AsFileDownloadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -63,19 +63,20 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.net.http.HttpResponse.BodyHandlers.ofFileDownload; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -86,24 +87,24 @@ import static org.testng.Assert.fail; * jdk.test.lib.Platform jdk.test.lib.util.FileUtils * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.common.TestServerConfigurator - * @run testng/othervm/timeout=480 AsFileDownloadTest + * @run junit/othervm/timeout=480 AsFileDownloadTest */ public class AsFileDownloadTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - Http2TestServer http2TestServer; // HTTP/2 ( h2c ) - Http2TestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer h3TestServer; // HTTP/3 - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String h3URI; + private static HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpsServer httpsTestServer; // HTTPS/1.1 + private static Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + private static Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer h3TestServer; // HTTP/3 + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String h3URI; final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - Path tempDir; + static Path tempDir; static final String[][] contentDispositionValues = new String[][] { // URI query Content-Type header value Expected filename @@ -151,8 +152,7 @@ public class AsFileDownloadTest { { "041", "attachment; filename=\"foo/../../file12.txt\"", "file12.txt" }, }; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { List list = new ArrayList<>(); Arrays.asList(contentDispositionValues).stream() @@ -181,7 +181,8 @@ public class AsFileDownloadTest { return builder.sslContext(sslContext).proxy(Builder.NO_PROXY).build(); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString, String contentDispositionValue, String expectedFilename, Optional requestVersion) throws Exception { out.printf("test(%s, %s, %s): starting", uriString, contentDispositionValue, expectedFilename); @@ -207,14 +208,13 @@ public class AsFileDownloadTest { String fileContents = new String(Files.readAllBytes(response.body()), UTF_8); out.println("Got body: " + fileContents); - assertEquals(response.statusCode(), 200); - assertEquals(body.getFileName().toString(), expectedFilename); + assertEquals(200, response.statusCode()); + assertEquals(expectedFilename, body.getFileName().toString()); assertTrue(response.headers().firstValue("Content-Disposition").isPresent()); - assertEquals(response.headers().firstValue("Content-Disposition").get(), - contentDispositionValue); - assertEquals(fileContents, "May the luck of the Irish be with you!"); + assertEquals( contentDispositionValue, response.headers().firstValue("Content-Disposition").get()); + assertEquals("May the luck of the Irish be with you!", fileContents); if (requestVersion.isPresent()) { - assertEquals(response.version(), requestVersion.get(), "unexpected HTTP version" + + assertEquals(requestVersion.get(), response.version(), "unexpected HTTP version" + " in response"); } @@ -254,8 +254,7 @@ public class AsFileDownloadTest { }; - @DataProvider(name = "negative") - public Object[][] negative() { + public static Object[][] negative() { List list = new ArrayList<>(); Arrays.asList(contentDispositionBADValues).stream() @@ -276,7 +275,8 @@ public class AsFileDownloadTest { return list.stream().toArray(Object[][]::new); } - @Test(dataProvider = "negative") + @ParameterizedTest + @MethodSource("negative") void negativeTest(String uriString, String contentDispositionValue, Optional requestVersion) throws Exception { out.printf("negativeTest(%s, %s): starting", uriString, contentDispositionValue); @@ -330,8 +330,8 @@ public class AsFileDownloadTest { return builder; } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { tempDir = Paths.get("asFileDownloadTest.tmp.dir"); if (Files.exists(tempDir)) throw new AssertionError("Unexpected test work dir existence: " + tempDir.toString()); @@ -380,8 +380,8 @@ public class AsFileDownloadTest { h3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(0); httpsTestServer.stop(0); http2TestServer.stop(); @@ -474,11 +474,11 @@ public class AsFileDownloadTest { for (String name : List.of(headerName.toUpperCase(Locale.ROOT), headerName.toLowerCase(Locale.ROOT))) { assertTrue(headers.firstValue(name).isPresent()); - assertEquals(headers.firstValue(name).get(), headerValue.get(0)); - assertEquals(headers.allValues(name).size(), headerValue.size()); - assertEquals(headers.allValues(name), headerValue); - assertEquals(headers.map().get(name).size(), headerValue.size()); - assertEquals(headers.map().get(name), headerValue); + assertEquals(headerValue.get(0), headers.firstValue(name).get()); + assertEquals(headerValue.size(), headers.allValues(name).size()); + assertEquals(headerValue, headers.allValues(name)); + assertEquals(headerValue.size(), headers.map().get(name).size()); + assertEquals(headerValue, headers.map().get(name)); } } } catch (Throwable t) { diff --git a/test/jdk/java/net/httpclient/AsyncExecutorShutdown.java b/test/jdk/java/net/httpclient/AsyncExecutorShutdown.java index 14ed4f63124..308288bb9bf 100644 --- a/test/jdk/java/net/httpclient/AsyncExecutorShutdown.java +++ b/test/jdk/java/net/httpclient/AsyncExecutorShutdown.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * AsyncExecutorShutdown @@ -68,10 +68,6 @@ import javax.net.ssl.SSLContext; import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; @@ -81,9 +77,14 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class AsyncExecutorShutdown implements HttpServerAdapters { @@ -92,27 +93,26 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { } static final Random RANDOM = RandomFactory.getRandom(); - ExecutorService readerService; + private static ExecutorService readerService; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 6 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer h2h3TestServer; // HTTP/2 ( h2+h3 ) - HttpTestServer h3TestServer; // HTTP/2 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String h2h3URI; - String h3URI; - String h2h3Head; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 6 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer h2h3TestServer; // HTTP/2 ( h2+h3 ) + private static HttpTestServer h3TestServer; // HTTP/2 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String h2h3URI; + private static String h3URI; + private static String h2h3Head; static final String MESSAGE = "AsyncExecutorShutdown message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { h2h3URI, HTTP_3, h2h3TestServer.h3DiscoveryConfig() }, { h3URI, HTTP_3, h3TestServer.h3DiscoveryConfig() }, @@ -124,7 +124,7 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { } static final AtomicLong requestCounter = new AtomicLong(); - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; static Throwable getCause(Throwable t) { while (t instanceof CompletionException || t instanceof ExecutionException) { @@ -165,7 +165,8 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { throw new AssertionError(what + ": Unexpected exception: " + cause, cause); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testConcurrent(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting (%s, %s, %s) ----%n%n", uriString, version, config); ExecutorService executorService = Executors.newCachedThreadPool(); @@ -206,14 +207,14 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { responseCF = client.sendAsync(request, BodyHandlers.ofInputStream()) .thenApply((response) -> { out.println(si + ": Got response: " + response); - assertEquals(response.statusCode(), 200); - if (si >= head) assertEquals(response.version(), version); + assertEquals(200, response.statusCode()); + if (si >= head) assertEquals(version, response.version()); return response; }); bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) .thenApply(AsyncExecutorShutdown::readBody) .thenApply((s) -> { - assertEquals(s, MESSAGE); + assertEquals(MESSAGE, s); return s; }); } catch (RejectedExecutionException x) { @@ -275,7 +276,8 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { CompletableFuture.allOf(bodies.toArray(new CompletableFuture[0])).get(); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testSequential(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting (%s, %s, %s) ----%n%n", uriString, version, config); ExecutorService executorService = Executors.newCachedThreadPool(); @@ -316,13 +318,13 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { responseCF = client.sendAsync(request, BodyHandlers.ofInputStream()) .thenApply((response) -> { out.println(si + ": Got response: " + response); - assertEquals(response.statusCode(), 200); - if (si > 0) assertEquals(response.version(), version); + assertEquals(200, response.statusCode()); + if (si > 0) assertEquals(version, response.version()); return response; }); bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) .thenApply(AsyncExecutorShutdown::readBody) - .thenApply((s) -> {assertEquals(s, MESSAGE); return s;}) + .thenApply((s) -> {assertEquals(MESSAGE, s); return s;}) .thenApply((s) -> {out.println(si + ": Got body: " + s); return s;}); } catch (RejectedExecutionException x) { out.println(i + ": Got expected exception: " + x); @@ -397,7 +399,7 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { .HEAD() .build(); var resp = client.send(request, BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } static void shutdown(ExecutorService executorService) { @@ -409,8 +411,8 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println("\n**** Setup ****\n"); readerService = Executors.newCachedThreadPool(); @@ -445,8 +447,8 @@ public class AsyncExecutorShutdown implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.checkShutdown(5000); try { diff --git a/test/jdk/java/net/httpclient/AsyncShutdownNow.java b/test/jdk/java/net/httpclient/AsyncShutdownNow.java index 56b0378cf66..7dc13cf08e2 100644 --- a/test/jdk/java/net/httpclient/AsyncShutdownNow.java +++ b/test/jdk/java/net/httpclient/AsyncShutdownNow.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 @@ -31,7 +31,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * AsyncShutdownNow @@ -70,10 +70,6 @@ import javax.net.ssl.SSLContext; import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; @@ -84,10 +80,15 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class AsyncShutdownNow implements HttpServerAdapters { @@ -96,27 +97,26 @@ public class AsyncShutdownNow implements HttpServerAdapters { } static final Random RANDOM = RandomFactory.getRandom(); - ExecutorService readerService; + private static ExecutorService readerService; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) - HttpTestServer h3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String h2h3URI; - String h2h3Head; - String h3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) + private static HttpTestServer h3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String h2h3URI; + private static String h2h3Head; + private static String h3URI; static final String MESSAGE = "AsyncShutdownNow message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { h2h3URI, HTTP_3, h2h3TestServer.h3DiscoveryConfig()}, { h3URI, HTTP_3, h3TestServer.h3DiscoveryConfig()}, @@ -168,11 +168,11 @@ public class AsyncShutdownNow implements HttpServerAdapters { out.println(step + ": Got response: " + response); out.printf("%s: expect status 200 and version %s (%s) for %s%n", step, version, config, response.request().uri()); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (step == 0 && version == HTTP_3 && firstVersionMayNotMatch) { out.printf("%s: version not checked%n", step); } else { - assertEquals(response.version(), version); + assertEquals(version, response.version()); out.printf("%s: got expected version %s%n", step, response.version()); } return this; @@ -185,7 +185,7 @@ public class AsyncShutdownNow implements HttpServerAdapters { .HEAD() .build(); var resp = client.send(request, BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } static boolean hasExpectedMessage(IOException io) { @@ -223,7 +223,8 @@ public class AsyncShutdownNow implements HttpServerAdapters { throw new AssertionError(what + ": Unexpected exception: " + cause, cause); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testConcurrent(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting concurrent (%s, %s, %s) ----%n%n", uriString, version, config); HttpClient client = newClientBuilderForH3() @@ -260,7 +261,7 @@ public class AsyncShutdownNow implements HttpServerAdapters { bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) .thenApply(AsyncShutdownNow::readBody) .thenApply((s) -> { - assertEquals(s, MESSAGE); + assertEquals(MESSAGE, s); return s; }); long sleep = RANDOM.nextLong(5); @@ -320,7 +321,8 @@ public class AsyncShutdownNow implements HttpServerAdapters { return failed; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testSequential(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting sequential (%s, %s, %s) ----%n%n", uriString, version, config); @@ -354,7 +356,7 @@ public class AsyncShutdownNow implements HttpServerAdapters { bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) .thenApply(AsyncShutdownNow::readBody) .thenApply((s) -> { - assertEquals(s, MESSAGE); + assertEquals(MESSAGE, s); return s; }) .thenApply((s) -> { @@ -403,8 +405,8 @@ public class AsyncShutdownNow implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println("\n**** Setup ****\n"); readerService = Executors.newCachedThreadPool(); @@ -439,8 +441,8 @@ public class AsyncShutdownNow implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.checkShutdown(5000); try { diff --git a/test/jdk/java/net/httpclient/AuthFilterCacheTest.java b/test/jdk/java/net/httpclient/AuthFilterCacheTest.java index 6d64caa1950..0d87055ff51 100644 --- a/test/jdk/java/net/httpclient/AuthFilterCacheTest.java +++ b/test/jdk/java/net/httpclient/AuthFilterCacheTest.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 @@ -40,10 +40,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import com.sun.net.httpserver.HttpsServer; import jdk.httpclient.test.lib.common.TestServerConfigurator; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; @@ -54,7 +50,12 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -63,7 +64,7 @@ import static org.testng.Assert.*; * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext * DigestEchoServer ReferenceTracker jdk.httpclient.test.lib.common.TestServerConfigurator - * @run testng/othervm -Dtest.requiresHost=true + * @run junit/othervm -Dtest.requiresHost=true * -Djdk.httpclient.HttpClient.log=requests,headers,errors,quic * -Djdk.internal.httpclient.debug=false * AuthFilterCacheTest @@ -81,29 +82,28 @@ public class AuthFilterCacheTest implements HttpServerAdapters { SSLContext.setDefault(context); } - HttpTestServer http1Server; - HttpTestServer http2Server; - HttpTestServer https1Server; - HttpTestServer https2Server; - HttpTestServer h3onlyServer; - HttpTestServer h3altSvcServer; - DigestEchoServer.TunnelingProxy proxy; - URI http1URI; - URI https1URI; - URI http2URI; - URI https2URI; - URI h3onlyURI; - URI h3altSvcURI; - InetSocketAddress proxyAddress; - ProxySelector proxySelector; - MyAuthenticator auth; - HttpClient client; - ExecutorService serverExecutor = Executors.newCachedThreadPool(); - ExecutorService virtualExecutor = Executors.newThreadPerTaskExecutor(Thread.ofVirtual() + private static HttpTestServer http1Server; + private static HttpTestServer http2Server; + private static HttpTestServer https1Server; + private static HttpTestServer https2Server; + private static HttpTestServer h3onlyServer; + private static HttpTestServer h3altSvcServer; + private static DigestEchoServer.TunnelingProxy proxy; + private static URI http1URI; + private static URI https1URI; + private static URI http2URI; + private static URI https2URI; + private static URI h3onlyURI; + private static URI h3altSvcURI; + private static InetSocketAddress proxyAddress; + private static ProxySelector proxySelector; + private static MyAuthenticator auth; + private static HttpClient client; + private static ExecutorService serverExecutor = Executors.newCachedThreadPool(); + private static ExecutorService virtualExecutor = Executors.newThreadPerTaskExecutor(Thread.ofVirtual() .name("HttpClient-Worker", 0).factory()); - @DataProvider(name = "uris") - Object[][] testURIs() { + static Object[][] testURIs() { Object[][] uris = new Object[][]{ {List.of(http1URI.resolve("direct/orig/"), https1URI.resolve("direct/orig/"), @@ -117,8 +117,8 @@ public class AuthFilterCacheTest implements HttpServerAdapters { return uris; } - public HttpClient newHttpClient(ProxySelector ps, Authenticator auth) { - HttpClient.Builder builder = newClientBuilderForH3() + public static HttpClient newHttpClient(ProxySelector ps, Authenticator auth) { + HttpClient.Builder builder = HttpServerAdapters.createClientBuilderForH3() .executor(virtualExecutor) .sslContext(context) .authenticator(auth) @@ -126,8 +126,8 @@ public class AuthFilterCacheTest implements HttpServerAdapters { return builder.build(); } - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { try { InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); @@ -189,8 +189,8 @@ public class AuthFilterCacheTest implements HttpServerAdapters { .version(HTTP_2).build(); System.out.println("Sending head request: " + headRequest); var headResponse = client.send(headRequest, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(HTTP_2, headResponse.version()); System.out.println("Setup: done"); } catch (Exception x) { @@ -202,8 +202,8 @@ public class AuthFilterCacheTest implements HttpServerAdapters { } } - @AfterClass - public void tearDown() { + @AfterAll + public static void tearDown() { proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop); http1Server = stop(http1Server, HttpTestServer::stop); https1Server = stop(https1Server, HttpTestServer::stop); @@ -378,7 +378,8 @@ public class AuthFilterCacheTest implements HttpServerAdapters { } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("testURIs") public void test(List uris) throws Exception { System.out.println("Servers listening at " + uris.stream().map(URI::toString) diff --git a/test/jdk/java/net/httpclient/BasicRedirectTest.java b/test/jdk/java/net/httpclient/BasicRedirectTest.java index d79c39fe47a..d873a64bb1b 100644 --- a/test/jdk/java/net/httpclient/BasicRedirectTest.java +++ b/test/jdk/java/net/httpclient/BasicRedirectTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ * @summary Basic test for redirect and redirect policies * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=trace,headers,requests * -Djdk.internal.httpclient.debug=true * BasicRedirectTest @@ -49,10 +49,6 @@ import java.util.Optional; import java.util.stream.Collectors; import javax.net.ssl.SSLContext; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; @@ -61,39 +57,42 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class BasicRedirectTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpURIToMoreSecure; // redirects HTTP to HTTPS - String httpURIToH3MoreSecure; // redirects HTTP to HTTPS/3 - String httpsURI; - String httpsURIToLessSecure; // redirects HTTPS to HTTP - String http2URI; - String http2URIToMoreSecure; // redirects HTTP to HTTPS - String http2URIToH3MoreSecure; // redirects HTTP to HTTPS/3 - String https2URI; - String https2URIToLessSecure; // redirects HTTPS to HTTP - String https3URI; - String https3HeadURI; - String http3URIToLessSecure; // redirects HTTP3 to HTTP - String http3URIToH2cLessSecure; // redirects HTTP3 to h2c + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpURIToMoreSecure; // redirects HTTP to HTTPS + private static String httpURIToH3MoreSecure; // redirects HTTP to HTTPS/3 + private static String httpsURI; + private static String httpsURIToLessSecure; // redirects HTTPS to HTTP + private static String http2URI; + private static String http2URIToMoreSecure; // redirects HTTP to HTTPS + private static String http2URIToH3MoreSecure; // redirects HTTP to HTTPS/3 + private static String https2URI; + private static String https2URIToLessSecure; // redirects HTTPS to HTTP + private static String https3URI; + private static String https3HeadURI; + private static String http3URIToLessSecure; // redirects HTTP3 to HTTP + private static String http3URIToH2cLessSecure; // redirects HTTP3 to h2c static final String MESSAGE = "Is fearr Gaeilge briste, na Bearla cliste"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { httpURI, Redirect.ALWAYS, Optional.empty() }, { httpsURI, Redirect.ALWAYS, Optional.empty() }, @@ -121,8 +120,8 @@ public class BasicRedirectTest implements HttpServerAdapters { }; } - HttpClient createClient(Redirect redirectPolicy, Optional version) throws Exception { - var clientBuilder = newClientBuilderForH3() + static HttpClient createClient(Redirect redirectPolicy, Optional version) throws Exception { + var clientBuilder = HttpServerAdapters.createClientBuilderForH3() .followRedirects(redirectPolicy) .sslContext(sslContext); HttpClient client = version.map(clientBuilder::version) @@ -135,23 +134,24 @@ public class BasicRedirectTest implements HttpServerAdapters { var get = builder.copy().GET().build(); out.printf("%n---- sending initial head request (%s) -----%n", head.uri()); var resp = client.send(head, BodyHandlers.ofString()); - assertEquals(resp.statusCode(), 200); - assertEquals(resp.version(), HTTP_2); + assertEquals(200, resp.statusCode()); + assertEquals(HTTP_2, resp.version()); out.println("HEADERS: " + resp.headers()); var length = resp.headers().firstValueAsLong("Content-Length") .orElseThrow(AssertionError::new); if (length < 0) throw new AssertionError("negative length " + length); out.printf("%n---- sending initial HTTP/3 GET request (%s) -----%n", get.uri()); resp = client.send(get, BodyHandlers.ofString()); - assertEquals(resp.statusCode(), 200); - assertEquals(resp.version(), HTTP_3); - assertEquals(resp.body().getBytes(UTF_8).length, length, + assertEquals(200, resp.statusCode()); + assertEquals(HTTP_3, resp.version()); + assertEquals(length, resp.body().getBytes(UTF_8).length, "body \"" + resp.body() + "\": "); } return client; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString, Redirect redirectPolicy, Optional clientVersion) throws Exception { out.printf("%n---- starting positive (%s, %s, %s) ----%n", uriString, redirectPolicy, clientVersion.map(Version::name).orElse("empty")); @@ -169,8 +169,8 @@ public class BasicRedirectTest implements HttpServerAdapters { out.println(" Got body Path: " + response.body()); out.println(" Got response.request: " + response.request()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MESSAGE); + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, response.body()); // asserts redirected URI in response.request().uri() assertTrue(response.uri().getPath().endsWith("message")); assertPreviousRedirectResponses(request, response, clientVersion); @@ -193,7 +193,7 @@ public class BasicRedirectTest implements HttpServerAdapters { versions.add(response.version()); assertTrue(300 <= response.statusCode() && response.statusCode() <= 309, "Expected 300 <= code <= 309, got:" + response.statusCode()); - assertEquals(response.body(), null, "Unexpected body: " + response.body()); + assertEquals(null, response.body(), "Unexpected body: " + response.body()); String locationHeader = response.headers().firstValue("Location") .orElseThrow(() -> new RuntimeException("no previous Location")); assertTrue(uri.toString().endsWith(locationHeader), @@ -202,7 +202,7 @@ public class BasicRedirectTest implements HttpServerAdapters { } while (response.previousResponse().isPresent()); // initial - assertEquals(initialRequest, response.request(), + assertEquals(response.request(), initialRequest, String.format("Expected initial request [%s] to equal last prev req [%s]", initialRequest, response.request())); if (clientVersion.stream().anyMatch(HTTP_3::equals)) { @@ -214,8 +214,7 @@ public class BasicRedirectTest implements HttpServerAdapters { // -- negatives - @DataProvider(name = "negative") - public Object[][] negative() { + public static Object[][] negative() { return new Object[][] { { httpURI, Redirect.NEVER, Optional.empty() }, { httpsURI, Redirect.NEVER, Optional.empty() }, @@ -238,7 +237,8 @@ public class BasicRedirectTest implements HttpServerAdapters { }; } - @Test(dataProvider = "negative") + @ParameterizedTest + @MethodSource("negative") void testNegatives(String uriString, Redirect redirectPolicy, Optional clientVersion) throws Exception { out.printf("%n---- starting negative (%s, %s, %s) ----%n", uriString, redirectPolicy, @@ -257,8 +257,8 @@ public class BasicRedirectTest implements HttpServerAdapters { out.println(" Got body Path: " + response.body()); out.println(" Got response.request: " + response.request()); - assertEquals(response.statusCode(), 302); - assertEquals(response.body(), "XY"); + assertEquals(302, response.statusCode()); + assertEquals("XY", response.body()); // asserts original URI in response.request().uri() assertTrue(response.uri().equals(uri)); assertFalse(response.previousResponse().isPresent()); @@ -268,8 +268,8 @@ public class BasicRedirectTest implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new BasicHttpRedirectHandler(), "/http1/same/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/same/redirect"; @@ -325,8 +325,8 @@ public class BasicRedirectTest implements HttpServerAdapters { createClient(Redirect.NEVER, Optional.of(HTTP_3)); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/BodySubscribersTest.java b/test/jdk/java/net/httpclient/BodySubscribersTest.java index 21aa4ce3b17..abd86693b83 100644 --- a/test/jdk/java/net/httpclient/BodySubscribersTest.java +++ b/test/jdk/java/net/httpclient/BodySubscribersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, 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 @@ -25,7 +25,7 @@ * @test * @summary Basic test for the standard BodySubscribers default behavior * @bug 8225583 8334028 - * @run testng BodySubscribersTest + * @run junit BodySubscribersTest */ import java.net.http.HttpResponse.BodySubscriber; @@ -34,16 +34,18 @@ import java.nio.file.Path; import java.util.List; import java.util.concurrent.Flow; import java.util.function.Supplier; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpResponse.BodySubscribers.*; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardOpenOption.CREATE; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.expectThrows; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class BodySubscribersTest { @@ -78,8 +80,7 @@ public class BodySubscribersTest { @Override public void onComplete() { fail(); } } - @DataProvider(name = "bodySubscriberSuppliers") - public Object[][] bodySubscriberSuppliers() { ; + public static Object[][] bodySubscriberSuppliers() { ; List>> list = List.of( BSSupplier.create("ofByteArray", () -> ofByteArray()), BSSupplier.create("ofInputStream", () -> ofInputStream()), @@ -102,7 +103,8 @@ public class BodySubscribersTest { return list.stream().map(x -> new Object[] { x }).toArray(Object[][]::new); } - @Test(dataProvider = "bodySubscriberSuppliers") + @ParameterizedTest + @MethodSource("bodySubscriberSuppliers") void nulls(Supplier> bodySubscriberSupplier) { BodySubscriber bodySubscriber = bodySubscriberSupplier.get(); boolean subscribed = false; @@ -111,18 +113,18 @@ public class BodySubscribersTest { assertNotNull(bodySubscriber.getBody()); assertNotNull(bodySubscriber.getBody()); assertNotNull(bodySubscriber.getBody()); - expectThrows(NPE, () -> bodySubscriber.onSubscribe(null)); - expectThrows(NPE, () -> bodySubscriber.onSubscribe(null)); - expectThrows(NPE, () -> bodySubscriber.onSubscribe(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onSubscribe(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onSubscribe(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onSubscribe(null)); - expectThrows(NPE, () -> bodySubscriber.onNext(null)); - expectThrows(NPE, () -> bodySubscriber.onNext(null)); - expectThrows(NPE, () -> bodySubscriber.onNext(null)); - expectThrows(NPE, () -> bodySubscriber.onNext(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onNext(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onNext(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onNext(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onNext(null)); - expectThrows(NPE, () -> bodySubscriber.onError(null)); - expectThrows(NPE, () -> bodySubscriber.onError(null)); - expectThrows(NPE, () -> bodySubscriber.onError(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onError(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onError(null)); + Assertions.assertThrows(NPE, () -> bodySubscriber.onError(null)); if (!subscribed) { out.println("subscribing"); @@ -138,7 +140,8 @@ public class BodySubscribersTest { } while (true); } - @Test(dataProvider = "bodySubscriberSuppliers") + @ParameterizedTest + @MethodSource("bodySubscriberSuppliers") void subscribeMoreThanOnce(Supplier> bodySubscriberSupplier) { BodySubscriber bodySubscriber = bodySubscriberSupplier.get(); bodySubscriber.onSubscribe(new Flow.Subscription() { diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java index a4c5bab55dc..7813b8c3d7b 100644 --- a/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java +++ b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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,33 +32,34 @@ import java.util.concurrent.SubmissionPublisher; import java.util.function.IntSupplier; import java.util.stream.IntStream; import java.net.http.HttpResponse.BodySubscriber; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.Long.MAX_VALUE; import static java.lang.Long.MIN_VALUE; import static java.lang.System.out; import static java.nio.ByteBuffer.wrap; import static java.util.concurrent.TimeUnit.SECONDS; import static java.net.http.HttpResponse.BodySubscribers.buffering; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test * @summary Direct test for HttpResponse.BodySubscriber.buffering() cancellation - * @run testng/othervm BufferingSubscriberCancelTest + * @run junit/othervm BufferingSubscriberCancelTest */ public class BufferingSubscriberCancelTest { - @DataProvider(name = "bufferSizes") - public Object[][] bufferSizes() { + public static Object[][] bufferSizes() { return new Object[][]{ // bufferSize should be irrelevant {1}, {100}, {511}, {512}, {513}, {1024}, {2047}, {2048} }; } - @Test(dataProvider = "bufferSizes") + @ParameterizedTest + @MethodSource("bufferSizes") public void cancelWithoutAnyItemsPublished(int bufferSize) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(1); SubmissionPublisher> publisher = @@ -85,8 +86,7 @@ public class BufferingSubscriberCancelTest { executor.shutdown(); } - @DataProvider(name = "sizeAndItems") - public Object[][] sizeAndItems() { + public static Object[][] sizeAndItems() { return new Object[][] { // bufferSize and item bytes must be equal to count onNext calls // bufferSize items @@ -103,7 +103,8 @@ public class BufferingSubscriberCancelTest { }; } - @Test(dataProvider = "sizeAndItems") + @ParameterizedTest + @MethodSource("sizeAndItems") public void cancelWithItemsPublished(int bufferSize, List items) throws Exception { @@ -125,12 +126,13 @@ public class BufferingSubscriberCancelTest { IntStream.range(0, ITERATION_TIMES+1).forEach(x -> publisher.submit(items)); assertEqualsWithRetry(publisher::getNumberOfSubscribers, 0); - assertEquals(exposingSubscriber.onNextInvocations, ITERATION_TIMES); + assertEquals(ITERATION_TIMES, exposingSubscriber.onNextInvocations); executor.shutdown(); } // same as above but with more racy conditions, do not wait on the gate - @Test(dataProvider = "sizeAndItems") + @ParameterizedTest + @MethodSource("sizeAndItems") public void cancelWithItemsPublishedNoWait(int bufferSize, List items) throws Exception { @@ -212,6 +214,6 @@ public class BufferingSubscriberCancelTest { return; Thread.sleep(100); } - assertEquals(actual, expected); // will fail with the usual testng message + assertEquals(expected, actual); // will fail with the usual testng message } } diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java index 5292f4b9860..255946232be 100644 --- a/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java +++ b/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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,30 +32,31 @@ import java.util.concurrent.Phaser; import java.util.concurrent.SubmissionPublisher; import java.util.stream.IntStream; import java.net.http.HttpResponse.BodySubscriber; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.Long.MAX_VALUE; import static java.lang.Long.MIN_VALUE; import static java.nio.ByteBuffer.wrap; import static java.net.http.HttpResponse.BodySubscribers.buffering; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test * @summary Test for HttpResponse.BodySubscriber.buffering() onError/onComplete - * @run testng/othervm BufferingSubscriberErrorCompleteTest + * @run junit/othervm BufferingSubscriberErrorCompleteTest */ public class BufferingSubscriberErrorCompleteTest { - @DataProvider(name = "illegalDemand") - public Object[][] illegalDemand() { + public static Object[][] illegalDemand() { return new Object[][]{ {0L}, {-1L}, {-5L}, {-100L}, {-101L}, {-100_001L}, {MIN_VALUE} }; } - @Test(dataProvider = "illegalDemand") + @ParameterizedTest + @MethodSource("illegalDemand") public void illegalRequest(long demand) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(1); SubmissionPublisher> publisher = @@ -72,18 +73,17 @@ public class BufferingSubscriberErrorCompleteTest { s.request(demand); gate.arriveAndAwaitAdvance(); - assertEquals(previous + 1, exposingSubscriber.onErrorInvocations); + assertEquals(exposingSubscriber.onErrorInvocations, previous + 1); assertTrue(exposingSubscriber.throwable instanceof IllegalArgumentException, "Expected IAE, got:" + exposingSubscriber.throwable); furtherCancelsRequestsShouldBeNoOp(s); - assertEquals(exposingSubscriber.onErrorInvocations, 1); + assertEquals(1, exposingSubscriber.onErrorInvocations); executor.shutdown(); } - @DataProvider(name = "bufferAndItemSizes") - public Object[][] bufferAndItemSizes() { + public static Object[][] bufferAndItemSizes() { List values = new ArrayList<>(); for (int bufferSize : new int[] { 1, 5, 10, 100, 1000 }) @@ -93,7 +93,8 @@ public class BufferingSubscriberErrorCompleteTest { return values.stream().toArray(Object[][]::new); } - @Test(dataProvider = "bufferAndItemSizes") + @ParameterizedTest + @MethodSource("bufferAndItemSizes") public void onErrorFromPublisher(int bufferSize, int numberOfItems) throws Exception @@ -117,19 +118,19 @@ public class BufferingSubscriberErrorCompleteTest { Subscription s = exposingSubscriber.subscription; - assertEquals(exposingSubscriber.onErrorInvocations, 1); - assertEquals(exposingSubscriber.onCompleteInvocations, 0); - assertEquals(exposingSubscriber.throwable, t); - assertEquals(exposingSubscriber.throwable.getMessage(), - "a message from me to me"); + assertEquals(1, exposingSubscriber.onErrorInvocations); + assertEquals(0, exposingSubscriber.onCompleteInvocations); + assertEquals(t, exposingSubscriber.throwable); + assertEquals("a message from me to me", exposingSubscriber.throwable.getMessage()); furtherCancelsRequestsShouldBeNoOp(s); - assertEquals(exposingSubscriber.onErrorInvocations, 1); - assertEquals(exposingSubscriber.onCompleteInvocations, 0); + assertEquals(1, exposingSubscriber.onErrorInvocations); + assertEquals(0, exposingSubscriber.onCompleteInvocations); executor.shutdown(); } - @Test(dataProvider = "bufferAndItemSizes") + @ParameterizedTest + @MethodSource("bufferAndItemSizes") public void onCompleteFromPublisher(int bufferSize, int numberOfItems) throws Exception @@ -152,14 +153,14 @@ public class BufferingSubscriberErrorCompleteTest { Subscription s = exposingSubscriber.subscription; - assertEquals(exposingSubscriber.onErrorInvocations, 0); - assertEquals(exposingSubscriber.onCompleteInvocations, 1); - assertEquals(exposingSubscriber.throwable, null); + assertEquals(0, exposingSubscriber.onErrorInvocations); + assertEquals(1, exposingSubscriber.onCompleteInvocations); + assertEquals(null, exposingSubscriber.throwable); furtherCancelsRequestsShouldBeNoOp(s); - assertEquals(exposingSubscriber.onErrorInvocations, 0); - assertEquals(exposingSubscriber.onCompleteInvocations, 1); - assertEquals(exposingSubscriber.throwable, null); + assertEquals(0, exposingSubscriber.onErrorInvocations); + assertEquals(1, exposingSubscriber.onCompleteInvocations); + assertEquals(null, exposingSubscriber.throwable); executor.shutdown(); } diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberTest.java index 6d15ba5d3a8..bb33b18ed9c 100644 --- a/test/jdk/java/net/httpclient/BufferingSubscriberTest.java +++ b/test/jdk/java/net/httpclient/BufferingSubscriberTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -38,14 +38,15 @@ import java.net.http.HttpResponse.BodyHandlers; import java.net.http.HttpResponse.BodySubscriber; import java.net.http.HttpResponse.BodySubscribers; import jdk.test.lib.RandomFactory; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.Long.MAX_VALUE; -import static java.lang.Long.min; import static java.lang.System.out; import static java.util.concurrent.CompletableFuture.delayedExecutor; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -54,7 +55,7 @@ import static org.testng.Assert.*; * @key randomness * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=480 -Djdk.internal.httpclient.debug=true BufferingSubscriberTest + * @run junit/othervm/timeout=480 -Djdk.internal.httpclient.debug=true BufferingSubscriberTest */ public class BufferingSubscriberTest { @@ -80,37 +81,41 @@ public class BufferingSubscriberTest { time = time + ms + "ms"; out.println(what + "\t ["+time+"]\t "+ String.format(fmt,args)); } - @DataProvider(name = "negatives") - public Object[][] negatives() { + public static Object[][] negatives() { return new Object[][] { { 0 }, { -1 }, { -1000 } }; } - @Test(dataProvider = "negatives", expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("negatives") public void subscriberThrowsIAE(int bufferSize) { - printStamp(START, "subscriberThrowsIAE(%d)", bufferSize); - try { - BodySubscriber bp = BodySubscribers.ofByteArray(); - BodySubscribers.buffering(bp, bufferSize); - } finally { - printStamp(END, "subscriberThrowsIAE(%d)", bufferSize); - } + Assertions.assertThrows(IllegalArgumentException.class, () -> { + printStamp(START, "subscriberThrowsIAE(%d)", bufferSize); + try { + BodySubscriber bp = BodySubscribers.ofByteArray(); + BodySubscribers.buffering(bp, bufferSize); + } finally { + printStamp(END, "subscriberThrowsIAE(%d)", bufferSize); + } + }); } - @Test(dataProvider = "negatives", expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("negatives") public void handlerThrowsIAE(int bufferSize) { - printStamp(START, "handlerThrowsIAE(%d)", bufferSize); - try { - BodyHandler bp = BodyHandlers.ofByteArray(); - BodyHandlers.buffering(bp, bufferSize); - } finally { - printStamp(END, "handlerThrowsIAE(%d)", bufferSize); - } + Assertions.assertThrows(IllegalArgumentException.class, () -> { + printStamp(START, "handlerThrowsIAE(%d)", bufferSize); + try { + BodyHandler bp = BodyHandlers.ofByteArray(); + BodyHandlers.buffering(bp, bufferSize); + } finally { + printStamp(END, "handlerThrowsIAE(%d)", bufferSize); + } + }); } // --- - @DataProvider(name = "config") - public Object[][] config() { + public static Object[][] config() { return new Object[][] { // iterations delayMillis numBuffers bufferSize maxBufferSize minBufferSize { 1, 0, 1, 1, 2, 1 }, @@ -129,7 +134,8 @@ public class BufferingSubscriberTest { }; } - @Test(dataProvider = "config") + @ParameterizedTest + @MethodSource("config") public void test(int iterations, int delayMillis, int numBuffers, @@ -282,8 +288,8 @@ public class BufferingSubscriberTest { } count++; onNextInvocations++; - assertNotEquals(sz, 0L, "Unexpected empty buffers"); - items.stream().forEach(b -> assertEquals(b.position(), 0)); + assertNotEquals(0L, sz, "Unexpected empty buffers"); + items.stream().forEach(b -> assertEquals(0, b.position())); assertFalse(noMoreOnNext); if (sz != bufferSize) { @@ -296,20 +302,20 @@ public class BufferingSubscriberTest { "Possibly received last buffer: sz=%d, accumulated=%d, total=%d", sz, totalBytesReceived, totalBytesReceived + sz); } else { - assertEquals(sz, bufferSize, "Expected to receive exactly bufferSize"); + assertEquals(bufferSize, sz, "Expected to receive exactly bufferSize"); } lastSeenSize = sz; // Ensure expected contents for (ByteBuffer b : items) { while (b.hasRemaining()) { - assertEquals(b.get(), (byte) (index % 100)); + assertEquals((byte) (index % 100), b.get()); index++; } } totalBytesReceived += sz; - assertEquals(totalBytesReceived, index); + assertEquals(index, totalBytesReceived); if (delayMillis > 0 && ((expectedTotalSize - totalBytesReceived) > bufferSize)) delayedExecutor.execute(this::requestMore); else diff --git a/test/jdk/java/net/httpclient/CancelledPartialResponseTest.java b/test/jdk/java/net/httpclient/CancelledPartialResponseTest.java index bb190cfc348..33df36100ad 100644 --- a/test/jdk/java/net/httpclient/CancelledPartialResponseTest.java +++ b/test/jdk/java/net/httpclient/CancelledPartialResponseTest.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,7 +28,7 @@ * @bug 8309118 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm/timeout=40 -Djdk.internal.httpclient.debug=false -Djdk.httpclient.HttpClient.log=trace,errors,headers + * @run junit/othervm/timeout=40 -Djdk.internal.httpclient.debug=false -Djdk.httpclient.HttpClient.log=trace,errors,headers * CancelledPartialResponseTest */ @@ -47,11 +47,6 @@ import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.frame.ResetFrame; import jdk.internal.net.http.http3.Http3Error; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.TestException; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; @@ -74,16 +69,22 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.fail; + public class CancelledPartialResponseTest { - Http2TestServer http2TestServer; + private static Http2TestServer http2TestServer; - HttpTestServer http3TestServer; + private static HttpTestServer http3TestServer; // "NoError" urls complete with an exception. "NoError" or "Error" here refers to the error code in the RST_STREAM frame // and not the outcome of the test. - URI warmup, h2PartialResponseResetNoError, h2PartialResponseResetError, h2FullResponseResetNoError, h2FullResponseResetError; - URI h3PartialResponseStopSending, h3FullResponseStopSending; + private static URI warmup, h2PartialResponseResetNoError, h2PartialResponseResetError, h2FullResponseResetNoError, h2FullResponseResetError; + private static URI h3PartialResponseStopSending, h3FullResponseStopSending; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); @@ -91,8 +92,7 @@ public class CancelledPartialResponseTest { static PrintStream out = System.out; // TODO: Investigate further if checking against HTTP/3 Full Response is necessary - @DataProvider(name = "testData") - public Object[][] testData() { + public static Object[][] testData() { return new Object[][] { { HTTP_2, h2PartialResponseResetNoError }, { HTTP_2, h2PartialResponseResetError }, // Checks RST_STREAM is processed if client sees no END_STREAM @@ -104,7 +104,8 @@ public class CancelledPartialResponseTest { } - @Test(dataProvider = "testData") + @ParameterizedTest + @MethodSource("testData") public void test(Version version, URI uri) { out.printf("\nTesting with Version: %s, URI: %s\n", version, uri.toASCIIString()); err.printf("\nTesting with Version: %s, URI: %s\n", version, uri.toASCIIString()); @@ -161,8 +162,8 @@ public class CancelledPartialResponseTest { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { http2TestServer = new Http2TestServer(false, 0); http3TestServer = HttpTestServer.create(Http3DiscoveryMode.HTTP_3_URI_ONLY, sslContext); @@ -187,8 +188,8 @@ public class CancelledPartialResponseTest { http3TestServer.start(); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { http2TestServer.stop(); } @@ -246,10 +247,10 @@ public class CancelledPartialResponseTest { switch (exchange.getRequestURI().getPath()) { case "/partialResponse/codeNoError" -> testExchange.addResetToOutputQ(ResetFrame.NO_ERROR); case "/partialResponse/codeError" -> testExchange.addResetToOutputQ(ResetFrame.PROTOCOL_ERROR); - default -> throw new TestException("Invalid Request Path"); + default -> fail("Invalid Request Path"); } } else { - throw new TestException("Wrong Exchange type used"); + fail("Wrong Exchange type used"); } } } @@ -268,10 +269,10 @@ public class CancelledPartialResponseTest { switch (exchange.getRequestURI().getPath()) { case "/fullResponse/codeNoError" -> testExchange.addResetToOutputQ(ResetFrame.NO_ERROR); case "/fullResponse/codeError" -> testExchange.addResetToOutputQ(ResetFrame.PROTOCOL_ERROR); - default -> throw new TestException("Invalid Request Path"); + default -> fail("Invalid Request Path"); } } else { - throw new TestException("Wrong Exchange type used"); + fail("Wrong Exchange type used"); } } } diff --git a/test/jdk/java/net/httpclient/CancelledResponse2.java b/test/jdk/java/net/httpclient/CancelledResponse2.java index 48223e84135..6604fadea20 100644 --- a/test/jdk/java/net/httpclient/CancelledResponse2.java +++ b/test/jdk/java/net/httpclient/CancelledResponse2.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 @@ -25,10 +25,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.common.OperationTrackers.Tracker; import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -57,15 +53,20 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * @compile ReferenceTracker.java - * @run testng/othervm -Djdk.internal.httpclient.debug=true CancelledResponse2 + * @run junit/othervm -Djdk.internal.httpclient.debug=true CancelledResponse2 */ // -Djdk.internal.httpclient.debug=true public class CancelledResponse2 implements HttpServerAdapters { @@ -74,17 +75,16 @@ public class CancelledResponse2 implements HttpServerAdapters { private static final Random RANDOM = RandomFactory.getRandom(); private static final int MAX_CLIENT_DELAY = 160; - HttpTestServer h2TestServer; - URI h2TestServerURI; - URI h2h3TestServerURI; - URI h2h3HeadTestServerURI; - URI h3TestServerURI; - HttpTestServer h2h3TestServer; - HttpTestServer h3TestServer; + private static HttpTestServer h2TestServer; + private static URI h2TestServerURI; + private static URI h2h3TestServerURI; + private static URI h2h3HeadTestServerURI; + private static URI h3TestServerURI; + private static HttpTestServer h2h3TestServer; + private static HttpTestServer h3TestServer; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - @DataProvider(name = "versions") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][]{ { HTTP_2, null, h2TestServerURI }, { HTTP_3, null, h2h3TestServerURI }, @@ -101,7 +101,8 @@ public class CancelledResponse2 implements HttpServerAdapters { out.println("Unexpected exception: " + x); } } - @Test(dataProvider = "versions") + @ParameterizedTest + @MethodSource("positive") public void test(Version version, Http3DiscoveryMode config, URI uri) throws Exception { for (int i = 0; i < 5; i++) { HttpClient httpClient = newClientBuilderForH3().sslContext(sslContext).version(version).build(); @@ -147,11 +148,11 @@ public class CancelledResponse2 implements HttpServerAdapters { .HEAD() .build(); var resp = client.send(request, HttpResponse.BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { h2TestServer = HttpTestServer.create(HTTP_2, sslContext); h2TestServer.addHandler(new CancelledResponseHandler(), "/h2"); h2TestServerURI = URI.create("https://" + h2TestServer.serverAuthority() + "/h2"); @@ -172,8 +173,8 @@ public class CancelledResponse2 implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { h2TestServer.stop(); h2h3TestServer.stop(); h3TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/ConcurrentResponses.java b/test/jdk/java/net/httpclient/ConcurrentResponses.java index c44452bf4d2..53c85b8e383 100644 --- a/test/jdk/java/net/httpclient/ConcurrentResponses.java +++ b/test/jdk/java/net/httpclient/ConcurrentResponses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.TestServerConfigurator - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * ConcurrentResponses */ @@ -75,29 +75,30 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.net.http.HttpResponse.BodyHandlers.discarding; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ConcurrentResponses { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - Http2TestServer http2TestServer; // HTTP/2 ( h2c ) - Http2TestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer https3TestServer; - String httpFixedURI, httpsFixedURI, httpChunkedURI, httpsChunkedURI; - String http2FixedURI, https2FixedURI, http2VariableURI, https2VariableURI; - String https3FixedURI, https3VariableURI; + private static HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpsServer httpsTestServer; // HTTPS/1.1 + private static Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + private static Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer https3TestServer; + private static String httpFixedURI, httpsFixedURI, httpChunkedURI, httpsChunkedURI; + private static String http2FixedURI, https2FixedURI, http2VariableURI, https2VariableURI; + private static String https3FixedURI, https3VariableURI; static final int CONCURRENT_REQUESTS = 13; static final AtomicInteger IDS = new AtomicInteger(); @@ -131,7 +132,7 @@ public class ConcurrentResponses { */ static final CompletionStage> assert200ResponseCode(HttpResponse response) { - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); return CompletableFuture.completedFuture(response); } @@ -141,12 +142,11 @@ public class ConcurrentResponses { */ static final CompletionStage> assertbody(HttpResponse response, T body) { - assertEquals(response.body(), body); + assertEquals(body, response.body()); return CompletableFuture.completedFuture(response); } - @DataProvider(name = "uris") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpFixedURI }, { httpsFixedURI }, @@ -164,7 +164,8 @@ public class ConcurrentResponses { // The ofString implementation accumulates data, below a certain threshold // into the byte buffers it is given. - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsString(String uri) throws Exception { int id = IDS.getAndIncrement(); ExecutorService virtualExecutor = Executors.newThreadPerTaskExecutor(Thread.ofVirtual() @@ -204,7 +205,8 @@ public class ConcurrentResponses { // The custom subscriber aggressively attacks any area, between the limit // and the capacity, in the byte buffers it is given, by writing 'X' into it. - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testWithCustomSubscriber(String uri) throws Exception { int id = IDS.getAndIncrement(); ExecutorService virtualExecutor = Executors.newThreadPerTaskExecutor(Thread.ofVirtual() @@ -301,8 +303,8 @@ public class ConcurrentResponses { + server.getAddress().getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); httpTestServer = HttpServer.create(sa, 0); httpTestServer.createContext("/http1/fixed", new Http1FixedHandler()); @@ -342,8 +344,8 @@ public class ConcurrentResponses { https3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(0); httpsTestServer.stop(0); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/ConnectExceptionTest.java b/test/jdk/java/net/httpclient/ConnectExceptionTest.java index 29ffeca7d5e..0745e2af934 100644 --- a/test/jdk/java/net/httpclient/ConnectExceptionTest.java +++ b/test/jdk/java/net/httpclient/ConnectExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -25,7 +25,7 @@ * @test * @summary Expect ConnectException for all non-security related connect errors * @bug 8204864 - * @run testng/othervm -Djdk.net.hosts.file=HostFileDoesNotExist ConnectExceptionTest + * @run junit/othervm -Djdk.net.hosts.file=HostFileDoesNotExist ConnectExceptionTest */ import java.io.IOException; @@ -42,11 +42,12 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.util.List; import java.util.concurrent.ExecutionException; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ConnectExceptionTest { @@ -64,8 +65,7 @@ public class ConnectExceptionTest { @Override public String toString() { return "NO_PROXY"; } }; - @DataProvider(name = "uris") - public Object[][] uris() { + public static Object[][] uris() { return new Object[][]{ { "http://test.invalid/", NO_PROXY }, { "https://test.invalid/", NO_PROXY }, @@ -74,7 +74,8 @@ public class ConnectExceptionTest { }; } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("uris") void testSynchronousGET(String uriString, ProxySelector proxy) throws Exception { out.printf("%n---%ntestSynchronousGET starting uri:%s, proxy:%s%n", uriString, proxy); HttpClient client = HttpClient.newBuilder().proxy(proxy).build(); @@ -90,7 +91,8 @@ public class ConnectExceptionTest { } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("uris") void testSynchronousPOST(String uriString, ProxySelector proxy) throws Exception { out.printf("%n---%ntestSynchronousPOST starting uri:%s, proxy:%s%n", uriString, proxy); HttpClient client = HttpClient.newBuilder().proxy(proxy).build(); @@ -108,7 +110,8 @@ public class ConnectExceptionTest { } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("uris") void testAsynchronousGET(String uriString, ProxySelector proxy) throws Exception { out.printf("%n---%ntestAsynchronousGET starting uri:%s, proxy:%s%n", uriString, proxy); HttpClient client = HttpClient.newBuilder().proxy(proxy).build(); @@ -129,7 +132,8 @@ public class ConnectExceptionTest { } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("uris") void testAsynchronousPOST(String uriString, ProxySelector proxy) throws Exception { out.printf("%n---%ntestAsynchronousPOST starting uri:%s, proxy:%s%n", uriString, proxy); HttpClient client = HttpClient.newBuilder().proxy(proxy).build(); diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java index 6cdaf594b1d..e1f4aee1d51 100644 --- a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java +++ b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeAsync.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,7 +23,9 @@ import java.net.http.HttpClient.Version; import java.time.Duration; -import org.testng.annotations.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -31,14 +33,15 @@ import org.testng.annotations.Test; * @bug 8208391 * @library /test/lib * @build AbstractConnectTimeoutHandshake - * @run testng/othervm ConnectTimeoutHandshakeAsync + * @run junit/othervm ConnectTimeoutHandshakeAsync */ public class ConnectTimeoutHandshakeAsync extends AbstractConnectTimeoutHandshake { - @Test(dataProvider = "variants") @Override + @ParameterizedTest + @MethodSource("variants") public void timeoutAsync(Version requestVersion, String method, Duration connectTimeout, diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java index 17ea3041c5b..eb571b2d16c 100644 --- a/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java +++ b/test/jdk/java/net/httpclient/ConnectTimeoutHandshakeSync.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,7 +23,9 @@ import java.net.http.HttpClient.Version; import java.time.Duration; -import org.testng.annotations.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -31,14 +33,15 @@ import org.testng.annotations.Test; * @bug 8208391 * @library /test/lib * @build AbstractConnectTimeoutHandshake - * @run testng/othervm ConnectTimeoutHandshakeSync + * @run junit/othervm ConnectTimeoutHandshakeSync */ public class ConnectTimeoutHandshakeSync extends AbstractConnectTimeoutHandshake { - @Test(dataProvider = "variants") @Override + @ParameterizedTest + @MethodSource("variants") public void timeoutSync(Version requestVersion, String method, Duration connectTimeout, diff --git a/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java b/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java index 75c7c984a6b..01c166dcb1b 100644 --- a/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java +++ b/test/jdk/java/net/httpclient/ContentLengthHeaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -30,7 +30,7 @@ * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * @bug 8283544 8358942 - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.allowRestrictedHeaders=content-length * -Djdk.internal.httpclient.debug=true * ContentLengthHeaderTest @@ -39,10 +39,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; @@ -59,19 +55,22 @@ import java.util.Optional; import javax.net.ssl.SSLContext; import jdk.test.lib.net.URIBuilder; - import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ContentLengthHeaderTest implements HttpServerAdapters { - final String NO_BODY_PATH = "/no_body"; - final String BODY_PATH = "/body"; + static final String NO_BODY_PATH = "/no_body"; + static final String BODY_PATH = "/body"; static HttpTestServer testContentLengthServerH1; static HttpTestServer testContentLengthServerH2; @@ -79,13 +78,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { static PrintStream testLog = System.err; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpClient hc; - URI testContentLengthURIH1; - URI testContentLengthURIH2; - URI testContentLengthURIH3; + private static HttpClient hc; + private static URI testContentLengthURIH1; + private static URI testContentLengthURIH2; + private static URI testContentLengthURIH3; - @BeforeTest - public void setup() throws IOException, URISyntaxException, InterruptedException { + @BeforeAll + public static void setup() throws IOException, URISyntaxException, InterruptedException { testContentLengthServerH1 = HttpTestServer.create(HTTP_1_1); testContentLengthServerH2 = HttpTestServer.create(HTTP_2, sslContext); testContentLengthServerH3 = HttpTestServer.create(HTTP_3, sslContext); @@ -128,7 +127,7 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { + testContentLengthServerH3.getH3AltService().get().getAddress()); testLog.println("Request URI for Client: " + testContentLengthURIH3); - hc = newClientBuilderForH3() + hc = HttpServerAdapters.createClientBuilderForH3() .proxy(HttpClient.Builder.NO_PROXY) .sslContext(sslContext) .build(); @@ -139,12 +138,12 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .build(); // populate alt-service registry var resp = hc.send(firstReq, BodyHandlers.ofString()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); testLog.println("**** setup done ****"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { testLog.println("**** tearing down ****"); if (testContentLengthServerH1 != null) testContentLengthServerH1.stop(); @@ -154,8 +153,7 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { testContentLengthServerH3.stop(); } - @DataProvider(name = "bodies") - Object[][] bodies() { + static Object[][] bodies() { return new Object[][]{ {HTTP_1_1, URI.create(testContentLengthURIH1 + BODY_PATH)}, {HTTP_2, URI.create(testContentLengthURIH2 + BODY_PATH)}, @@ -163,15 +161,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { }; } - @DataProvider(name = "h1body") - Object[][] h1body() { + static Object[][] h1body() { return new Object[][]{ {HTTP_1_1, URI.create(testContentLengthURIH1 + BODY_PATH)} }; } - @DataProvider(name = "nobodies") - Object[][] nobodies() { + static Object[][] nobodies() { return new Object[][]{ {HTTP_1_1, URI.create(testContentLengthURIH1 + NO_BODY_PATH)}, {HTTP_2, URI.create(testContentLengthURIH2 + NO_BODY_PATH)}, @@ -179,8 +175,9 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { }; } - @Test(dataProvider = "nobodies") + @ParameterizedTest // A GET request with no request body should have no Content-length header + @MethodSource("nobodies") public void getWithNoBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking GET with no request body"); HttpRequest req = HttpRequest.newBuilder() @@ -189,12 +186,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "nobodies") + @ParameterizedTest // A GET request with empty request body should have no Content-length header + @MethodSource("nobodies") public void getWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking GET with no request body"); HttpRequest req = HttpRequest.newBuilder() @@ -203,12 +201,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "bodies") + @ParameterizedTest // A GET request with empty request body and explicitly added Content-length header + @MethodSource("bodies") public void getWithZeroContentLength(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking GET with no request body"); HttpRequest req = HttpRequest.newBuilder() @@ -218,13 +217,14 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "bodies") + @ParameterizedTest // A GET request with a request body should have a Content-length header // in HTTP/1.1 + @MethodSource("bodies") public void getWithBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking GET with request body"); HttpRequest req = HttpRequest.newBuilder() @@ -233,12 +233,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "nobodies") + @ParameterizedTest // A DELETE request with no request body should have no Content-length header + @MethodSource("nobodies") public void deleteWithNoBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking DELETE with no request body"); HttpRequest req = HttpRequest.newBuilder() @@ -247,12 +248,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "nobodies") + @ParameterizedTest // A DELETE request with empty request body should have no Content-length header + @MethodSource("nobodies") public void deleteWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking DELETE with no request body"); HttpRequest req = HttpRequest.newBuilder() @@ -261,13 +263,14 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "bodies") + @ParameterizedTest // A DELETE request with a request body should have a Content-length header // in HTTP/1.1 + @MethodSource("bodies") public void deleteWithBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking DELETE with request body"); HttpRequest req = HttpRequest.newBuilder() @@ -276,12 +279,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "nobodies") + @ParameterizedTest // A HEAD request with no request body should have no Content-length header + @MethodSource("nobodies") public void headWithNoBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking HEAD with no request body"); HttpRequest req = HttpRequest.newBuilder() @@ -290,12 +294,13 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "nobodies") + @ParameterizedTest // A HEAD request with empty request body should have no Content-length header + @MethodSource("nobodies") public void headWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking HEAD with no request body"); HttpRequest req = HttpRequest.newBuilder() @@ -304,13 +309,14 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "bodies") + @ParameterizedTest // A HEAD request with a request body should have a Content-length header // in HTTP/1.1 + @MethodSource("bodies") public void headWithBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking HEAD with request body"); HttpRequest req = HttpRequest.newBuilder() @@ -321,13 +327,14 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { // Sending this request invokes sendResponseHeaders which emits a warning about including // a Content-length header with a HEAD request HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "h1body") + @ParameterizedTest // A POST request with empty request body should have a Content-length header // in HTTP/1.1 + @MethodSource("h1body") public void postWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking POST with request body"); HttpRequest req = HttpRequest.newBuilder() @@ -336,13 +343,14 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "bodies") + @ParameterizedTest // A POST request with a request body should have a Content-length header // in HTTP/1.1 + @MethodSource("bodies") public void postWithBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking POST with request body"); HttpRequest req = HttpRequest.newBuilder() @@ -351,13 +359,14 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "h1body") + @ParameterizedTest // A PUT request with empty request body should have a Content-length header // in HTTP/1.1 + @MethodSource("h1body") public void putWithEmptyBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking PUT with request body"); HttpRequest req = HttpRequest.newBuilder() @@ -366,13 +375,14 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } - @Test(dataProvider = "bodies") + @ParameterizedTest // A PUT request with a request body should have a Content-length header // in HTTP/1.1 + @MethodSource("bodies") public void putWithBody(Version version, URI uri) throws IOException, InterruptedException { testLog.println(version + " Checking PUT with request body"); HttpRequest req = HttpRequest.newBuilder() @@ -381,8 +391,8 @@ public class ContentLengthHeaderTest implements HttpServerAdapters { .uri(uri) .build(); HttpResponse resp = hc.send(req, HttpResponse.BodyHandlers.ofString(UTF_8)); - assertEquals(resp.statusCode(), 200, resp.body()); - assertEquals(resp.version(), version); + assertEquals(200, resp.statusCode(), resp.body()); + assertEquals(version, resp.version()); } public static void handleResponse(long expected, HttpTestExchange ex, String body, int rCode) throws IOException { diff --git a/test/jdk/java/net/httpclient/CookieHeaderTest.java b/test/jdk/java/net/httpclient/CookieHeaderTest.java index cd47262825c..62e62680e03 100644 --- a/test/jdk/java/net/httpclient/CookieHeaderTest.java +++ b/test/jdk/java/net/httpclient/CookieHeaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,17 +27,13 @@ * @summary Test for multiple vs single cookie header for HTTP/2 vs HTTP/1.1 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm + * @run junit/othervm * -Djdk.tls.acknowledgeCloseNotify=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * CookieHeaderTest */ import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; @@ -78,26 +74,31 @@ import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class CookieHeaderTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 6 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - DummyServer httpDummyServer; - DummyServer httpsDummyServer; - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; - String httpDummy; - String httpsDummy; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 6 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static DummyServer httpDummyServer; + private static DummyServer httpsDummyServer; + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; + private static String httpDummy; + private static String httpsDummy; static final String MESSAGE = "Basic CookieHeaderTest message body"; static final int ITERATIONS = 3; @@ -110,8 +111,7 @@ public class CookieHeaderTest implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { httpURI, HTTP_1_1 }, { httpsURI, HTTP_1_1 }, @@ -129,7 +129,8 @@ public class CookieHeaderTest implements HttpServerAdapters { static final AtomicLong requestCounter = new AtomicLong(); - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString, HttpClient.Version version) throws Exception { out.printf("%n---- starting (%s) ----%n", uriString); ConcurrentHashMap> cookieHeaders @@ -169,14 +170,15 @@ public class CookieHeaderTest implements HttpServerAdapters { + ", version=" + response.version()); out.println(" Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MESSAGE); - assertEquals(response.headers().allValues("X-Request-Cookie"), - cookies.stream() - .filter(s -> !s.startsWith("LOC")) - .collect(Collectors.toList())); + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, response.body()); + List expectedCookieList = cookies.stream() + .filter(s -> !s.startsWith("LOC")).toList(); + List actualCookieList = response.headers() + .allValues("X-Request-Cookie"); + assertEquals(expectedCookieList, actualCookieList); if (version == HTTP_3 && i > 0) { - assertEquals(response.version(), HTTP_3); + assertEquals(HTTP_3, response.version()); } requestBuilder = HttpRequest.newBuilder(uri) .header("X-uuid", "uuid-" + requestCounter.incrementAndGet()); @@ -192,8 +194,8 @@ public class CookieHeaderTest implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new CookieValidationHandler(), "/http1/cookie/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/cookie/retry"; @@ -228,8 +230,8 @@ public class CookieHeaderTest implements HttpServerAdapters { httpsDummyServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/CustomRequestPublisher.java b/test/jdk/java/net/httpclient/CustomRequestPublisher.java index f26def9a44c..c8d22419852 100644 --- a/test/jdk/java/net/httpclient/CustomRequestPublisher.java +++ b/test/jdk/java/net/httpclient/CustomRequestPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ * @summary Checks correct handling of Publishers that call onComplete without demand * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm CustomRequestPublisher + * @run junit/othervm CustomRequestPublisher */ import java.net.InetAddress; @@ -51,10 +51,6 @@ import java.net.http.HttpResponse; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; @@ -62,27 +58,31 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.net.http.HttpResponse.BodyHandlers.ofString; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class CustomRequestPublisher implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { Supplier fixedSupplier = () -> new FixedLengthBodyPublisher(); Supplier unknownSupplier = () -> new UnknownLengthBodyPublisher(); @@ -114,12 +114,12 @@ public class CustomRequestPublisher implements HttpServerAdapters { /** Asserts HTTP Version, and SSLSession presence when applicable. */ static void assertVersionAndSession(int step, HttpResponse response, String uri) { if (uri.contains("http2") || uri.contains("https2")) { - assertEquals(response.version(), HTTP_2); + assertEquals(HTTP_2, response.version()); } else if (uri.contains("http1") || uri.contains("https1")) { - assertEquals(response.version(), HTTP_1_1); + assertEquals(HTTP_1_1, response.version()); } else if (uri.contains("http3")) { - if (step == 0) assertNotEquals(response.version(), HTTP_1_1); - else assertEquals(response.version(), HTTP_3, + if (step == 0) assertNotEquals(HTTP_1_1, response.version()); + else assertEquals(HTTP_3, response.version(), "unexpected response version on step " + step); } else { fail("Unknown HTTP version in test for: " + uri); @@ -160,7 +160,8 @@ public class CustomRequestPublisher implements HttpServerAdapters { return builder; } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void test(String uri, Supplier bpSupplier, boolean sameClient) throws Exception { @@ -180,13 +181,14 @@ public class CustomRequestPublisher implements HttpServerAdapters { out.println("Got body: " + resp.body()); assertTrue(resp.statusCode() == 200, "Expected 200, got:" + resp.statusCode()); - assertEquals(resp.body(), bodyPublisher.bodyAsString()); + assertEquals(bodyPublisher.bodyAsString(), resp.body()); assertVersionAndSession(i, resp, uri); } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void testAsync(String uri, Supplier bpSupplier, boolean sameClient) throws Exception { @@ -207,7 +209,7 @@ public class CustomRequestPublisher implements HttpServerAdapters { out.println("Got body: " + resp.body()); assertTrue(resp.statusCode() == 200, "Expected 200, got:" + resp.statusCode()); - assertEquals(resp.body(), bodyPublisher.bodyAsString()); + assertEquals(bodyPublisher.bodyAsString(), resp.body()); assertVersionAndSession(0, resp, uri); } @@ -337,8 +339,8 @@ public class CustomRequestPublisher implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new HttpTestEchoHandler(), "/http1/echo"); @@ -367,8 +369,8 @@ public class CustomRequestPublisher implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/CustomResponseSubscriber.java b/test/jdk/java/net/httpclient/CustomResponseSubscriber.java index c581491a214..b2695271035 100644 --- a/test/jdk/java/net/httpclient/CustomResponseSubscriber.java +++ b/test/jdk/java/net/httpclient/CustomResponseSubscriber.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -27,7 +27,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer * jdk.httpclient.test.lib.common.TestServerConfigurator - * @run testng/othervm CustomResponseSubscriber + * @run junit/othervm CustomResponseSubscriber */ import java.io.IOException; @@ -59,37 +59,37 @@ import jdk.httpclient.test.lib.http2.Http2Handler; import javax.net.ssl.SSLContext; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class CustomResponseSubscriber { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - Http2TestServer http2TestServer; // HTTP/2 ( h2c ) - Http2TestServer https2TestServer; // HTTP/2 ( h2 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; + private static HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpsServer httpsTestServer; // HTTPS/1.1 + private static Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + private static Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; static final int ITERATION_COUNT = 10; // a shared executor helps reduce the amount of threads created by the test static final Executor executor = Executors.newCachedThreadPool(); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI_fixed, false }, { httpURI_chunk, false }, @@ -118,7 +118,8 @@ public class CustomResponseSubscriber { .build(); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsString(String uri, boolean sameClient) throws Exception { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { @@ -130,14 +131,14 @@ public class CustomResponseSubscriber { BodyHandler handler = new CRSBodyHandler(); HttpResponse response = client.send(req, handler); String body = response.body(); - assertEquals(body, ""); + assertEquals("", body); } } static class CRSBodyHandler implements BodyHandler { @Override public BodySubscriber apply(HttpResponse.ResponseInfo rinfo) { - assertEquals(rinfo.statusCode(), 200); + assertEquals(200, rinfo.statusCode()); return new CRSBodySubscriber(); } } @@ -185,8 +186,8 @@ public class CustomResponseSubscriber { + server.getAddress().getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 HttpHandler h1_fixedLengthHandler = new HTTP1_FixedLengthHandler(); HttpHandler h1_chunkHandler = new HTTP1_ChunkedHandler(); @@ -226,8 +227,8 @@ public class CustomResponseSubscriber { https2TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(0); httpsTestServer.stop(0); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/DependentActionsTest.java b/test/jdk/java/net/httpclient/DependentActionsTest.java index 9a77d377497..fcfbb393e5a 100644 --- a/test/jdk/java/net/httpclient/DependentActionsTest.java +++ b/test/jdk/java/net/httpclient/DependentActionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext * DependentActionsTest - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.quic.maxPtoBackoff=9 * DependentActionsTest */ @@ -39,12 +39,6 @@ import java.io.InputStreamReader; import java.lang.StackWalker.StackFrame; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.SkipException; -import org.testng.annotations.AfterTest; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -89,28 +83,35 @@ import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.util.stream.Collectors.toList; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class DependentActionsTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; - String http3URI_fixed; - String http3URI_chunk; - String http3URI_head; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; + private static String http3URI_fixed; + private static String http3URI_chunk; + private static String http3URI_head; static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); @@ -132,7 +133,7 @@ public class DependentActionsTest implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - private volatile HttpClient sharedClient; + private static volatile HttpClient sharedClient; static class TestExecutor implements Executor { final AtomicLong tasks = new AtomicLong(); @@ -158,7 +159,7 @@ public class DependentActionsTest implements HttpServerAdapters { } } - @AfterClass + @AfterAll static final void printFailedTests() { out.println("\n========================="); try { @@ -179,7 +180,7 @@ public class DependentActionsTest implements HttpServerAdapters { } } - private String[] uris() { + private static String[] uris() { return new String[] { httpURI_fixed, httpURI_chunk, @@ -206,8 +207,7 @@ public class DependentActionsTest implements HttpServerAdapters { } } - @DataProvider(name = "noStalls") - public Object[][] noThrows() { + public static Object[][] noThrows() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2][]; int i = 0; @@ -220,8 +220,7 @@ public class DependentActionsTest implements HttpServerAdapters { return result; } - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2][]; int i = 0; @@ -237,20 +236,21 @@ public class DependentActionsTest implements HttpServerAdapters { return result; } - private HttpClient makeNewClient() { + private static HttpClient makeNewClient() { clientCount.incrementAndGet(); - return newClientBuilderForH3() + return HttpServerAdapters.createClientBuilderForH3() .proxy(Builder.NO_PROXY) .executor(executor) .sslContext(sslContext) .build(); } - HttpClient newHttpClient(boolean share) { + private static final Object zis = new Object(); + static HttpClient newHttpClient(boolean share) { if (!share) return makeNewClient(); HttpClient shared = sharedClient; if (shared != null) return shared; - synchronized (this) { + synchronized (zis) { shared = sharedClient; if (shared == null) { shared = sharedClient = makeNewClient(); @@ -259,7 +259,8 @@ public class DependentActionsTest implements HttpServerAdapters { } } - @Test(dataProvider = "noStalls") + @ParameterizedTest + @MethodSource("noThrows") public void testNoStalls(String uri, boolean sameClient) throws Exception { HttpClient client = null; @@ -279,11 +280,12 @@ public class DependentActionsTest implements HttpServerAdapters { BodyHandlers.ofString()); HttpResponse response = client.send(req, handler); String body = response.body(); - assertEquals(URI.create(body).getPath(), URI.create(uri).getPath()); + assertEquals(URI.create(uri).getPath(), URI.create(body).getPath()); } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsStringAsync(String uri, boolean sameClient, Supplier s) @@ -296,7 +298,8 @@ public class DependentActionsTest implements HttpServerAdapters { this::finish, this::extractString, staller); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsLinesAsync(String uri, boolean sameClient, Supplier s) @@ -309,7 +312,8 @@ public class DependentActionsTest implements HttpServerAdapters { this::finish, this::extractStream, staller); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsInputStreamAsync(String uri, boolean sameClient, Supplier s) @@ -329,11 +333,8 @@ public class DependentActionsTest implements HttpServerAdapters { Staller staller) throws Exception { - if (errorRef.get() != null) { - SkipException sk = new SkipException("skipping due to previous failure: " + name); - sk.setStackTrace(new StackTraceElement[0]); - throw sk; - } + Assumptions.assumeTrue(errorRef.get() == null, + "skipping due to previous failure: " + name); out.printf("%n%s%s%n", now(), name); try { testDependent(uri, sameClient, handlers, finisher, extractor, staller); @@ -376,7 +377,7 @@ public class DependentActionsTest implements HttpServerAdapters { // it's possible that the first request still went through HTTP/2 // if the config was HTTP3_ANY. Retry it - the next time we should // have HTTP/3 - assertEquals(resp.version(), HTTP_3, + assertEquals(HTTP_3, resp.version(), "expected second request to go through HTTP/3 (serverConfig=" + http3TestServer.h3DiscoveryConfig() + ")"); } @@ -479,10 +480,10 @@ public class DependentActionsTest implements HttpServerAdapters { throw new RuntimeException("Test failed in " + w + ": " + response, error); } - assertEquals(result, List.of(response.request().uri().getPath())); + assertEquals(List.of(response.request().uri().getPath()), result); var uriStr = response.request().uri().toString(); if (HTTP_3 != version(uriStr) || http3TestServer.h3DiscoveryConfig() != Http3DiscoveryMode.ANY) { - assertEquals(response.version(), version(uriStr), uriStr); + assertEquals(version(uriStr), response.version(), uriStr); } return response; } finally { @@ -619,7 +620,7 @@ public class DependentActionsTest implements HttpServerAdapters { return null; } - HttpRequest.Builder newRequestBuilder(String uri) { + static HttpRequest.Builder newRequestBuilder(String uri) { var builder = HttpRequest.newBuilder(URI.create(uri)); if (version(uri) == HTTP_3) { builder.version(HTTP_3); @@ -628,21 +629,21 @@ public class DependentActionsTest implements HttpServerAdapters { return builder; } - HttpResponse headRequest(HttpClient client) + static HttpResponse headRequest(HttpClient client) throws IOException, InterruptedException { var request = newRequestBuilder(http3URI_head) .HEAD().version(HTTP_2).build(); var response = client.send(request, BodyHandlers.ofString()); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_2); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_2, response.version()); System.out.println("\n--- HEAD request succeeded ----\n"); System.err.println("\n--- HEAD request succeeded ----\n"); return response; } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 HttpTestHandler h1_fixedLengthHandler = new HTTP_FixedLengthHandler(); HttpTestHandler h1_chunkHandler = new HTTP_ChunkedHandler(); @@ -703,8 +704,8 @@ public class DependentActionsTest implements HttpServerAdapters { headRequest(newHttpClient(true)); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { sharedClient = null; httpTestServer.stop(); httpsTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java b/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java index f1b07f7abce..e045f4e22f1 100644 --- a/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java +++ b/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext * DependentPromiseActionsTest - * @run testng/othervm -Djdk.internal.httpclient.debug=true DependentPromiseActionsTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true DependentPromiseActionsTest */ import java.io.BufferedReader; @@ -37,11 +37,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.lang.StackWalker.StackFrame; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -93,21 +88,26 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class DependentPromiseActionsTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; - String http3URI_fixed; - String http3URI_chunk; + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; + private static String http3URI_fixed; + private static String http3URI_chunk; static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); @@ -129,7 +129,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - private volatile HttpClient sharedClient; + private static volatile HttpClient sharedClient; static class TestExecutor implements Executor { final AtomicLong tasks = new AtomicLong(); @@ -155,7 +155,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { } } - @AfterClass + @AfterAll static final void printFailedTests() { out.println("\n========================="); try { @@ -176,7 +176,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { } } - private String[] uris() { + private static String[] uris() { return new String[] { http3URI_fixed, http3URI_chunk, @@ -201,8 +201,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { } } - @DataProvider(name = "noStalls") - public Object[][] noThrows() { + public static Object[][] noThrows() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2][]; int i = 0; @@ -215,8 +214,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { return result; } - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2][]; int i = 0; @@ -271,7 +269,8 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { return builder.build(); } - @Test(dataProvider = "noStalls") + @ParameterizedTest + @MethodSource("noThrows") public void testNoStalls(String rootUri, boolean sameClient) throws Exception { if (!FAILURES.isEmpty()) return; @@ -287,7 +286,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { HttpRequest req = request(uri); BodyHandler> handler = - new StallingBodyHandler((w) -> {}, + new StallingBodyHandler<>((w) -> {}, BodyHandlers.ofLines()); Map>>> pushPromises = new ConcurrentHashMap<>(); @@ -304,13 +303,13 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { HttpResponse> response = client.sendAsync(req, BodyHandlers.ofLines(), pushHandler).get(); String body = response.body().collect(Collectors.joining("|")); - assertEquals(URI.create(body).getPath(), URI.create(uri).getPath()); + assertEquals(URI.create(uri).getPath(), URI.create(body).getPath()); for (HttpRequest promised : pushPromises.keySet()) { out.printf("%s Received promise: %s%n\tresponse: %s%n", now(), promised, pushPromises.get(promised).get()); String promisedBody = pushPromises.get(promised).get().body() .collect(Collectors.joining("|")); - assertEquals(promisedBody, promised.uri().toASCIIString()); + assertEquals(promised.uri().toASCIIString(), promisedBody); } assertEquals(3, pushPromises.size()); } @@ -321,8 +320,9 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testAsStringAsync(String uri, + @ParameterizedTest + @MethodSource("variants") + void testAsStringAsync(String uri, boolean sameClient, Supplier stallers) throws Exception @@ -334,8 +334,9 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { SubscriberType.EAGER); } - @Test(dataProvider = "variants") - public void testAsLinesAsync(String uri, + @ParameterizedTest + @MethodSource("variants") + void testAsLinesAsync(String uri, boolean sameClient, Supplier stallers) throws Exception @@ -347,8 +348,9 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { SubscriberType.LAZZY); } - @Test(dataProvider = "variants") - public void testAsInputStreamAsync(String uri, + @ParameterizedTest + @MethodSource("variants") + void testAsInputStreamAsync(String uri, boolean sameClient, Supplier stallers) throws Exception @@ -362,7 +364,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { private void testDependent(String name, String uri, boolean sameClient, Supplier> handlers, - Finisher finisher, + Finisher finisher, Extractor extractor, Supplier stallers, SubscriberType subscriberType) @@ -384,7 +386,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { private void testDependent(String rootUri, boolean sameClient, Supplier> handlers, - Finisher finisher, + Finisher finisher, Extractor extractor, Supplier stallers, SubscriberType subscriberType) @@ -424,7 +426,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { enum Where { ON_PUSH_PROMISE, BODY_HANDLER, ON_SUBSCRIBE, ON_NEXT, ON_COMPLETE, ON_ERROR, GET_BODY, BODY_CF; public Consumer select(Consumer consumer) { - return new Consumer() { + return new Consumer<>() { @Override public void accept(Where where) { if (Where.this == where) { @@ -475,10 +477,10 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { staller.acquire(); assert staller.willStall(); try { - BodyHandler handler = new StallingBodyHandler<>( + BodyHandler handler = new StallingBodyHandler<>( where.select(staller), handlers.get()); CompletableFuture> cf = acceptor.apply(handler); - Tuple tuple = new Tuple(failed, cf, staller); + Tuple tuple = new Tuple<>(failed, cf, staller); promiseMap.putIfAbsent(pushPromiseRequest, tuple); CompletableFuture done = cf.whenComplete( (r, t) -> checkThreadAndStack(thread, failed, r, t)); @@ -567,7 +569,7 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { throw new RuntimeException("Test failed in " + w + ": " + uri, error); } - assertEquals(result, List.of(response.request().uri().toASCIIString())); + assertEquals(List.of(response.request().uri().toASCIIString()), result); } finally { staller.reset(); } @@ -582,10 +584,10 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { for (HttpRequest req : ph.promiseMap.keySet()) { finish(w, ph.promiseMap.get(req), extractor); } - assertEquals(ph.promiseMap.size(), 3, + assertEquals(3, ph.promiseMap.size(), "Expected 3 push promises for " + w + " in " + response.request().uri()); - assertEquals(result, List.of(response.request().uri().toASCIIString())); + assertEquals(List.of(response.request().uri().toASCIIString()), result); } @@ -699,8 +701,8 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/2 HttpTestHandler fixedLengthHandler = new HTTP_FixedLengthHandler(); HttpTestHandler chunkedHandler = new HTTP_ChunkedHandler(); @@ -729,8 +731,8 @@ public class DependentPromiseActionsTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { if (sharedClient != null) { sharedClient.close(); } diff --git a/test/jdk/java/net/httpclient/EncodedCharsInURI.java b/test/jdk/java/net/httpclient/EncodedCharsInURI.java index eab9a29b172..19b0a1d651b 100644 --- a/test/jdk/java/net/httpclient/EncodedCharsInURI.java +++ b/test/jdk/java/net/httpclient/EncodedCharsInURI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext * EncodedCharsInURI - * @run testng/othervm + * @run junit/othervm * -Djdk.tls.acknowledgeCloseNotify=true * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=headers,errors EncodedCharsInURI @@ -38,11 +38,6 @@ import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; @@ -83,32 +78,37 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class EncodedCharsInURI implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ] - DummyServer httpsDummyServer; // HTTPS/1.1 - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; - String http3URI_fixed; - String http3URI_chunk; - String http3URI_head; - String httpDummy; - String httpsDummy; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ] + private static DummyServer httpsDummyServer; // HTTPS/1.1 + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; + private static String http3URI_fixed; + private static String http3URI_chunk; + private static String http3URI_head; + private static String httpDummy; + private static String httpsDummy; static final int ITERATION_COUNT = 1; // a shared executor helps reduce the amount of threads created by the test @@ -126,7 +126,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - private volatile HttpClient sharedClient; + private static volatile HttpClient sharedClient; static class TestExecutor implements Executor { final AtomicLong tasks = new AtomicLong(); @@ -152,7 +152,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { } } - @AfterClass + @AfterAll static final void printFailedTests() { out.println("\n========================="); try { @@ -172,7 +172,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { } } - private String[] uris() { + private static String[] uris() { return new String[] { httpDummy, httpsDummy, @@ -189,8 +189,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { }; } - @DataProvider(name = "noThrows") - public Object[][] noThrows() { + public static Object[][] noThrows() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2][]; //Object[][] result = new Object[uris.length][]; @@ -216,7 +215,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { return null; } - HttpRequest.Builder newRequestBuilder(String uri) { + static HttpRequest.Builder newRequestBuilder(String uri) { var builder = HttpRequest.newBuilder(URI.create(uri)); if (version(uri) == HTTP_3) { builder.version(HTTP_3); @@ -225,7 +224,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { return builder; } - HttpResponse headRequest(HttpClient client) + static HttpResponse headRequest(HttpClient client) throws IOException, InterruptedException { out.println("\n" + now() + "--- Sending HEAD request ----\n"); @@ -234,27 +233,28 @@ public class EncodedCharsInURI implements HttpServerAdapters { var request = newRequestBuilder(http3URI_head) .HEAD().version(HTTP_2).build(); var response = client.send(request, BodyHandlers.ofString()); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_2); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_2, response.version()); out.println("\n" + now() + "--- HEAD request succeeded ----\n"); err.println("\n" + now() + "--- HEAD request succeeded ----\n"); return response; } - private HttpClient makeNewClient() { + private static HttpClient makeNewClient() { clientCount.incrementAndGet(); - return newClientBuilderForH3() + return HttpServerAdapters.createClientBuilderForH3() .executor(executor) .proxy(NO_PROXY) .sslContext(sslContext) .build(); } - HttpClient newHttpClient(boolean share) { + private static final Object zis = new Object(); + static HttpClient newHttpClient(boolean share) { if (!share) return makeNewClient(); HttpClient shared = sharedClient; if (shared != null) return shared; - synchronized (this) { + synchronized (zis) { shared = sharedClient; if (shared == null) { shared = sharedClient = makeNewClient(); @@ -273,7 +273,8 @@ public class EncodedCharsInURI implements HttpServerAdapters { } } - @Test(dataProvider = "noThrows") + @ParameterizedTest + @MethodSource("noThrows") public void testEncodedChars(String uri, boolean sameClient) throws Exception { HttpClient client = null; @@ -302,13 +303,13 @@ public class EncodedCharsInURI implements HttpServerAdapters { } else { out.println("Found expected " + body + " in " + uri); } - assertEquals(response.version(), version(uri)); + assertEquals(version(uri), response.version()); } } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println(now() + "begin setup"); // HTTP/1.1 @@ -387,8 +388,8 @@ public class EncodedCharsInURI implements HttpServerAdapters { err.println(now() + "setup done"); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { sharedClient.close(); httpTestServer.stop(); httpsTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/EscapedOctetsInURI.java b/test/jdk/java/net/httpclient/EscapedOctetsInURI.java index abb0d7d8541..6ef71b7d22d 100644 --- a/test/jdk/java/net/httpclient/EscapedOctetsInURI.java +++ b/test/jdk/java/net/httpclient/EscapedOctetsInURI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,7 +27,7 @@ * @bug 8198716 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=reqeusts,headers * EscapedOctetsInURI */ @@ -52,10 +52,6 @@ import java.util.List; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.err; import static java.lang.System.out; @@ -65,24 +61,29 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class EscapedOctetsInURI implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; - String http3URI_head; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; + private static String http3URI_head; - private volatile HttpClient sharedClient; + private static volatile HttpClient sharedClient; static final String[][] pathsAndQueryStrings = new String[][] { // partial-path URI query @@ -95,8 +96,7 @@ public class EscapedOctetsInURI implements HttpServerAdapters { { "/012/with%20space", "?target=http%3A%2F%2Fwww.w3.org%2Fns%2Foa%23hasBody" }, }; - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { List list = new ArrayList<>(); for (boolean sameClient : new boolean[] { false, true }) { @@ -140,7 +140,7 @@ public class EscapedOctetsInURI implements HttpServerAdapters { return null; } - HttpRequest.Builder newRequestBuilder(String uri) { + static HttpRequest.Builder newRequestBuilder(String uri) { var builder = HttpRequest.newBuilder(URI.create(uri)); if (version(uri) == HTTP_3) { builder.version(HTTP_3); @@ -149,7 +149,7 @@ public class EscapedOctetsInURI implements HttpServerAdapters { return builder; } - HttpResponse headRequest(HttpClient client) + static HttpResponse headRequest(HttpClient client) throws IOException, InterruptedException { out.println("\n" + now() + "--- Sending HEAD request ----\n"); @@ -158,25 +158,26 @@ public class EscapedOctetsInURI implements HttpServerAdapters { var request = newRequestBuilder(http3URI_head) .HEAD().version(HTTP_2).build(); var response = client.send(request, BodyHandlers.ofString()); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_2); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_2, response.version()); out.println("\n" + now() + "--- HEAD request succeeded ----\n"); err.println("\n" + now() + "--- HEAD request succeeded ----\n"); return response; } - private HttpClient makeNewClient() { - return newClientBuilderForH3() + private static HttpClient makeNewClient() { + return HttpServerAdapters.createClientBuilderForH3() .proxy(NO_PROXY) .sslContext(sslContext) .build(); } - HttpClient newHttpClient(boolean share) { + private static final Object zis = new Object(); + static HttpClient newHttpClient(boolean share) { if (!share) return makeNewClient(); HttpClient shared = sharedClient; if (shared != null) return shared; - synchronized (this) { + synchronized (zis) { shared = sharedClient; if (shared == null) { shared = sharedClient = makeNewClient(); @@ -193,7 +194,8 @@ public class EscapedOctetsInURI implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void test(String uriString, boolean sameClient) throws Exception { System.out.println("\n--- Starting "); @@ -217,19 +219,20 @@ public class EscapedOctetsInURI implements HttpServerAdapters { out.println("Got response: " + resp); out.println("Got body: " + resp.body()); - assertEquals(resp.statusCode(), 200, + assertEquals(200, resp.statusCode(), "Expected 200, got:" + resp.statusCode()); // the response body should contain the exact escaped request URI URI retrievedURI = URI.create(resp.body()); - assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); - assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); - assertEquals(resp.version(), version(uriString)); + assertEquals(uri.getRawPath(), retrievedURI.getRawPath()); + assertEquals(uri.getRawQuery(), retrievedURI.getRawQuery()); + assertEquals(version(uriString), resp.version()); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void testAsync(String uriString, boolean sameClient) throws Exception { System.out.println("\n--- Starting "); URI uri = URI.create(uriString); @@ -249,22 +252,22 @@ public class EscapedOctetsInURI implements HttpServerAdapters { .thenApply(response -> { out.println("Got response: " + response); out.println("Got body: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uriString)); + assertEquals(200, response.statusCode()); + assertEquals(version(uriString), response.version()); return response.body(); }) .thenApply(body -> URI.create(body)) .thenAccept(retrievedURI -> { // the body should contain the exact escaped request URI - assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); - assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); + assertEquals(uri.getRawPath(), retrievedURI.getRawPath()); + assertEquals(uri.getRawQuery(), retrievedURI.getRawQuery()); }).join(); } } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println(now() + "begin setup"); InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); @@ -311,8 +314,8 @@ public class EscapedOctetsInURI implements HttpServerAdapters { err.println(now() + "setup done"); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { sharedClient.close(); httpTestServer.stop(); httpsTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/ExecutorShutdown.java b/test/jdk/java/net/httpclient/ExecutorShutdown.java index a7cfd56b282..a46ea66f3ae 100644 --- a/test/jdk/java/net/httpclient/ExecutorShutdown.java +++ b/test/jdk/java/net/httpclient/ExecutorShutdown.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * ExecutorShutdown @@ -65,10 +65,6 @@ import javax.net.ssl.SSLContext; import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; @@ -78,8 +74,13 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ExecutorShutdown implements HttpServerAdapters { @@ -89,25 +90,24 @@ public class ExecutorShutdown implements HttpServerAdapters { static final Random RANDOM = RandomFactory.getRandom(); private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 6 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer h2h3TestServer; // HTTP/2 ( h2+h3 ) - HttpTestServer h3TestServer; // HTTP/2 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String h2h3URI; - String h3URI; - String h2h3Head; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 6 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer h2h3TestServer; // HTTP/2 ( h2+h3 ) + private static HttpTestServer h3TestServer; // HTTP/2 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String h2h3URI; + private static String h3URI; + private static String h2h3Head; static final String MESSAGE = "ExecutorShutdown message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { h2h3URI, HTTP_3, h2h3TestServer.h3DiscoveryConfig() }, { h3URI, HTTP_3, h3TestServer.h3DiscoveryConfig() }, @@ -119,7 +119,7 @@ public class ExecutorShutdown implements HttpServerAdapters { } static final AtomicLong requestCounter = new AtomicLong(); - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; static Throwable getCause(Throwable t) { while (t instanceof CompletionException || t instanceof ExecutionException) { @@ -158,7 +158,8 @@ public class ExecutorShutdown implements HttpServerAdapters { throw new AssertionError(what + ": Unexpected exception: " + cause, cause); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testConcurrent(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting (%s) ----%n", uriString); ExecutorService executorService = Executors.newCachedThreadPool(); @@ -209,9 +210,9 @@ public class ExecutorShutdown implements HttpServerAdapters { var cf = responseCF.thenApply((response) -> { out.println(si + ": Got response: " + response); out.println(si + ": Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - if (si >= head) assertEquals(response.version(), version); - assertEquals(response.body(), MESSAGE); + assertEquals(200, response.statusCode()); + if (si >= head) assertEquals(version, response.version()); + assertEquals(MESSAGE, response.body()); return response; }).exceptionally((t) -> { Throwable cause = getCause(t); @@ -228,7 +229,8 @@ public class ExecutorShutdown implements HttpServerAdapters { } } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testSequential(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting (%s, %s, %s) ----%n%n", uriString, version, config); ExecutorService executorService = Executors.newCachedThreadPool(); @@ -272,9 +274,9 @@ public class ExecutorShutdown implements HttpServerAdapters { responseCF.thenApply((response) -> { out.println(si + ": Got response: " + response); out.println(si + ": Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - if (si > 0) assertEquals(response.version(), version); - assertEquals(response.body(), MESSAGE); + assertEquals(200, response.statusCode()); + if (si > 0) assertEquals(version, response.version()); + assertEquals(MESSAGE, response.body()); return response; }).handle((r,t) -> { if (t != null) { @@ -305,11 +307,11 @@ public class ExecutorShutdown implements HttpServerAdapters { .HEAD() .build(); var resp = client.send(request, BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println("\n**** Setup ****\n"); httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new ServerRequestHandler(), "/http1/exec/"); @@ -342,8 +344,8 @@ public class ExecutorShutdown implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.check(5000); try { diff --git a/test/jdk/java/net/httpclient/ExpectContinue.java b/test/jdk/java/net/httpclient/ExpectContinue.java index a2d928e6f41..e9ae894f3e1 100644 --- a/test/jdk/java/net/httpclient/ExpectContinue.java +++ b/test/jdk/java/net/httpclient/ExpectContinue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,7 +28,7 @@ * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestServerConfigurator * @modules java.net.http/jdk.internal.net.http.common * jdk.httpserver - * @run testng/othervm ExpectContinue + * @run junit/othervm ExpectContinue */ import com.sun.net.httpserver.HttpExchange; @@ -51,23 +51,24 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.TestServerConfigurator; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ExpectContinue { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; // HTTP/1.1 [ 2 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - String httpURI; - String httpsURI; + private static HttpServer httpTestServer; // HTTP/1.1 [ 2 servers ] + private static HttpsServer httpsTestServer; // HTTPS/1.1 + private static String httpURI; + private static String httpsURI; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { httpURI, false, "Billy" }, { httpURI, false, "Bob" }, @@ -76,7 +77,8 @@ public class ExpectContinue { }; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString, boolean expectedContinue, String data) throws Exception { @@ -94,17 +96,18 @@ public class ExpectContinue { HttpResponse response = client.send(request, BodyHandlers.ofString()); System.out.println("First response: " + response); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), data); + assertEquals(200, response.statusCode()); + assertEquals(data, response.body()); // again with the same request, to ensure no Expect header duplication response = client.send(request, BodyHandlers.ofString()); System.out.println("Second response: " + response); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), data); + assertEquals(200, response.statusCode()); + assertEquals(data, response.body()); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testAsync(String uriString, boolean expectedContinue, String data) { out.printf("test(%s, %s, %s): starting%n", uriString, expectedContinue, data); HttpClient client = HttpClient.newBuilder() @@ -120,14 +123,14 @@ public class ExpectContinue { HttpResponse response = client.sendAsync(request, BodyHandlers.ofString()).join(); System.out.println("First response: " + response); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), data); + assertEquals(200, response.statusCode()); + assertEquals(data, response.body()); // again with the same request, to ensure no Expect header duplication response = client.sendAsync(request, BodyHandlers.ofString()).join(); System.out.println("Second response: " + response); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), data); + assertEquals(200, response.statusCode()); + assertEquals(data, response.body()); } // -- Infrastructure @@ -137,8 +140,8 @@ public class ExpectContinue { + server.getAddress().getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); httpTestServer = HttpServer.create(sa, 0); httpTestServer.createContext("/http1/ec", new Http1ExpectContinueHandler()); @@ -153,8 +156,8 @@ public class ExpectContinue { httpsTestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(0); httpsTestServer.stop(0); } diff --git a/test/jdk/java/net/httpclient/ExpectContinueTest.java b/test/jdk/java/net/httpclient/ExpectContinueTest.java index a703fbfaacf..56fa363870a 100644 --- a/test/jdk/java/net/httpclient/ExpectContinueTest.java +++ b/test/jdk/java/net/httpclient/ExpectContinueTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -27,7 +27,7 @@ * @bug 8286171 8307648 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=errors ExpectContinueTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=errors ExpectContinueTest */ @@ -40,11 +40,6 @@ import jdk.httpclient.test.lib.http2.Http2TestServerConnection; import jdk.httpclient.test.lib.http2.Http2TestServerConnection.ResponseHeaders; import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.frame.HeaderFrame; -import org.testng.TestException; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLSession; @@ -79,23 +74,29 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ExpectContinueTest implements HttpServerAdapters { - HttpTestServer http1TestServer; // HTTP/1.1 - Http1HangServer http1HangServer; - Http2TestServer http2TestServer; // HTTP/2 + private static HttpTestServer http1TestServer; // HTTP/1.1 + private static Http1HangServer http1HangServer; + private static Http2TestServer http2TestServer; // HTTP/2 - URI getUri, postUri, forcePostUri, hangUri; - URI h2postUri, h2forcePostUri, h2hangUri, h2endStreamUri, h2warmupURI; + private static URI getUri, postUri, forcePostUri, hangUri; + private static URI h2postUri, h2forcePostUri, h2hangUri, h2endStreamUri, h2warmupURI; static PrintStream err = new PrintStream(System.err); static PrintStream out = new PrintStream(System.out); static final String EXPECTATION_FAILED_417 = "417 Expectation Failed"; - @DataProvider(name = "uris") - public Object[][] urisData() { + public static Object[][] urisData() { return new Object[][]{ // URI, Expected Status Code, Will finish with Exception, Protocol Version { postUri, 200, false, HTTP_1_1 }, @@ -107,7 +108,8 @@ public class ExpectContinueTest implements HttpServerAdapters { { h2endStreamUri, 200, true, HTTP_2 }, // Error }; } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("urisData") public void test(URI uri, int expectedStatusCode, boolean exceptionally, HttpClient.Version version) throws CancellationException, InterruptedException, ExecutionException, IOException { @@ -135,8 +137,8 @@ public class ExpectContinueTest implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { InetSocketAddress saHang = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); http1TestServer = HttpTestServer.create(HTTP_1_1); http1TestServer.addHandler(new GetHandler(), "/http1/get"); @@ -173,8 +175,8 @@ public class ExpectContinueTest implements HttpServerAdapters { http1HangServer.start(); http2TestServer.start(); } - @AfterTest - public void teardown() throws IOException { + @AfterAll + public static void teardown() throws IOException { http1TestServer.stop(); http1HangServer.close(); http2TestServer.stop(); @@ -372,11 +374,11 @@ public class ExpectContinueTest implements HttpServerAdapters { } if (exceptionally && testThrowable != null) { err.println("Finished exceptionally Test throwable: " + testThrowable); - assertEquals(testThrowable.getClass(), ProtocolException.class); + assertEquals(ProtocolException.class, testThrowable.getClass()); } else if (exceptionally) { - throw new TestException("Expected case to finish with an IOException but testException is null"); + fail("Expected case to finish with an IOException but testException is null"); } else if (resp != null) { - assertEquals(resp.statusCode(), expectedStatusCode); + assertEquals(expectedStatusCode, resp.statusCode()); err.println("Request completed successfully for path " + path); err.println("Response Headers: " + resp.headers()); err.println("Response Status Code: " + resp.statusCode()); diff --git a/test/jdk/java/net/httpclient/FilePublisherTest.java b/test/jdk/java/net/httpclient/FilePublisherTest.java index 9501e8054cc..292f770fc30 100644 --- a/test/jdk/java/net/httpclient/FilePublisherTest.java +++ b/test/jdk/java/net/httpclient/FilePublisherTest.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 @@ -29,14 +29,10 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm FilePublisherTest + * @run junit/othervm FilePublisherTest */ import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -58,22 +54,27 @@ import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class FilePublisherTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServerAdapters.HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpServerAdapters.HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpServerAdapters.HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpServerAdapters.HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; - FileSystem zipFs; - Path defaultFsPath; - Path zipFsPath; + private static FileSystem zipFs; + private static Path defaultFsPath; + private static Path zipFsPath; // Default file system set up static final String DEFAULT_FS_MSG = "default fs"; @@ -84,12 +85,11 @@ public class FilePublisherTest implements HttpServerAdapters { Files.createFile(file); Files.writeString(file, DEFAULT_FS_MSG); } - assertEquals(Files.readString(file), DEFAULT_FS_MSG); + assertEquals(DEFAULT_FS_MSG, Files.readString(file)); return file; } - @DataProvider(name = "defaultFsData") - public Object[][] defaultFsData() { + public static Object[][] defaultFsData() { return new Object[][]{ { httpURI, defaultFsPath, DEFAULT_FS_MSG, true }, { httpsURI, defaultFsPath, DEFAULT_FS_MSG, true }, @@ -102,7 +102,8 @@ public class FilePublisherTest implements HttpServerAdapters { }; } - @Test(dataProvider = "defaultFsData") + @ParameterizedTest + @MethodSource("defaultFsData") public void testDefaultFs(String uriString, Path path, String expectedMsg, @@ -126,12 +127,11 @@ public class FilePublisherTest implements HttpServerAdapters { Files.createFile(file); Files.writeString(file, ZIP_FS_MSG); } - assertEquals(Files.readString(file), ZIP_FS_MSG); + assertEquals(ZIP_FS_MSG, Files.readString(file)); return file; } - @DataProvider(name = "zipFsData") - public Object[][] zipFsData() { + public static Object[][] zipFsData() { return new Object[][]{ { httpURI, zipFsPath, ZIP_FS_MSG, true }, { httpsURI, zipFsPath, ZIP_FS_MSG, true }, @@ -144,7 +144,8 @@ public class FilePublisherTest implements HttpServerAdapters { }; } - @Test(dataProvider = "zipFsData") + @ParameterizedTest + @MethodSource("zipFsData") public void testZipFs(String uriString, Path path, String expectedMsg, @@ -176,13 +177,13 @@ public class FilePublisherTest implements HttpServerAdapters { var resp = client.send(req, HttpResponse.BodyHandlers.ofString()); out.println("Got response: " + resp); out.println("Got body: " + resp.body()); - assertEquals(resp.statusCode(), 200); - assertEquals(resp.body(), expectedMsg); + assertEquals(200, resp.statusCode()); + assertEquals(expectedMsg, resp.body()); } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { defaultFsPath = defaultFsFile(); zipFs = newZipFs(); zipFsPath = zipFsFile(zipFs); @@ -209,8 +210,8 @@ public class FilePublisherTest implements HttpServerAdapters { https2TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/FlowAdapterPublisherTest.java b/test/jdk/java/net/httpclient/FlowAdapterPublisherTest.java index ef9dc7b3077..7fd3061aa3c 100644 --- a/test/jdk/java/net/httpclient/FlowAdapterPublisherTest.java +++ b/test/jdk/java/net/httpclient/FlowAdapterPublisherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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,10 +42,6 @@ import java.net.http.HttpResponse; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import static java.net.http.HttpOption.H3_DISCOVERY; @@ -53,8 +49,14 @@ import static java.util.stream.Collectors.joining; import static java.nio.charset.StandardCharsets.UTF_8; import static java.net.http.HttpRequest.BodyPublishers.fromPublisher; import static java.net.http.HttpResponse.BodyHandlers.ofString; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -62,25 +64,24 @@ import static org.testng.Assert.fail; * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.internal.httpclient.debug=err FlowAdapterPublisherTest + * @run junit/othervm -Djdk.internal.httpclient.debug=err FlowAdapterPublisherTest */ public class FlowAdapterPublisherTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; - @DataProvider(name = "uris") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI }, { httpsURI }, @@ -120,7 +121,8 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { return builder; } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testByteBufferPublisherUnknownLength(String uri) { String[] body = new String[] { "You know ", "it's summer ", "in Ireland ", "when the ", "rain gets ", "warmer." }; @@ -131,13 +133,14 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { HttpResponse response = client.sendAsync(request, ofString(UTF_8)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, Arrays.stream(body).collect(joining())); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals(Arrays.stream(body).collect(joining()), text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testByteBufferPublisherFixedLength(String uri) { String[] body = new String[] { "You know ", "it's summer ", "in Ireland ", "when the ", "rain gets ", "warmer." }; @@ -149,15 +152,16 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { HttpResponse response = client.sendAsync(request, ofString(UTF_8)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, Arrays.stream(body).collect(joining())); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals(Arrays.stream(body).collect(joining()), text); } } // Flow.Publisher - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testMappedByteBufferPublisherUnknownLength(String uri) { String[] body = new String[] { "God invented ", "whiskey to ", "keep the ", "Irish from ", "ruling the ", "world." }; @@ -168,13 +172,14 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { HttpResponse response = client.sendAsync(request, ofString(UTF_8)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, Arrays.stream(body).collect(joining())); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals(Arrays.stream(body).collect(joining()), text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testMappedByteBufferPublisherFixedLength(String uri) { String[] body = new String[] { "God invented ", "whiskey to ", "keep the ", "Irish from ", "ruling the ", "world." }; @@ -186,9 +191,9 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { HttpResponse response = client.sendAsync(request, ofString(UTF_8)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, Arrays.stream(body).collect(joining())); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals(Arrays.stream(body).collect(joining()), text); } } @@ -196,7 +201,8 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { // not ideal, but necessary to discern correct behavior. They should be // updated if the exception message is updated. - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testPublishTooFew(String uri) throws InterruptedException { String[] body = new String[] { "You know ", "it's summer ", "in Ireland ", "when the ", "rain gets ", "warmer." }; @@ -214,7 +220,8 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testPublishTooMany(String uri) throws InterruptedException { String[] body = new String[] { "You know ", "it's summer ", "in Ireland ", "when the ", "rain gets ", "warmer." }; @@ -356,8 +363,8 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(Version.HTTP_1_1); httpTestServer.addHandler(new HttpEchoHandler(), "/http1/echo"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/echo"; @@ -385,8 +392,8 @@ public class FlowAdapterPublisherTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/FlowAdapterSubscriberTest.java b/test/jdk/java/net/httpclient/FlowAdapterSubscriberTest.java index 0e175cf0f52..32003023d7f 100644 --- a/test/jdk/java/net/httpclient/FlowAdapterSubscriberTest.java +++ b/test/jdk/java/net/httpclient/FlowAdapterSubscriberTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -53,17 +53,19 @@ import java.net.http.HttpResponse.BodySubscribers; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -72,22 +74,22 @@ import static org.testng.Assert.assertTrue; * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.internal.httpclient.debug=true FlowAdapterSubscriberTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true FlowAdapterSubscriberTest */ public class FlowAdapterSubscriberTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); @@ -101,8 +103,7 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - @DataProvider(name = "uris") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI }, { httpsURI }, @@ -154,7 +155,7 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { assertThrows(NPE, () -> BodySubscribers.fromSubscriber(new ListSubscriber(), null)); assertThrows(NPE, () -> BodySubscribers.fromSubscriber(null, null)); - Subscriber subscriber = BodySubscribers.fromSubscriber(new ListSubscriber()); + Subscriber subscriber = BodySubscribers.fromSubscriber(new ListSubscriber()); assertThrows(NPE, () -> subscriber.onSubscribe(null)); assertThrows(NPE, () -> subscriber.onNext(null)); assertThrows(NPE, () -> subscriber.onError(null)); @@ -162,7 +163,8 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { // List - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testListWithFinisher(String uri) { System.out.printf(now() + "testListWithFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -174,13 +176,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, Supplier::get)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "May the luck of the Irish be with you!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("May the luck of the Irish be with you!", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testListWithoutFinisher(String uri) { System.out.printf(now() + "testListWithoutFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -192,13 +195,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)).join(); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "May the luck of the Irish be with you!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("May the luck of the Irish be with you!", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testListWithFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testListWithFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -210,13 +214,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, Supplier::get)); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "May the luck of the Irish be with you!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("May the luck of the Irish be with you!", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testListWithoutFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testListWithoutFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -228,15 +233,16 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "May the luck of the Irish be with you!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("May the luck of the Irish be with you!", text); } } // Collection - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testCollectionWithFinisher(String uri) { System.out.printf(now() + "testCollectionWithFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -248,13 +254,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, CollectionSubscriber::get)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "What's the craic?"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("What's the craic?", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testCollectionWithoutFinisher(String uri) { System.out.printf(now() + "testCollectionWithoutFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -266,13 +273,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)).join(); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "What's the craic?"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("What's the craic?", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testCollectionWithFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testCollectionWithFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -284,13 +292,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, CollectionSubscriber::get)); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "What's the craic?"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("What's the craic?", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testCollectionWithoutFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testCollectionWithoutFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -302,15 +311,16 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "What's the craic?"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("What's the craic?", text); } } // Iterable - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testIterableWithFinisher(String uri) { System.out.printf(now() + "testIterableWithFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -322,13 +332,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, Supplier::get)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "We're sucking diesel now!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("We're sucking diesel now!", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testIterableWithoutFinisher(String uri) { System.out.printf(now() + "testIterableWithoutFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -340,13 +351,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)).join(); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "We're sucking diesel now!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("We're sucking diesel now!", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testIterableWithFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testIterableWithFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -358,13 +370,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, Supplier::get)); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "We're sucking diesel now!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("We're sucking diesel now!", text); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testIterableWithoutFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testIterableWithoutFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -376,15 +389,16 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); - assertEquals(text, "We're sucking diesel now!"); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); + assertEquals("We're sucking diesel now!", text); } } // Subscriber - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithFinisher(String uri) { System.out.printf(now() + "testObjectWithFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -396,13 +410,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, ObjectSubscriber::get)).join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); assertTrue(text.length() != 0); // what else can be asserted! } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithoutFinisher(String uri) { System.out.printf(now() + "testObjectWithoutFinisher(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -414,13 +429,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)).join(); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); assertTrue(text.length() != 0); // what else can be asserted! } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testObjectWithFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -432,13 +448,14 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber, ObjectSubscriber::get)); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); assertTrue(text.length() != 0); // what else can be asserted! } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithoutFinisherBlocking(String uri) throws Exception { System.out.printf(now() + "testObjectWithoutFinisherBlocking(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -450,8 +467,8 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { BodyHandlers.fromSubscriber(subscriber)); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uri)); + assertEquals(200, response.statusCode()); + assertEquals(version(uri), response.version()); assertTrue(text.length() != 0); // what else can be asserted! } } @@ -459,7 +476,8 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { // -- mapping using convenience handlers - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void mappingFromByteArray(String uri) throws Exception { System.out.printf(now() + "mappingFromByteArray(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -470,12 +488,13 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { bas -> new String(bas.getBody().toCompletableFuture().join(), UTF_8))) .thenApply(FlowAdapterSubscriberTest::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, "We're sucking diesel now!")) + .thenAccept(body -> assertEquals("We're sucking diesel now!", body)) .join(); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void mappingFromInputStream(String uri) throws Exception { System.out.printf(now() + "mappingFromInputStream(%s) starting%n", uri); try (HttpClient client = newHttpClient(uri)) { @@ -512,7 +531,7 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { finisher)) .thenApply(FlowAdapterSubscriberTest::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, "May the wind always be at your back.")) + .thenAccept(body -> assertEquals("May the wind always be at your back.", body)) .join(); var error = failed.get(); if (error != null) throw error; @@ -626,13 +645,13 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { } static final HttpResponse assert200ResponseCode(HttpResponse response) { - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(response.request().uri().toString())); + assertEquals(200, response.statusCode()); + assertEquals(version(response.request().uri().toString()), response.version()); return response; } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(Version.HTTP_1_1); httpTestServer.addHandler(new HttpEchoHandler(), "/http1/echo"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/echo"; @@ -660,8 +679,8 @@ public class FlowAdapterSubscriberTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/GZIPInputStreamTest.java b/test/jdk/java/net/httpclient/GZIPInputStreamTest.java index 948ecd4d7cc..1cbb24d81ce 100644 --- a/test/jdk/java/net/httpclient/GZIPInputStreamTest.java +++ b/test/jdk/java/net/httpclient/GZIPInputStreamTest.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 @@ -27,15 +27,11 @@ * @summary Tests that you can map an InputStream to a GZIPInputStream * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters ReferenceTracker - * @run testng/othervm GZIPInputStreamTest + * @run junit/othervm GZIPInputStreamTest */ import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -46,7 +42,6 @@ import java.net.InetAddress; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; -import java.net.http.HttpOption.Http3DiscoveryMode; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodyHandlers; @@ -68,21 +63,26 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class GZIPInputStreamTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer https3TestServer; // HTTP/3 - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String https3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer https3TestServer; // HTTP/3 + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String https3URI; static final int ITERATION_COUNT = 3; // a shared executor helps reduce the amount of threads created by the test @@ -154,8 +154,7 @@ public class GZIPInputStreamTest implements HttpServerAdapters { - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI, false }, { httpURI, true }, @@ -170,7 +169,7 @@ public class GZIPInputStreamTest implements HttpServerAdapters { }; } - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; HttpClient newHttpClient() { return TRACKER.track(newClientBuilderForH3() .executor(executor) @@ -192,7 +191,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { .build()); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testPlainSyncAsString(String uri, boolean sameClient) throws Exception { out.println("\nSmoke test: verify that the result we get from the server is correct."); out.println("Uses plain send() and `asString` to get the plain string."); @@ -209,7 +209,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testPlainSyncAsInputStream(String uri, boolean sameClient) throws Exception { out.println("Uses plain send() and `asInputStream` - calls readAllBytes() from main thread"); out.println("Uses single threaded executor"); @@ -225,7 +226,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testGZIPSyncAsInputStream(String uri, boolean sameClient) throws Exception { out.println("Uses plain send() and `asInputStream` - " + "creates GZIPInputStream and calls readAllBytes() from main thread"); @@ -243,7 +245,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testGZIPSyncAsGZIPInputStream(String uri, boolean sameClient) throws Exception { out.println("Uses plain send() and a mapping subscriber to "+ "create the GZIPInputStream. Calls readAllBytes() from main thread"); @@ -262,7 +265,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testGZIPSyncAsGZIPInputStreamSupplier(String uri, boolean sameClient) throws Exception { out.println("Uses plain send() and a mapping subscriber to "+ "create a Supplier. Calls Supplier.get() " + @@ -276,7 +280,7 @@ public class GZIPInputStreamTest implements HttpServerAdapters { HttpRequest req = buildRequest(URI.create(uri + "/gz/LoremIpsum.txt.gz")); // This is dangerous, because the finisher will block. // We support this, but the executor must have enough threads. - BodyHandler> handler = new BodyHandler>() { + BodyHandler> handler = new BodyHandler<>() { public HttpResponse.BodySubscriber> apply( HttpResponse.ResponseInfo responseInfo) { @@ -304,7 +308,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testPlainAsyncAsInputStreamBlocks(String uri, boolean sameClient) throws Exception { out.println("Uses sendAsync() and `asInputStream`. Registers a dependent action "+ "that calls readAllBytes()"); @@ -331,7 +336,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testGZIPAsyncAsGZIPInputStreamBlocks(String uri, boolean sameClient) throws Exception { out.println("Uses sendAsync() and a mapping subscriber to create a GZIPInputStream. " + "Registers a dependent action that calls readAllBytes()"); @@ -358,7 +364,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testGZIPSyncAsGZIPInputStreamBlocks(String uri, boolean sameClient) throws Exception { out.println("Uses sendAsync() and a mapping subscriber to create a GZIPInputStream," + "which is mapped again using a mapping subscriber " + @@ -386,7 +393,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testGZIPSyncAsGZIPInputStreamSupplierInline(String uri, boolean sameClient) throws Exception { out.println("Uses plain send() and a mapping subscriber to "+ "create a Supplier. Calls Supplier.get() " + @@ -432,7 +440,7 @@ public class GZIPInputStreamTest implements HttpServerAdapters { if (!LOREM_IPSUM.equals(responseBody)) { out.println("Response doesn't match"); out.println("[" + LOREM_IPSUM + "] != [" + responseBody + "]"); - assertEquals(LOREM_IPSUM, responseBody); + assertEquals(responseBody, LOREM_IPSUM); } else { out.println("Received expected response."); } @@ -487,8 +495,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { + server.getAddress().getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { HttpTestHandler plainHandler = new LoremIpsumPlainHandler(); HttpTestHandler gzipHandler = new LoremIpsumGZIPHandler(); @@ -526,8 +534,8 @@ public class GZIPInputStreamTest implements HttpServerAdapters { https3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.check(500); try { diff --git a/test/jdk/java/net/httpclient/HeadTest.java b/test/jdk/java/net/httpclient/HeadTest.java index 3e05442faf5..3a52cd101b8 100644 --- a/test/jdk/java/net/httpclient/HeadTest.java +++ b/test/jdk/java/net/httpclient/HeadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,14 +27,10 @@ * @summary Tests Client handles HEAD and 304 responses correctly. * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.httpclient.HttpClient.log=trace,headers,requests HeadTest + * @run junit/othervm -Djdk.httpclient.HttpClient.log=trace,headers,requests HeadTest */ import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -55,19 +51,24 @@ import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static jdk.httpclient.test.lib.common.HttpServerAdapters.createClientBuilderForH3; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class HeadTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer https3TestServer; // HTTP/3 - String httpURI, httpsURI; - String http2URI, https2URI; - String https3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer https3TestServer; // HTTP/3 + private static String httpURI, httpsURI; + private static String http2URI, https2URI; + private static String https3URI; static final String CONTENT_LEN = "300"; @@ -80,8 +81,7 @@ public class HeadTest implements HttpServerAdapters { static final int HTTP_OK = 200; static final PrintStream out = System.out; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { // HTTP/1.1 { httpURI, "GET", HTTP_NOT_MODIFIED, HTTP_1_1 }, @@ -103,7 +103,8 @@ public class HeadTest implements HttpServerAdapters { }; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString, String method, int expResp, Version version) throws Exception { out.printf("%n---- starting (%s) ----%n", uriString); @@ -136,17 +137,17 @@ public class HeadTest implements HttpServerAdapters { out.println(" Got response: " + response); - assertEquals(response.statusCode(), expResp); - assertEquals(response.body(), ""); - assertEquals(response.headers().firstValue("Content-length").get(), CONTENT_LEN); - assertEquals(response.version(), request.version().get()); + assertEquals(expResp, response.statusCode()); + assertEquals("", response.body()); + assertEquals(CONTENT_LEN, response.headers().firstValue("Content-length").get()); + assertEquals(request.version().get(), response.version()); } } // -- Infrastructure // TODO: See if test performs better with Vthreads, see H3SimplePost and H3SimpleGet - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new HeadHandler(), "/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/"; @@ -173,8 +174,8 @@ public class HeadTest implements HttpServerAdapters { https3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/HeadersTest1.java b/test/jdk/java/net/httpclient/HeadersTest1.java index 79932dfc92f..26733c1c928 100644 --- a/test/jdk/java/net/httpclient/HeadersTest1.java +++ b/test/jdk/java/net/httpclient/HeadersTest1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 8153142 8195138 * @modules java.net.http * jdk.httpserver - * @run testng/othervm HeadersTest1 + * @run junit/othervm HeadersTest1 */ import java.io.IOException; @@ -48,13 +48,13 @@ import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; -import org.testng.annotations.Test; import static java.net.http.HttpResponse.BodyHandlers.ofString; import static java.nio.charset.StandardCharsets.US_ASCII; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; public class HeadersTest1 { @@ -106,7 +106,7 @@ public class HeadersTest1 { for (String headerName : headernames) { List v2 = hd.allValues(headerName); assertNotNull(v2); - assertEquals(new HashSet<>(v2), Set.of("resp1", "resp2")); + assertEquals(Set.of("resp1", "resp2"), new HashSet<>(v2)); TestKit.assertUnmodifiableList(v2); } @@ -130,7 +130,7 @@ public class HeadersTest1 { // quote List quote = hd.allValues("X-Quote-Response"); - assertEquals(quote, List.of(QUOTED)); + assertEquals(List.of(QUOTED), quote); } finally { server.stop(0); e.shutdownNow(); diff --git a/test/jdk/java/net/httpclient/HttpClientBuilderTest.java b/test/jdk/java/net/httpclient/HttpClientBuilderTest.java index 9f7d2ade62f..a5fb1a39e66 100644 --- a/test/jdk/java/net/httpclient/HttpClientBuilderTest.java +++ b/test/jdk/java/net/httpclient/HttpClientBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -51,9 +51,10 @@ import java.net.http.HttpClient.Version; import java.util.concurrent.atomic.AtomicInteger; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.Test; import static java.time.Duration.*; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /* * @test @@ -61,7 +62,7 @@ import static org.testng.Assert.*; * @summary HttpClient[.Builder] API and behaviour checks * @library /test/lib * @build jdk.test.lib.net.SimpleSSLContext - * @run testng HttpClientBuilderTest + * @run junit HttpClientBuilderTest */ public class HttpClientBuilderTest { @@ -83,10 +84,10 @@ public class HttpClientBuilderTest { assertFalse(client.connectTimeout().isPresent()); assertFalse(client.executor().isPresent()); assertFalse(client.proxy().isPresent()); - assertTrue(client.sslParameters() != null); - assertTrue(client.followRedirects().equals(HttpClient.Redirect.NEVER)); - assertTrue(client.sslContext() == SSLContext.getDefault()); - assertTrue(client.version().equals(HttpClient.Version.HTTP_2)); + assertNotNull(client.sslParameters()); + assertEquals(Redirect.NEVER, client.followRedirects()); + assertSame(SSLContext.getDefault(), client.sslContext()); + assertEquals(Version.HTTP_2, client.version()); } } } @@ -133,18 +134,18 @@ public class HttpClientBuilderTest { Authenticator a = new TestAuthenticator(); builder.authenticator(a); try (var closer = closeable(builder)) { - assertTrue(closer.build().authenticator().get() == a); + assertSame(a, closer.build().authenticator().get()); } Authenticator b = new TestAuthenticator(); builder.authenticator(b); try (var closer = closeable(builder)) { - assertTrue(closer.build().authenticator().get() == b); + assertSame(b, closer.build().authenticator().get()); } assertThrows(NPE, () -> builder.authenticator(null)); Authenticator c = new TestAuthenticator(); builder.authenticator(c); try (var closer = closeable(builder)) { - assertTrue(closer.build().authenticator().get() == c); + assertSame(c, closer.build().authenticator().get()); } } @@ -154,18 +155,18 @@ public class HttpClientBuilderTest { CookieHandler a = new CookieManager(); builder.cookieHandler(a); try (var closer = closeable(builder)) { - assertTrue(closer.build().cookieHandler().get() == a); + assertSame(a, closer.build().cookieHandler().get()); } CookieHandler b = new CookieManager(); builder.cookieHandler(b); try (var closer = closeable(builder)) { - assertTrue(closer.build().cookieHandler().get() == b); + assertSame(b, closer.build().cookieHandler().get()); } assertThrows(NPE, () -> builder.cookieHandler(null)); CookieManager c = new CookieManager(); builder.cookieHandler(c); try (var closer = closeable(builder)) { - assertTrue(closer.build().cookieHandler().get() == c); + assertSame(c, closer.build().cookieHandler().get()); } } @@ -175,18 +176,18 @@ public class HttpClientBuilderTest { Duration a = Duration.ofSeconds(5); builder.connectTimeout(a); try (var closer = closeable(builder)) { - assertTrue(closer.build().connectTimeout().get() == a); + assertSame(a, closer.build().connectTimeout().get()); } Duration b = Duration.ofMinutes(1); builder.connectTimeout(b); try (var closer = closeable(builder)) { - assertTrue(closer.build().connectTimeout().get() == b); + assertSame(b, closer.build().connectTimeout().get()); } assertThrows(NPE, () -> builder.cookieHandler(null)); Duration c = Duration.ofHours(100); builder.connectTimeout(c); try (var closer = closeable(builder)) { - assertTrue(closer.build().connectTimeout().get() == c); + assertSame(c, closer.build().connectTimeout().get()); } assertThrows(IAE, () -> builder.connectTimeout(ZERO)); @@ -205,18 +206,18 @@ public class HttpClientBuilderTest { TestExecutor a = new TestExecutor(); builder.executor(a); try (var closer = closeable(builder)) { - assertTrue(closer.build().executor().get() == a); + assertSame(a, closer.build().executor().get()); } TestExecutor b = new TestExecutor(); builder.executor(b); try (var closer = closeable(builder)) { - assertTrue(closer.build().executor().get() == b); + assertSame(b, closer.build().executor().get()); } assertThrows(NPE, () -> builder.executor(null)); TestExecutor c = new TestExecutor(); builder.executor(c); try (var closer = closeable(builder)) { - assertTrue(closer.build().executor().get() == c); + assertSame(c, closer.build().executor().get()); } } @@ -226,18 +227,18 @@ public class HttpClientBuilderTest { ProxySelector a = ProxySelector.of(null); builder.proxy(a); try (var closer = closeable(builder)) { - assertTrue(closer.build().proxy().get() == a); + assertSame(a, closer.build().proxy().get()); } ProxySelector b = ProxySelector.of(InetSocketAddress.createUnresolved("foo", 80)); builder.proxy(b); try (var closer = closeable(builder)) { - assertTrue(closer.build().proxy().get() == b); + assertSame(b, closer.build().proxy().get()); } assertThrows(NPE, () -> builder.proxy(null)); ProxySelector c = ProxySelector.of(InetSocketAddress.createUnresolved("bar", 80)); builder.proxy(c); try (var closer = closeable(builder)) { - assertTrue(closer.build().proxy().get() == c); + assertSame(c, closer.build().proxy().get()); } } @@ -249,16 +250,16 @@ public class HttpClientBuilderTest { builder.sslParameters(a); a.setCipherSuites(new String[] { "Z" }); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslParameters() != (a)); + assertNotSame(a, closer.build().sslParameters()); } try (var closer = closeable(builder)) { - assertTrue(closer.build().sslParameters().getCipherSuites()[0].equals("A")); + assertEquals("A", closer.build().sslParameters().getCipherSuites()[0]); } SSLParameters b = new SSLParameters(); b.setEnableRetransmissions(true); builder.sslParameters(b); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslParameters() != b); + assertNotSame(b, closer.build().sslParameters()); } try (var closer = closeable(builder)) { assertTrue(closer.build().sslParameters().getEnableRetransmissions()); @@ -269,21 +270,21 @@ public class HttpClientBuilderTest { builder.sslParameters(c); c.setProtocols(new String[] { "D" }); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslParameters().getProtocols()[0].equals("C")); + assertEquals("C", closer.build().sslParameters().getProtocols()[0]); } SSLParameters d = new SSLParameters(); d.setSignatureSchemes(new String[] { "C" }); builder.sslParameters(d); d.setSignatureSchemes(new String[] { "D" }); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslParameters().getSignatureSchemes()[0].equals("C")); + assertEquals("C", closer.build().sslParameters().getSignatureSchemes()[0]); } SSLParameters e = new SSLParameters(); e.setNamedGroups(new String[] { "C" }); builder.sslParameters(e); e.setNamedGroups(new String[] { "D" }); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslParameters().getNamedGroups()[0].equals("C")); + assertEquals("C", closer.build().sslParameters().getNamedGroups()[0]); } // test defaults for needClientAuth and wantClientAuth builder.sslParameters(new SSLParameters()); @@ -321,18 +322,18 @@ public class HttpClientBuilderTest { SSLContext a = SimpleSSLContext.findSSLContext(); builder.sslContext(a); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslContext() == a); + assertSame(a, closer.build().sslContext()); } SSLContext b = SimpleSSLContext.findSSLContext(); builder.sslContext(b); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslContext() == b); + assertSame(b, closer.build().sslContext()); } assertThrows(NPE, () -> builder.sslContext(null)); SSLContext c = SimpleSSLContext.findSSLContext(); builder.sslContext(c); try (var closer = closeable(builder)) { - assertTrue(closer.build().sslContext() == c); + assertSame(c, closer.build().sslContext()); } } @@ -341,16 +342,16 @@ public class HttpClientBuilderTest { HttpClient.Builder builder = HttpClient.newBuilder(); builder.followRedirects(Redirect.ALWAYS); try (var closer = closeable(builder)) { - assertTrue(closer.build().followRedirects() == Redirect.ALWAYS); + assertSame(Redirect.ALWAYS, closer.build().followRedirects()); } builder.followRedirects(Redirect.NEVER); try (var closer = closeable(builder)) { - assertTrue(closer.build().followRedirects() == Redirect.NEVER); + assertSame(Redirect.NEVER, closer.build().followRedirects()); } assertThrows(NPE, () -> builder.followRedirects(null)); builder.followRedirects(Redirect.NORMAL); try (var closer = closeable(builder)) { - assertTrue(closer.build().followRedirects() == Redirect.NORMAL); + assertSame(Redirect.NORMAL, closer.build().followRedirects()); } } @@ -358,37 +359,37 @@ public class HttpClientBuilderTest { public void testVersion() { HttpClient.Builder builder = HttpClient.newBuilder(); try (var closer = closeable(builder)) { - assertTrue(closer.build().version() == Version.HTTP_2); + assertSame(Version.HTTP_2, closer.build().version()); } builder.version(Version.HTTP_3); try (var closer = closeable(builder)) { - assertTrue(closer.build().version() == Version.HTTP_3); + assertSame(Version.HTTP_3, closer.build().version()); } builder.version(Version.HTTP_2); try (var closer = closeable(builder)) { - assertTrue(closer.build().version() == Version.HTTP_2); + assertSame(Version.HTTP_2, closer.build().version()); } builder.version(Version.HTTP_1_1); try (var closer = closeable(builder)) { - assertTrue(closer.build().version() == Version.HTTP_1_1); + assertSame(Version.HTTP_1_1, closer.build().version()); } assertThrows(NPE, () -> builder.version(null)); builder.version(Version.HTTP_3); try (var closer = closeable(builder)) { - assertTrue(closer.build().version() == Version.HTTP_3); + assertSame(Version.HTTP_3, closer.build().version()); } builder.version(Version.HTTP_2); try (var closer = closeable(builder)) { - assertTrue(closer.build().version() == Version.HTTP_2); + assertSame(Version.HTTP_2, closer.build().version()); } builder.version(Version.HTTP_1_1); try (var closer = closeable(builder)) { - assertTrue(closer.build().version() == Version.HTTP_1_1); + assertSame(Version.HTTP_1_1, closer.build().version()); } } @Test - static void testPriority() throws Exception { + void testPriority() throws Exception { HttpClient.Builder builder = HttpClient.newBuilder(); assertThrows(IAE, () -> builder.priority(-1)); assertThrows(IAE, () -> builder.priority(0)); @@ -489,7 +490,7 @@ public class HttpClientBuilderTest { static final URI uri = URI.create("http://foo.com/"); @Test - static void testHttpClientSendArgs() throws Exception { + void testHttpClientSendArgs() throws Exception { try (HttpClient client = HttpClient.newHttpClient()) { HttpRequest request = HttpRequest.newBuilder(uri).build(); @@ -527,21 +528,21 @@ public class HttpClientBuilderTest { // --- @Test - static void testUnsupportedWebSocket() throws Exception { + void testUnsupportedWebSocket() throws Exception { // @implSpec The default implementation of this method throws // {@code UnsupportedOperationException}. assertThrows(UOE, () -> (new MockHttpClient()).newWebSocketBuilder()); } @Test - static void testDefaultShutdown() throws Exception { + void testDefaultShutdown() throws Exception { try (HttpClient client = new MockHttpClient()) { client.shutdown(); // does nothing } } @Test - static void testDefaultShutdownNow() throws Exception { + void testDefaultShutdownNow() throws Exception { try (HttpClient client = new MockHttpClient()) { client.shutdownNow(); // calls shutdown, doesn't wait } @@ -559,18 +560,18 @@ public class HttpClientBuilderTest { } // once from shutdownNow(), and once from close() - assertEquals(shutdownCalled.get(), 2); + assertEquals(2, shutdownCalled.get()); } @Test - static void testDefaultIsTerminated() throws Exception { + void testDefaultIsTerminated() throws Exception { try (HttpClient client = new MockHttpClient()) { assertFalse(client.isTerminated()); } } @Test - static void testDefaultAwaitTermination() throws Exception { + void testDefaultAwaitTermination() throws Exception { try (HttpClient client = new MockHttpClient()) { assertTrue(client.awaitTermination(Duration.ofDays(1))); } @@ -581,7 +582,7 @@ public class HttpClientBuilderTest { } @Test - static void testDefaultClose() { + void testDefaultClose() { AtomicInteger shutdownCalled = new AtomicInteger(); AtomicInteger awaitTerminationCalled = new AtomicInteger(); AtomicInteger shutdownNowCalled = new AtomicInteger(); @@ -616,9 +617,9 @@ public class HttpClientBuilderTest { // awaitTermination() 0->1 -> false // awaitTermination() 1->2 -> true try (HttpClient client = mock) { } - assertEquals(shutdownCalled.get(), 1); // called by close() - assertEquals(shutdownNowCalled.get(), 0); // not called - assertEquals(awaitTerminationCalled.get(), 2); // called by close() twice + assertEquals(1, shutdownCalled.get()); // called by close() + assertEquals(0, shutdownNowCalled.get()); // not called + assertEquals(2, awaitTerminationCalled.get()); // called by close() twice assertFalse(Thread.currentThread().isInterrupted()); // second time around: @@ -629,9 +630,9 @@ public class HttpClientBuilderTest { // calls shutdown() 2->3 // awaitTermination() 3->4 -> true try (HttpClient client = mock) { } - assertEquals(shutdownCalled.get(), 3); // called by close() and shutdownNow() - assertEquals(shutdownNowCalled.get(), 1); // called by close() due to interrupt - assertEquals(awaitTerminationCalled.get(), 4); // called by close twice + assertEquals(3, shutdownCalled.get()); // called by close() and shutdownNow() + assertEquals(1, shutdownNowCalled.get()); // called by close() due to interrupt + assertEquals(4, awaitTerminationCalled.get()); // called by close twice assertTrue(Thread.currentThread().isInterrupted()); assertTrue(Thread.interrupted()); } diff --git a/test/jdk/java/net/httpclient/HttpClientClose.java b/test/jdk/java/net/httpclient/HttpClientClose.java index e71faa8fe4e..0a3fc03424a 100644 --- a/test/jdk/java/net/httpclient/HttpClientClose.java +++ b/test/jdk/java/net/httpclient/HttpClientClose.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 @@ -31,7 +31,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * HttpClientClose @@ -70,10 +70,6 @@ import javax.net.ssl.SSLContext; import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; @@ -84,10 +80,15 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class HttpClientClose implements HttpServerAdapters { @@ -96,27 +97,26 @@ public class HttpClientClose implements HttpServerAdapters { } static final Random RANDOM = RandomFactory.getRandom(); - ExecutorService readerService; + private static ExecutorService readerService; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) - HttpTestServer h3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String h2h3URI; - String h2h3Head; - String h3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) + private static HttpTestServer h3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String h2h3URI; + private static String h2h3Head; + private static String h3URI; static final String MESSAGE = "HttpClientClose message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { h2h3URI, HTTP_3, h2h3TestServer.h3DiscoveryConfig()}, { h3URI, HTTP_3, h3TestServer.h3DiscoveryConfig()}, @@ -128,7 +128,7 @@ public class HttpClientClose implements HttpServerAdapters { } static final AtomicLong requestCounter = new AtomicLong(); - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; static String readBody(InputStream body) { try (InputStream in = body) { @@ -138,7 +138,7 @@ public class HttpClientClose implements HttpServerAdapters { } } - private static record CancellingSubscriber(ExchangeResult result) + private record CancellingSubscriber(ExchangeResult result) implements Subscriber { @Override public void onSubscribe(Subscription subscription) { @@ -176,15 +176,15 @@ public class HttpClientClose implements HttpServerAdapters { boolean firstVersionMayNotMatch) { static ExchangeResult afterHead(int step, Version version, Http3DiscoveryMode config) { - return new ExchangeResult(step, version, config, null, false); + return new ExchangeResult<>(step, version, config, null, false); } static ExchangeResult ofSequential(int step, Version version, Http3DiscoveryMode config) { - return new ExchangeResult(step, version, config, null, true); + return new ExchangeResult<>(step, version, config, null, true); } ExchangeResult withResponse(HttpResponse response) { - return new ExchangeResult(step(), version(), config(), response, firstVersionMayNotMatch()); + return new ExchangeResult<>(step(), version(), config(), response, firstVersionMayNotMatch()); } // Ensures that the input stream gets closed in case of assertion @@ -193,11 +193,11 @@ public class HttpClientClose implements HttpServerAdapters { try { out.printf("%s: expect status 200 and version %s (%s) for %s%n", step, version, config, response.request().uri()); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (step == 0 && version == HTTP_3 && firstVersionMayNotMatch) { out.printf("%s: version not checked%n", step); } else { - assertEquals(response.version(), version); + assertEquals(version, response.version()); out.printf("%s: got expected version %s%n", step, response.version()); } } catch (AssertionError error) { @@ -227,10 +227,11 @@ public class HttpClientClose implements HttpServerAdapters { .HEAD() .build(); var resp = client.send(request, BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testConcurrent(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting concurrent (%s, %s, %s) ----%n%n", uriString, version, config); Throwable failed = null; @@ -266,7 +267,7 @@ public class HttpClientClose implements HttpServerAdapters { bodyCF = responseCF .thenApplyAsync((resp) -> readBody(si, resp), readerService) .thenApply((s) -> { - assertEquals(s, MESSAGE); + assertEquals(MESSAGE, s); return s; }); long sleep = RANDOM.nextLong(5); @@ -289,7 +290,8 @@ public class HttpClientClose implements HttpServerAdapters { failed == null ? "done" : failed.toString()); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testSequential(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting sequential (%s, %s, %s) ----%n%n", uriString, version, config); Throwable failed = null; @@ -320,7 +322,7 @@ public class HttpClientClose implements HttpServerAdapters { bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) .thenApply(HttpClientClose::readBody) .thenApply((s) -> { - assertEquals(s, MESSAGE); + assertEquals(MESSAGE, s); return s; }) .thenApply((s) -> { @@ -340,8 +342,8 @@ public class HttpClientClose implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println("\n**** Setup ****\n"); readerService = Executors.newCachedThreadPool(); @@ -376,8 +378,8 @@ public class HttpClientClose implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.checkShutdown(5000); try { diff --git a/test/jdk/java/net/httpclient/HttpClientExceptionTest.java b/test/jdk/java/net/httpclient/HttpClientExceptionTest.java index f9c585d955e..dff7219c0c8 100644 --- a/test/jdk/java/net/httpclient/HttpClientExceptionTest.java +++ b/test/jdk/java/net/httpclient/HttpClientExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -21,8 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.Test; import java.io.IOException; import java.net.ProtocolFamily; import java.net.http.HttpClient; @@ -34,12 +32,15 @@ import java.nio.channels.Channel; import java.nio.channels.spi.AbstractSelector; import java.nio.channels.spi.SelectorProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + /* * @test * @bug 8248006 * @summary The test checks if UncheckedIOException is thrown * @build HttpClientExceptionTest - * @run testng/othervm -Djava.nio.channels.spi.SelectorProvider=HttpClientExceptionTest$CustomSelectorProvider + * @run junit/othervm -Djava.nio.channels.spi.SelectorProvider=HttpClientExceptionTest$CustomSelectorProvider * HttpClientExceptionTest */ @@ -50,8 +51,8 @@ public class HttpClientExceptionTest { @Test public void testHttpClientException() { for(int i = 0; i < ITERATIONS; i++) { - Assert.assertThrows(HttpClient.newBuilder()::build); - Assert.assertThrows(HttpClient::newHttpClient); + Assertions.assertThrows(Throwable.class, HttpClient.newBuilder()::build); + Assertions.assertThrows(Throwable.class, HttpClient::newHttpClient); } } diff --git a/test/jdk/java/net/httpclient/HttpClientLocalAddrTest.java b/test/jdk/java/net/httpclient/HttpClientLocalAddrTest.java index 10cca697231..f1e8aecbaa4 100644 --- a/test/jdk/java/net/httpclient/HttpClientLocalAddrTest.java +++ b/test/jdk/java/net/httpclient/HttpClientLocalAddrTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -23,11 +23,6 @@ import jdk.test.lib.net.IPSupport; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.Closeable; @@ -50,6 +45,12 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* * @test * @summary Tests HttpClient usage when configured with a local address to bind @@ -60,7 +61,7 @@ import static java.net.http.HttpClient.Version.HTTP_2; * @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.IPSupport * jdk.httpclient.test.lib.common.HttpServerAdapters * - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=frames,ssl,requests,responses,errors * -Djdk.internal.httpclient.debug=true * -Dsun.net.httpserver.idleInterval=50000 @@ -81,7 +82,7 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { private static final AtomicInteger IDS = new AtomicInteger(); // start various HTTP/HTTPS servers that will be invoked against in the tests - @BeforeClass + @BeforeAll public static void beforeClass() throws Exception { HttpServerAdapters.HttpTestHandler handler = (exchange) -> { // the handler receives a request and sends back a 200 response with the @@ -126,7 +127,7 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { } // stop each of the started servers - @AfterClass + @AfterAll public static void afterClass() throws Exception { // stop each of the server and accumulate any exception // that might happen during stop and finally throw @@ -166,8 +167,7 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { return prevException; } - @DataProvider(name = "params") - private Object[][] paramsProvider() throws Exception { + private static Object[][] paramsProvider() throws Exception { final List testMethodParams = new ArrayList<>(); final URI[] requestURIs = new URI[]{httpURI, httpsURI, http2URI, https2URI}; final Predicate requiresSSLContext = (uri) -> uri.getScheme().equals("https"); @@ -224,7 +224,7 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { // An object that holds a client and that can be closed // Used when closing the client might require closing additional // resources, such as an executor - sealed interface ClientCloseable extends Closeable { + public sealed interface ClientCloseable extends Closeable { HttpClient client(); @@ -261,7 +261,7 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { } // A supplier of ClientCloseable - sealed interface ClientProvider extends Supplier { + public sealed interface ClientProvider extends Supplier { ClientCloseable get(); @@ -323,7 +323,8 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { * seen by the server side handler is the same one as that is set on the * {@code client} */ - @Test(dataProvider = "params") + @ParameterizedTest + @MethodSource("paramsProvider") public void testSend(ClientProvider clientProvider, URI requestURI, InetAddress localAddress) throws Exception { try (var c = clientProvider.get()) { HttpClient client = c.client(); @@ -332,10 +333,10 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { // GET request var req = HttpRequest.newBuilder(requestURI).build(); var resp = client.send(req, HttpResponse.BodyHandlers.ofByteArray()); - Assert.assertEquals(resp.statusCode(), 200, "Unexpected status code"); + Assertions.assertEquals(200, resp.statusCode(), "Unexpected status code"); // verify the address only if a specific one was set on the client if (localAddress != null && !localAddress.isAnyLocalAddress()) { - Assert.assertEquals(resp.body(), localAddress.getAddress(), + Assertions.assertArrayEquals(localAddress.getAddress(), resp.body(), "Unexpected client address seen by the server handler"); } } @@ -347,7 +348,8 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { * seen by the server side handler is the same one as that is set on the * {@code client} */ - @Test(dataProvider = "params") + @ParameterizedTest + @MethodSource("paramsProvider") public void testSendAsync(ClientProvider clientProvider, URI requestURI, InetAddress localAddress) throws Exception { try (var c = clientProvider.get()) { HttpClient client = c.client(); @@ -359,10 +361,10 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { var cf = client.sendAsync(req, HttpResponse.BodyHandlers.ofByteArray()); var resp = cf.get(); - Assert.assertEquals(resp.statusCode(), 200, "Unexpected status code"); + Assertions.assertEquals(200, resp.statusCode(), "Unexpected status code"); // verify the address only if a specific one was set on the client if (localAddress != null && !localAddress.isAnyLocalAddress()) { - Assert.assertEquals(resp.body(), localAddress.getAddress(), + Assertions.assertArrayEquals(localAddress.getAddress(), resp.body(), "Unexpected client address seen by the server handler"); } } @@ -374,7 +376,8 @@ public class HttpClientLocalAddrTest implements HttpServerAdapters { * is used when multiple concurrent threads are involved in sending requests from * the {@code client} */ - @Test(dataProvider = "params") + @ParameterizedTest + @MethodSource("paramsProvider") public void testMultiSendRequests(ClientProvider clientProvider, URI requestURI, InetAddress localAddress) throws Exception { diff --git a/test/jdk/java/net/httpclient/HttpClientShutdown.java b/test/jdk/java/net/httpclient/HttpClientShutdown.java index 4a5583782cb..8532146c1e2 100644 --- a/test/jdk/java/net/httpclient/HttpClientShutdown.java +++ b/test/jdk/java/net/httpclient/HttpClientShutdown.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 @@ -32,7 +32,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * HttpClientShutdown @@ -74,10 +74,6 @@ import javax.net.ssl.SSLContext; import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; @@ -88,10 +84,15 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class HttpClientShutdown implements HttpServerAdapters { @@ -100,27 +101,26 @@ public class HttpClientShutdown implements HttpServerAdapters { } static final Random RANDOM = RandomFactory.getRandom(); - ExecutorService readerService; + private static ExecutorService readerService; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) - HttpTestServer h3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String h2h3URI; - String h2h3Head; - String h3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) + private static HttpTestServer h3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String h2h3URI; + private static String h2h3Head; + private static String h3URI; static final String MESSAGE = "HttpClientShutdown message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { h2h3URI, HTTP_3, h2h3TestServer.h3DiscoveryConfig()}, { h3URI, HTTP_3, h3TestServer.h3DiscoveryConfig()}, @@ -216,11 +216,11 @@ public class HttpClientShutdown implements HttpServerAdapters { try { out.printf(now() + "%s: expect status 200 and version %s (%s) for %s%n", step, version, config, response.request().uri()); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (step == 0 && version == HTTP_3 && firstVersionMayNotMatch) { out.printf(now() + "%s: version not checked%n", step); } else { - assertEquals(response.version(), version); + assertEquals(version, response.version()); out.printf(now() + "%s: got expected version %s%n", step, response.version()); } } catch (AssertionError error) { @@ -238,7 +238,7 @@ public class HttpClientShutdown implements HttpServerAdapters { .HEAD() .build(); var resp = client.send(request, BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } static boolean hasExpectedMessage(IOException io) { @@ -272,7 +272,8 @@ public class HttpClientShutdown implements HttpServerAdapters { throw new AssertionError(what + ": Unexpected exception: " + cause, cause); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testConcurrent(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- %sstarting concurrent (%s, %s, %s) ----%n%n", now(), uriString, version, config); @@ -310,7 +311,7 @@ public class HttpClientShutdown implements HttpServerAdapters { bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) .thenApply(HttpClientShutdown::readBody) .thenApply((s) -> { - assertEquals(s, MESSAGE); + assertEquals(MESSAGE, s); out.println(now() + si +": Got expected message: " + s); return s; }); @@ -375,7 +376,8 @@ public class HttpClientShutdown implements HttpServerAdapters { return failed; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testSequential(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- %sstarting sequential (%s, %s, %s) ----%n%n", now(), uriString, version, config); @@ -409,7 +411,7 @@ public class HttpClientShutdown implements HttpServerAdapters { bodyCF = responseCF.thenApplyAsync(HttpResponse::body, readerService) .thenApply(HttpClientShutdown::readBody) .thenApply((s) -> { - assertEquals(s, MESSAGE); + assertEquals(MESSAGE, s); return s; }) .thenApply((s) -> { @@ -461,8 +463,8 @@ public class HttpClientShutdown implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println("\n**** Setup ****\n"); readerService = Executors.newCachedThreadPool(); @@ -498,8 +500,8 @@ public class HttpClientShutdown implements HttpServerAdapters { start = System.nanoTime(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.checkShutdown(5000); try { diff --git a/test/jdk/java/net/httpclient/HttpHeadersOf.java b/test/jdk/java/net/httpclient/HttpHeadersOf.java index 6194ab0a472..77a800b7cf2 100644 --- a/test/jdk/java/net/httpclient/HttpHeadersOf.java +++ b/test/jdk/java/net/httpclient/HttpHeadersOf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,7 +24,7 @@ /* * @test * @summary Tests for HttpHeaders.of factory method - * @run testng HttpHeadersOf + * @run junit HttpHeadersOf */ import java.net.http.HttpHeaders; @@ -33,13 +33,16 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.function.BiPredicate; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class HttpHeadersOf { @@ -59,12 +62,12 @@ public class HttpHeadersOf { @Override public String toString() { return "REJECT_ALL"; } }; - @DataProvider(name = "predicates") - public Object[][] predicates() { + public static Object[][] predicates() { return new Object[][] { { ACCEPT_ALL }, { REJECT_ALL } }; } - @Test(dataProvider = "predicates") + @ParameterizedTest + @MethodSource("predicates") public void testNull(BiPredicate filter) { assertThrows(NPE, () -> HttpHeaders.of(null, null)); assertThrows(NPE, () -> HttpHeaders.of(null, filter)); @@ -80,8 +83,7 @@ public class HttpHeadersOf { } - @DataProvider(name = "filterMaps") - public Object[][] filterMaps() { + public static Object[][] filterMaps() { List>> maps = List.of( Map.of("A", List.of("B"), "X", List.of("Y", "Z")), Map.of("A", List.of("B", "C"), "X", List.of("Y", "Z")), @@ -90,33 +92,33 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "filterMaps") + @ParameterizedTest + @MethodSource("filterMaps") public void testFilter(Map> map) { HttpHeaders headers = HttpHeaders.of(map, REJECT_ALL); - assertEquals(headers.map().size(), 0); + assertEquals(0, headers.map().size()); assertFalse(headers.firstValue("A").isPresent()); - assertEquals(headers.allValues("A").size(), 0); + assertEquals(0, headers.allValues("A").size()); headers = HttpHeaders.of(map, (name, value) -> { if (name.equals("A")) return true; else return false; }); - assertEquals(headers.map().size(), 1); + assertEquals(1, headers.map().size()); assertTrue(headers.firstValue("A").isPresent()); - assertEquals(headers.allValues("A"), map.get("A")); - assertEquals(headers.allValues("A").size(), map.get("A").size()); + assertEquals(map.get("A"), headers.allValues("A")); + assertEquals(map.get("A").size(), headers.allValues("A").size()); assertFalse(headers.firstValue("X").isPresent()); headers = HttpHeaders.of(map, (name, value) -> { if (name.equals("X")) return true; else return false; }); - assertEquals(headers.map().size(), 1); + assertEquals(1, headers.map().size()); assertTrue(headers.firstValue("X").isPresent()); - assertEquals(headers.allValues("X"), map.get("X")); - assertEquals(headers.allValues("X").size(), map.get("X").size()); + assertEquals(map.get("X"), headers.allValues("X")); + assertEquals(map.get("X").size(), headers.allValues("X").size()); assertFalse(headers.firstValue("A").isPresent()); } - @DataProvider(name = "mapValues") - public Object[][] mapValues() { + public static Object[][] mapValues() { List>> maps = List.of( Map.of("A", List.of("B")), Map.of("A", List.of("B", "C")), @@ -137,18 +139,19 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "mapValues") + @ParameterizedTest + @MethodSource("mapValues") public void testMapValues(Map> map) { HttpHeaders headers = HttpHeaders.of(map, ACCEPT_ALL); - assertEquals(headers.map().size(), map.size()); + assertEquals(map.size(), headers.map().size()); assertTrue(headers.firstValue("A").isPresent()); assertTrue(headers.firstValue("a").isPresent()); - assertEquals(headers.firstValue("A").get(), "B"); - assertEquals(headers.firstValue("a").get(), "B"); - assertEquals(headers.allValues("A"), map.get("A")); - assertEquals(headers.allValues("a"), map.get("A")); - assertEquals(headers.allValues("F").size(), 0); + assertEquals("B", headers.firstValue("A").get()); + assertEquals("B", headers.firstValue("a").get()); + assertEquals(map.get("A"), headers.allValues("A")); + assertEquals(map.get("A"), headers.allValues("a")); + assertEquals(0, headers.allValues("F").size()); assertTrue(headers.map().get("A").contains("B")); assertFalse(headers.map().get("A").contains("F")); assertThrows(NFE, () -> headers.firstValueAsLong("A")); @@ -167,8 +170,7 @@ public class HttpHeadersOf { } - @DataProvider(name = "caseInsensitivity") - public Object[][] caseInsensitivity() { + public static Object[][] caseInsensitivity() { List>> maps = List.of( Map.of("Accept-Encoding", List.of("gzip, deflate")), Map.of("accept-encoding", List.of("gzip, deflate")), @@ -179,7 +181,8 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "caseInsensitivity") + @ParameterizedTest + @MethodSource("caseInsensitivity") public void testCaseInsensitivity(Map> map) { HttpHeaders headers = HttpHeaders.of(map, ACCEPT_ALL); @@ -187,11 +190,11 @@ public class HttpHeadersOf { "aCCept-EnCODing", "accepT-encodinG")) { assertTrue(headers.firstValue(name).isPresent()); assertTrue(headers.allValues(name).contains("gzip, deflate")); - assertEquals(headers.firstValue(name).get(), "gzip, deflate"); - assertEquals(headers.allValues(name).size(), 1); - assertEquals(headers.map().size(), 1); - assertEquals(headers.map().get(name).size(), 1); - assertEquals(headers.map().get(name).get(0), "gzip, deflate"); + assertEquals("gzip, deflate", headers.firstValue(name).get()); + assertEquals(1, headers.allValues(name).size()); + assertEquals(1, headers.map().size()); + assertEquals(1, headers.map().get(name).size()); + assertEquals("gzip, deflate", headers.map().get(name).get(0)); } } @@ -212,8 +215,8 @@ public class HttpHeadersOf { HttpHeaders h2 = HttpHeaders.of(m2, ACCEPT_ALL); if (!m1.equals(m2)) mapDiffer++; if (m1.hashCode() != m2.hashCode()) mapHashDiffer++; - assertEquals(h1, h2, "HttpHeaders differ"); - assertEquals(h1.hashCode(), h2.hashCode(), + assertEquals(h2, h1, "HttpHeaders differ"); + assertEquals(h2.hashCode(), h1.hashCode(), "hashCode differ for " + List.of(m1,m2)); } } @@ -221,8 +224,7 @@ public class HttpHeadersOf { assertTrue(mapHashDiffer > 0, "all maps had same hashCode!"); } - @DataProvider(name = "valueAsLong") - public Object[][] valueAsLong() { + public static Object[][] valueAsLong() { return new Object[][] { new Object[] { Map.of("Content-Length", List.of("10")), 10l }, new Object[] { Map.of("Content-Length", List.of("101")), 101l }, @@ -232,15 +234,15 @@ public class HttpHeadersOf { }; } - @Test(dataProvider = "valueAsLong") + @ParameterizedTest + @MethodSource("valueAsLong") public void testValueAsLong(Map> map, long expected) { HttpHeaders headers = HttpHeaders.of(map, ACCEPT_ALL); - assertEquals(headers.firstValueAsLong("Content-Length").getAsLong(), expected); + assertEquals(expected, headers.firstValueAsLong("Content-Length").getAsLong()); } - @DataProvider(name = "duplicateNames") - public Object[][] duplicateNames() { + public static Object[][] duplicateNames() { List>> maps = List.of( Map.of("X-name", List.of(), "x-name", List.of()), @@ -262,7 +264,8 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "duplicateNames") + @ParameterizedTest + @MethodSource("duplicateNames") public void testDuplicates(Map> map) { HttpHeaders headers; try { @@ -275,8 +278,7 @@ public class HttpHeadersOf { } - @DataProvider(name = "noSplittingJoining") - public Object[][] noSplittingJoining() { + public static Object[][] noSplittingJoining() { List>> maps = List.of( Map.of("A", List.of("B")), Map.of("A", List.of("B", "C")), @@ -296,24 +298,24 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "noSplittingJoining") + @ParameterizedTest + @MethodSource("noSplittingJoining") public void testNoSplittingJoining(Map> map) { HttpHeaders headers = HttpHeaders.of(map, ACCEPT_ALL); Map> headersMap = headers.map(); - assertEquals(headers.map().size(), map.size()); + assertEquals(map.size(), headers.map().size()); for (Map.Entry> entry : map.entrySet()) { String headerName = entry.getKey(); List headerValues = entry.getValue(); - assertEquals(headerValues, headersMap.get(headerName)); - assertEquals(headerValues, headers.allValues(headerName)); - assertEquals(headerValues.get(0), headers.firstValue(headerName).get()); + assertEquals(headersMap.get(headerName), headerValues); + assertEquals(headers.allValues(headerName), headerValues); + assertEquals(headers.firstValue(headerName).get(), headerValues.get(0)); } } - @DataProvider(name = "trimming") - public Object[][] trimming() { + public static Object[][] trimming() { List>> maps = List.of( Map.of("A", List.of("B")), Map.of(" A", List.of("B")), @@ -331,23 +333,23 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "trimming") + @ParameterizedTest + @MethodSource("trimming") public void testTrimming(Map> map) { HttpHeaders headers = HttpHeaders.of(map, (name, value) -> { - assertEquals(name, "A"); - assertEquals(value, "B"); + assertEquals("A", name); + assertEquals("B", value); return true; }); - assertEquals(headers.map().size(), 1); - assertEquals(headers.firstValue("A").get(), "B"); - assertEquals(headers.allValues("A"), List.of("B")); - assertTrue(headers.map().get("A").equals(List.of("B"))); + assertEquals(1, headers.map().size()); + assertEquals("B", headers.firstValue("A").get()); + assertEquals(List.of("B"), headers.allValues("A")); + assertEquals(List.of("B"), headers.map().get("A")); } - @DataProvider(name = "emptyKey") - public Object[][] emptyKey() { + public static Object[][] emptyKey() { List>> maps = List.of( Map.of("", List.of("B")), Map.of(" ", List.of("B")), @@ -358,7 +360,8 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "emptyKey") + @ParameterizedTest + @MethodSource("emptyKey") public void testEmptyKey(Map> map) { HttpHeaders headers; try { @@ -371,8 +374,7 @@ public class HttpHeadersOf { } - @DataProvider(name = "emptyValue") - public Object[][] emptyValue() { + public static Object[][] emptyValue() { List>> maps = List.of( Map.of("A", List.of("")), Map.of("A", List.of("", "")), @@ -383,40 +385,41 @@ public class HttpHeadersOf { return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "emptyValue") + @ParameterizedTest + @MethodSource("emptyValue") public void testEmptyValue(Map> map) { HttpHeaders headers = HttpHeaders.of(map, (name, value) -> { - assertEquals(value, ""); + assertEquals("", value); return true; }); - assertEquals(headers.map().size(), map.size()); - assertEquals(headers.map().get("A").get(0), ""); - headers.allValues("A").forEach(v -> assertEquals(v, "")); - assertEquals(headers.firstValue("A").get(), ""); + assertEquals(map.size(), headers.map().size()); + assertEquals("", headers.map().get("A").get(0)); + headers.allValues("A").forEach(v -> assertEquals("", v)); + assertEquals("", headers.firstValue("A").get()); } - @DataProvider(name = "noValues") - public Object[][] noValues() { + public static Object[][] noValues() { List>> maps = List.of( Map.of("A", List.of()), Map.of("A", List.of(), "B", List.of()), Map.of("A", List.of(), "B", List.of(), "C", List.of()), - Map.of("A", new ArrayList()), - Map.of("A", new LinkedList()) + Map.of("A", new ArrayList<>()), + Map.of("A", new LinkedList<>()) ); return maps.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "noValues") + @ParameterizedTest + @MethodSource("noValues") public void testNoValues(Map> map) { HttpHeaders headers = HttpHeaders.of(map, (name, value) -> { fail("UNEXPECTED call to filter"); return true; }); - assertEquals(headers.map().size(), 0); - assertEquals(headers.map().get("A"), null); - assertEquals(headers.allValues("A").size(), 0); + assertEquals(0, headers.map().size()); + assertNull(headers.map().get("A")); + assertEquals(0, headers.allValues("A").size()); assertFalse(headers.firstValue("A").isPresent()); } } diff --git a/test/jdk/java/net/httpclient/HttpRedirectTest.java b/test/jdk/java/net/httpclient/HttpRedirectTest.java index e03a12a049c..2bf90bbe02c 100644 --- a/test/jdk/java/net/httpclient/HttpRedirectTest.java +++ b/test/jdk/java/net/httpclient/HttpRedirectTest.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 @@ -23,16 +23,13 @@ import com.sun.net.httpserver.HttpsServer; import jdk.httpclient.test.lib.common.TestServerConfigurator; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.AfterClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -63,7 +60,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import jdk.httpclient.test.lib.common.HttpServerAdapters; -/** +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/* * @test * @bug 8232625 * @summary This test verifies that the HttpClient works correctly when redirecting a post request. @@ -71,7 +73,7 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; * @build jdk.test.lib.net.SimpleSSLContext DigestEchoServer HttpRedirectTest * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.common.TestServerConfigurator - * @run testng/othervm -Dtest.requiresHost=true + * @run junit/othervm -Dtest.requiresHost=true * -Djdk.httpclient.HttpClient.log=headers * -Djdk.internal.httpclient.debug=false * HttpRedirectTest @@ -85,33 +87,30 @@ public class HttpRedirectTest implements HttpServerAdapters { SSLContext.setDefault(context); } - final AtomicLong requestCounter = new AtomicLong(); - final AtomicLong responseCounter = new AtomicLong(); - HttpTestServer http1Server; - HttpTestServer http2Server; - HttpTestServer https1Server; - HttpTestServer https2Server; - HttpTestServer http3Server; - DigestEchoServer.TunnelingProxy proxy; + static final AtomicLong requestCounter = new AtomicLong(); + private static HttpTestServer http1Server; + private static HttpTestServer http2Server; + private static HttpTestServer https1Server; + private static HttpTestServer https2Server; + private static HttpTestServer http3Server; + private static DigestEchoServer.TunnelingProxy proxy; - URI http1URI; - URI https1URI; - URI http2URI; - URI https2URI; - URI http3URI; - InetSocketAddress proxyAddress; - ProxySelector proxySelector; - HttpClient client; - List> futures = new CopyOnWriteArrayList<>(); - Set pending = new CopyOnWriteArraySet<>(); + private static URI http1URI; + private static URI https1URI; + private static URI http2URI; + private static URI https2URI; + private static URI http3URI; + private static InetSocketAddress proxyAddress; + private static ProxySelector proxySelector; + private static HttpClient client; - final ExecutorService executor = new ThreadPoolExecutor(12, 60, 10, + static final ExecutorService executor = new ThreadPoolExecutor(12, 60, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Shared by HTTP/1.1 servers - final ExecutorService clientexec = new ThreadPoolExecutor(6, 12, 1, + static final ExecutorService clientexec = new ThreadPoolExecutor(6, 12, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Used by the client - public HttpClient newHttpClient(ProxySelector ps) { - HttpClient.Builder builder = newClientBuilderForH3() + public static HttpClient newHttpClient(ProxySelector ps) { + HttpClient.Builder builder = HttpServerAdapters.createClientBuilderForH3() .sslContext(context) .executor(clientexec) .followRedirects(HttpClient.Redirect.ALWAYS) @@ -119,8 +118,7 @@ public class HttpRedirectTest implements HttpServerAdapters { return builder.build(); } - @DataProvider(name="uris") - Object[][] testURIs() throws URISyntaxException { + static Object[][] testURIs() { List uris = List.of( http3URI.resolve("direct/orig/"), http1URI.resolve("direct/orig/"), @@ -144,12 +142,11 @@ public class HttpRedirectTest implements HttpServerAdapters { ); Object[][] tests = new Object[redirects.size() * uris.size()][3]; int count = 0; - for (int i=0; i < uris.size(); i++) { - URI u = uris.get(i); - for (int j=0; j < redirects.size() ; j++) { - int code = redirects.get(j).getKey(); - String m = redirects.get(j).getValue(); - tests[count][0] = u.resolve(code +"/"); + for (URI u : uris) { + for (Map.Entry redirect : redirects) { + int code = redirect.getKey(); + String m = redirect.getValue(); + tests[count][0] = u.resolve(code + "/"); tests[count][1] = code; tests[count][2] = m; count++; @@ -158,8 +155,8 @@ public class HttpRedirectTest implements HttpServerAdapters { return tests; } - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { try { InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); @@ -208,10 +205,8 @@ public class HttpRedirectTest implements HttpServerAdapters { proxySelector = new HttpProxySelector(proxyAddress); client = newHttpClient(proxySelector); System.out.println("Setup: done"); - } catch (Exception x) { + } catch (Exception | Error x) { tearDown(); throw x; - } catch (Error e) { - tearDown(); throw e; } } @@ -222,19 +217,19 @@ public class HttpRedirectTest implements HttpServerAdapters { client.sendAsync(request, HttpResponse.BodyHandlers.ofString()); HttpResponse resp = respCf.join(); if (method.equals("DO_NOT_FOLLOW")) { - assertEquals(resp.statusCode(), code, u + ": status code"); + assertEquals(code, resp.statusCode(), u + ": status code"); } else { - assertEquals(resp.statusCode(), 200, u + ": status code"); + assertEquals(200, resp.statusCode(), u + ": status code"); } if (method.equals("POST")) { - assertEquals(resp.body(), REQUEST_BODY, u + ": body"); + assertEquals(REQUEST_BODY, resp.body(), u + ": body"); } else if (code == 304) { - assertEquals(resp.body(), "", u + ": body"); + assertEquals("", resp.body(), u + ": body"); } else if (method.equals("DO_NOT_FOLLOW")) { - assertNotEquals(resp.body(), GET_RESPONSE_BODY, u + ": body"); - assertNotEquals(resp.body(), REQUEST_BODY, u + ": body"); + assertNotEquals(GET_RESPONSE_BODY, resp.body(), u + ": body"); + assertNotEquals(REQUEST_BODY, resp.body(), u + ": body"); } else { - assertEquals(resp.body(), GET_RESPONSE_BODY, u + ": body"); + assertEquals(GET_RESPONSE_BODY, resp.body(), u + ": body"); } } @@ -244,21 +239,21 @@ public class HttpRedirectTest implements HttpServerAdapters { client.sendAsync(request, HttpResponse.BodyHandlers.ofString()); HttpResponse resp = respCf.join(); if (method.equals("DO_NOT_FOLLOW")) { - assertEquals(resp.statusCode(), code, u + ": status code"); + assertEquals(code, resp.statusCode(), u + ": status code"); } else { - assertEquals(resp.statusCode(), 200, u + ": status code"); + assertEquals(200, resp.statusCode(), u + ": status code"); } if (method.equals("POST")) { - assertEquals(resp.body(), REQUEST_BODY, u + ": body"); + assertEquals(REQUEST_BODY, resp.body(), u + ": body"); } else if (code == 304) { - assertEquals(resp.body(), "", u + ": body"); + assertEquals("", resp.body(), u + ": body"); } else if (method.equals("DO_NOT_FOLLOW")) { - assertNotEquals(resp.body(), GET_RESPONSE_BODY, u + ": body"); - assertNotEquals(resp.body(), REQUEST_BODY, u + ": body"); + assertNotEquals(GET_RESPONSE_BODY, resp.body(), u + ": body"); + assertNotEquals(REQUEST_BODY, resp.body(), u + ": body"); } else if (code == 303) { - assertEquals(resp.body(), GET_RESPONSE_BODY, u + ": body"); + assertEquals(GET_RESPONSE_BODY, resp.body(), u + ": body"); } else { - assertEquals(resp.body(), REQUEST_BODY, u + ": body"); + assertEquals(REQUEST_BODY, resp.body(), u + ": body"); } } @@ -270,7 +265,8 @@ public class HttpRedirectTest implements HttpServerAdapters { return builder; } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("testURIs") public void testPOST(URI uri, int code, String method) throws Exception { URI u = uri.resolve("foo?n=" + requestCounter.incrementAndGet()); HttpRequest request = newRequestBuilder(u) @@ -279,7 +275,8 @@ public class HttpRedirectTest implements HttpServerAdapters { testNonIdempotent(u, request, code, method); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("testURIs") public void testPUT(URI uri, int code, String method) throws Exception { URI u = uri.resolve("foo?n=" + requestCounter.incrementAndGet()); System.out.println("Testing with " + u); @@ -289,7 +286,8 @@ public class HttpRedirectTest implements HttpServerAdapters { testIdempotent(u, request, code, method); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("testURIs") public void testFoo(URI uri, int code, String method) throws Exception { URI u = uri.resolve("foo?n=" + requestCounter.incrementAndGet()); System.out.println("Testing with " + u); @@ -300,7 +298,8 @@ public class HttpRedirectTest implements HttpServerAdapters { testIdempotent(u, request, code, method); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("testURIs") public void testGet(URI uri, int code, String method) throws Exception { URI u = uri.resolve("foo?n=" + requestCounter.incrementAndGet()); System.out.println("Testing with " + u); @@ -312,24 +311,24 @@ public class HttpRedirectTest implements HttpServerAdapters { HttpResponse resp = respCf.join(); // body will be preserved except for 304 and 303: this is a GET. if (method.equals("DO_NOT_FOLLOW")) { - assertEquals(resp.statusCode(), code, u + ": status code"); + assertEquals(code, resp.statusCode(), u + ": status code"); } else { - assertEquals(resp.statusCode(), 200, u + ": status code"); + assertEquals(200, resp.statusCode(), u + ": status code"); } if (code == 304) { - assertEquals(resp.body(), "", u + ": body"); + assertEquals("", resp.body(), u + ": body"); } else if (method.equals("DO_NOT_FOLLOW")) { - assertNotEquals(resp.body(), GET_RESPONSE_BODY, u + ": body"); - assertNotEquals(resp.body(), REQUEST_BODY, u + ": body"); + assertNotEquals(GET_RESPONSE_BODY, resp.body(), u + ": body"); + assertNotEquals(REQUEST_BODY, resp.body(), u + ": body"); } else if (code == 303) { - assertEquals(resp.body(), GET_RESPONSE_BODY, u + ": body"); + assertEquals(GET_RESPONSE_BODY, resp.body(), u + ": body"); } else { - assertEquals(resp.body(), REQUEST_BODY, u + ": body"); + assertEquals(REQUEST_BODY, resp.body(), u + ": body"); } } - @AfterClass - public void tearDown() { + @AfterAll + public static void tearDown() { proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop); http1Server = stop(http1Server, HttpTestServer::stop); https1Server = stop(https1Server, HttpTestServer::stop); @@ -355,7 +354,7 @@ public class HttpRedirectTest implements HttpServerAdapters { private interface Stoppable { public void stop(T service) throws Exception; } static T stop(T service, Stoppable stop) { - try { if (service != null) stop.stop(service); } catch (Throwable x) { }; + try { if (service != null) stop.stop(service); } catch (Throwable x) { } return null; } diff --git a/test/jdk/java/net/httpclient/HttpRequestNewBuilderTest.java b/test/jdk/java/net/httpclient/HttpRequestNewBuilderTest.java index d7598ede3be..19afbfbd99b 100644 --- a/test/jdk/java/net/httpclient/HttpRequestNewBuilderTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestNewBuilderTest.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 @@ -44,19 +44,21 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -/** +/* * @test * @bug 8252304 8276559 * @summary HttpRequest.newBuilder(HttpRequest) API and behaviour checks -* @run testng/othervm HttpRequestNewBuilderTest +* @run junit/othervm HttpRequestNewBuilderTest */ public class HttpRequestNewBuilderTest { static final Class NPE = NullPointerException.class; @@ -64,21 +66,19 @@ public class HttpRequestNewBuilderTest { record NamedAssertion(String name, BiConsumer test) { } - List REQUEST_ASSERTIONS = List.of( - new NamedAssertion("uri", (r1, r2) -> assertEquals(r1.uri(), r2.uri())), - new NamedAssertion("timeout", (r1, r2) -> assertEquals(r1.timeout(), r2.timeout())), - new NamedAssertion("version", (r1, r2) -> assertEquals(r1.version(), r2.version())), - new NamedAssertion("headers", (r1, r2) -> assertEquals(r1.headers(), r2.headers())), - new NamedAssertion("options", (r1, r2) -> assertEquals(r1.getOption(H3_DISCOVERY), r2.getOption(H3_DISCOVERY))), - new NamedAssertion("expectContinue", (r1, r2) -> assertEquals(r1.expectContinue(), r2.expectContinue())), + static List REQUEST_ASSERTIONS = List.of(new NamedAssertion("uri", (r1, r2) -> assertEquals(r2.uri(), r1.uri())), + new NamedAssertion("timeout", (r1, r2) -> assertEquals(r2.timeout(), r1.timeout())), + new NamedAssertion("version", (r1, r2) -> assertEquals(r2.version(), r1.version())), + new NamedAssertion("headers", (r1, r2) -> assertEquals(r2.headers(), r1.headers())), + new NamedAssertion("options", (r1, r2) -> assertEquals(r2.getOption(H3_DISCOVERY), r1.getOption(H3_DISCOVERY))), + new NamedAssertion("expectContinue", (r1, r2) -> assertEquals(r2.expectContinue(), r1.expectContinue())), new NamedAssertion("method", (r1, r2) -> { - assertEquals(r1.method(), r2.method()); + assertEquals(r2.method(), r1.method()); assertBodyPublisherEqual(r1, r2); }) ); - @DataProvider(name = "testRequests") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { HttpRequest.newBuilder(URI.create("https://uri-1/")).build() }, { HttpRequest.newBuilder(URI.create("https://version-1/")).version(HTTP_1_1).build() }, @@ -152,14 +152,14 @@ public class HttpRequestNewBuilderTest { } // test methods - void assertBodyPublisherEqual(HttpRequest r1, HttpRequest r2) { + static void assertBodyPublisherEqual(HttpRequest r1, HttpRequest r2) { if (r1.bodyPublisher().isPresent()) { assertTrue(r2.bodyPublisher().isPresent()); var bp1 = r1.bodyPublisher().get(); var bp2 = r2.bodyPublisher().get(); - assertEquals(bp1.getClass(), bp2.getClass()); - assertEquals(bp1.contentLength(), bp2.contentLength()); + assertEquals(bp2.getClass(), bp1.getClass()); + assertEquals(bp2.contentLength(), bp1.contentLength()); final class TestSubscriber implements Flow.Subscriber { final BodySubscriber s; @@ -181,7 +181,7 @@ public class HttpRequestNewBuilderTest { bp2.subscribe(new TestSubscriber(bs2)); var b2 = bs2.getBody().toCompletableFuture().join().getBytes(); - assertEquals(b1, b2); + Assertions.assertArrayEquals(b2, b1); } else { assertFalse(r2.bodyPublisher().isPresent()); } @@ -199,9 +199,9 @@ public class HttpRequestNewBuilderTest { var r = HttpRequest.newBuilder(request, (n, v) -> true) .method(methodName, HttpRequest.BodyPublishers.ofString("testData")) .build(); - assertEquals(r.method(), methodName); + assertEquals(methodName, r.method()); assertTrue(r.bodyPublisher().isPresent()); - assertEquals(r.bodyPublisher().get().contentLength(), 8); + assertEquals(8, r.bodyPublisher().get().contentLength()); assertAllOtherElementsEqual(r, request, "method"); // method w/o body @@ -209,9 +209,9 @@ public class HttpRequestNewBuilderTest { var r1 = HttpRequest.newBuilder(request, (n, v) -> true) .method(methodName, noBodyPublisher) .build(); - assertEquals(r1.method(), methodName); + assertEquals(methodName, r1.method()); assertTrue(r1.bodyPublisher().isPresent()); - assertEquals(r1.bodyPublisher().get(), noBodyPublisher); + assertEquals(noBodyPublisher, r1.bodyPublisher().get()); assertAllOtherElementsEqual(r1, request, "method"); } @@ -223,113 +223,125 @@ public class HttpRequestNewBuilderTest { assertThrows(NPE, () -> HttpRequest.newBuilder(null, null)); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") void testBuilder(HttpRequest request) { var r = HttpRequest.newBuilder(request, (n, v) -> true).build(); - assertEquals(r, request); + assertEquals(request, r); assertAllOtherElementsEqual(r, request); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testURI(HttpRequest request) { URI newURI = URI.create("http://www.newURI.com/"); var r = HttpRequest.newBuilder(request, (n, v) -> true).uri(newURI).build(); - assertEquals(r.uri(), newURI); + assertEquals(newURI, r.uri()); assertAllOtherElementsEqual(r, request, "uri"); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testTimeout(HttpRequest request) { var r = HttpRequest.newBuilder(request, (n, v) -> true).timeout(Duration.ofSeconds(2)).build(); - assertEquals(r.timeout().get().getSeconds(), 2); + assertEquals(2, r.timeout().get().getSeconds()); assertAllOtherElementsEqual(r, request, "timeout"); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testVersion(HttpRequest request) { var r = HttpRequest.newBuilder(request, (n, v) -> true).version(HTTP_1_1).build(); - assertEquals(r.version().get(), HTTP_1_1); + assertEquals(HTTP_1_1, r.version().get()); assertAllOtherElementsEqual(r, request, "version"); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testGET(HttpRequest request) { var r = HttpRequest.newBuilder(request, (n, v) -> true) .GET() .build(); - assertEquals(r.method(), "GET"); + assertEquals("GET", r.method()); assertTrue(r.bodyPublisher().isEmpty()); assertAllOtherElementsEqual(r, request, "method"); testBodyPublisher("GET", request); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testDELETE(HttpRequest request) { var r = HttpRequest.newBuilder(request, (n, v) -> true) .DELETE() .build(); - assertEquals(r.method(), "DELETE"); + assertEquals("DELETE", r.method()); assertTrue(r.bodyPublisher().isEmpty()); assertAllOtherElementsEqual(r, request, "method"); testBodyPublisher("DELETE", request); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testPOST(HttpRequest request) { var r = HttpRequest.newBuilder(request, (n, v) -> true) .POST(HttpRequest.BodyPublishers.ofString("testData")) .build(); - assertEquals(r.method(), "POST"); + assertEquals("POST", r.method()); assertTrue(r.bodyPublisher().isPresent()); - assertEquals(r.bodyPublisher().get().contentLength(), 8); + assertEquals(8, r.bodyPublisher().get().contentLength()); assertAllOtherElementsEqual(r, request, "method"); testBodyPublisher("POST", request); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testPUT(HttpRequest request) { var r = HttpRequest.newBuilder(request, (n, v) -> true) .PUT(HttpRequest.BodyPublishers.ofString("testData")) .build(); - assertEquals(r.method(), "PUT"); + assertEquals("PUT", r.method()); assertTrue(r.bodyPublisher().isPresent()); - assertEquals(r.bodyPublisher().get().contentLength(), 8); + assertEquals(8, r.bodyPublisher().get().contentLength()); assertAllOtherElementsEqual(r, request, "method"); testBodyPublisher("PUT", request); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testUserDefinedMethod(HttpRequest request) { testBodyPublisher("TEST", request); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testAddHeader(HttpRequest request) { BiPredicate filter = (n, v) -> true; var r = HttpRequest.newBuilder(request, filter).headers("newName", "newValue").build(); - assertEquals(r.headers().firstValue("newName").get(), "newValue"); - assertEquals(r.headers().allValues("newName").size(), 1); + assertEquals("newValue", r.headers().firstValue("newName").get()); + assertEquals(1, r.headers().allValues("newName").size()); assertAllOtherElementsEqual(r, request, "headers"); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testSetOption(HttpRequest request) { BiPredicate filter = (n, v) -> true; var r = HttpRequest.newBuilder(request, filter).setOption(H3_DISCOVERY, ALT_SVC).build(); - assertEquals(r.getOption(H3_DISCOVERY).get(), ALT_SVC); + assertEquals(ALT_SVC, r.getOption(H3_DISCOVERY).get()); assertAllOtherElementsEqual(r, request, "options"); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testRemoveHeader(HttpRequest request) { if(!request.headers().map().isEmpty()) { assertTrue(request.headers().map().containsKey("testName1")); @@ -338,13 +350,14 @@ public class HttpRequestNewBuilderTest { var r = HttpRequest.newBuilder(request, filter).build(); assertFalse(r.headers().map().containsKey("testName1")); - assertEquals(r.headers().map(), HttpHeaders.of(request.headers().map(), filter).map()); + assertEquals(HttpHeaders.of(request.headers().map(), filter).map(), r.headers().map()); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testRemoveOption(HttpRequest request) { if(!request.getOption(H3_DISCOVERY).isEmpty()) { - assertEquals(request.getOption(H3_DISCOVERY).get(), ANY); + assertEquals(ANY, request.getOption(H3_DISCOVERY).get()); } var r = HttpRequest.newBuilder(request, (a, b) -> true) @@ -353,7 +366,8 @@ public class HttpRequestNewBuilderTest { assertAllOtherElementsEqual(r, request, "options"); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testRemoveSingleHeaderValue(HttpRequest request) { if(!request.headers().map().isEmpty()) { assertTrue(request.headers().allValues("testName1").contains("testValue1")); @@ -363,10 +377,12 @@ public class HttpRequestNewBuilderTest { var r = HttpRequest.newBuilder(request, filter).build(); assertFalse(r.headers().map().containsValue("testValue1")); - assertEquals(r.headers().map(), HttpHeaders.of(request.headers().map(), filter).map()); + assertFalse(r.headers().allValues("testName1").contains("testValue1")); + assertEquals(HttpHeaders.of(request.headers().map(), filter).map(), r.headers().map()); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testRemoveMultipleHeaders(HttpRequest request) { BiPredicate isTestName1Value1 = (n ,v) -> n.equalsIgnoreCase("testName1") && v.equals("testValue1"); @@ -375,34 +391,36 @@ public class HttpRequestNewBuilderTest { var filter = (isTestName1Value1.or(isTestName2Value2)).negate(); var r = HttpRequest.newBuilder(request, filter).build(); - assertEquals(r.headers().map(), HttpHeaders.of(request.headers().map(), filter).map()); + assertEquals(HttpHeaders.of(request.headers().map(), filter).map(), r.headers().map()); BiPredicate filter1 = (n, v) -> !(n.equalsIgnoreCase("testName1") && (v.equals("testValue1") || v.equals("testValue2"))); var r1 = HttpRequest.newBuilder(request, filter1).build(); - assertEquals(r1.headers().map(), HttpHeaders.of(request.headers().map(), filter1).map()); + assertEquals(HttpHeaders.of(request.headers().map(), filter1).map(), r1.headers().map()); } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testRemoveAllHeaders(HttpRequest request) { if (!request.headers().map().isEmpty()) { BiPredicate filter = (n, v) -> false; var r = HttpRequest.newBuilder(request, filter).build(); assertTrue(r.headers().map().isEmpty()); - assertEquals(r.headers().map(), HttpHeaders.of(request.headers().map(), filter).map()); + assertEquals(HttpHeaders.of(request.headers().map(), filter).map(), r.headers().map()); } } - @Test(dataProvider = "testRequests") + @ParameterizedTest + @MethodSource("variants") public void testRetainAllHeaders(HttpRequest request) { if (!request.headers().map().isEmpty()) { BiPredicate filter = (n, v) -> true; var r = HttpRequest.newBuilder(request, filter).build(); assertFalse(r.headers().map().isEmpty()); - assertEquals(r.headers().map(), HttpHeaders.of(request.headers().map(), filter).map()); + assertEquals(HttpHeaders.of(request.headers().map(), filter).map(), r.headers().map()); } } @@ -414,7 +432,7 @@ public class HttpRequestNewBuilderTest { BiPredicate filter = (n, v) -> !n.equalsIgnoreCase("Foo-Bar"); var r = HttpRequest.newBuilder(request, filter).build(); assertFalse(r.headers().map().containsKey("Foo-Bar")); - assertEquals(r.headers().map(), HttpHeaders.of(request.headers().map(), filter).map()); + assertEquals(HttpHeaders.of(request.headers().map(), filter).map(), r.headers().map()); } @Test diff --git a/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java b/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java index 2249839e8bb..633c6877716 100644 --- a/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java +++ b/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -38,15 +38,14 @@ import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import org.testng.annotations.Test; - -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /* * @test * @bug 8197564 8228970 * @summary Simple smoke test for BodySubscriber.asInputStream(); - * @run testng/othervm HttpResponseInputStreamTest + * @run junit/othervm HttpResponseInputStreamTest * @author daniel fuchs */ public class HttpResponseInputStreamTest { @@ -56,7 +55,7 @@ public class HttpResponseInputStreamTest { static class TestException extends IOException {} public static void main(String[] args) throws InterruptedException, ExecutionException { - testOnError(); + new HttpResponseInputStreamTest().testOnError(); } /** @@ -66,7 +65,7 @@ public class HttpResponseInputStreamTest { * @throws ExecutionException */ @Test - public static void testOnError() throws InterruptedException, ExecutionException { + public void testOnError() throws InterruptedException, ExecutionException { CountDownLatch latch = new CountDownLatch(1); BodySubscriber isb = BodySubscribers.ofInputStream(); ErrorTestSubscription s = new ErrorTestSubscription(isb); @@ -160,7 +159,7 @@ public class HttpResponseInputStreamTest { } @Test - public static void testCloseAndSubscribe() + public void testCloseAndSubscribe() throws InterruptedException, ExecutionException { BodySubscriber isb = BodySubscribers.ofInputStream(); @@ -189,34 +188,34 @@ public class HttpResponseInputStreamTest { } @Test - public static void testReadParameters() throws InterruptedException, ExecutionException, IOException { + public void testReadParameters() throws InterruptedException, ExecutionException, IOException { BodySubscriber isb = BodySubscribers.ofInputStream(); InputStream is = isb.getBody().toCompletableFuture().get(); Throwable ex; // len == 0 - assertEquals(is.read(new byte[16], 0, 0), 0); - assertEquals(is.read(new byte[16], 16, 0), 0); + assertEquals(0, is.read(new byte[16], 0, 0)); + assertEquals(0, is.read(new byte[16], 16, 0)); // index == -1 - ex = expectThrows(OOB, () -> is.read(new byte[16], -1, 10)); + ex = assertThrows(OOB, () -> is.read(new byte[16], -1, 10)); System.out.println("OutOfBoundsException thrown as expected: " + ex); // large offset - ex = expectThrows(OOB, () -> is.read(new byte[16], 17, 10)); + ex = assertThrows(OOB, () -> is.read(new byte[16], 17, 10)); System.out.println("OutOfBoundsException thrown as expected: " + ex); - ex = expectThrows(OOB, () -> is.read(new byte[16], 10, 10)); + ex = assertThrows(OOB, () -> is.read(new byte[16], 10, 10)); System.out.println("OutOfBoundsException thrown as expected: " + ex); // null value - ex = expectThrows(NPE, () -> is.read(null, 0, 10)); + ex = assertThrows(NPE, () -> is.read(null, 0, 10)); System.out.println("NullPointerException thrown as expected: " + ex); } @Test - public static void testSubscribeAndClose() + public void testSubscribeAndClose() throws InterruptedException, ExecutionException { BodySubscriber isb = BodySubscribers.ofInputStream(); diff --git a/test/jdk/java/net/httpclient/HttpVersionsTest.java b/test/jdk/java/net/httpclient/HttpVersionsTest.java index a55c2727a00..f047e2902e8 100644 --- a/test/jdk/java/net/httpclient/HttpVersionsTest.java +++ b/test/jdk/java/net/httpclient/HttpVersionsTest.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 @@ -28,7 +28,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * jdk.test.lib.Platform - * @run testng/othervm HttpVersionsTest + * @run junit/othervm HttpVersionsTest */ import java.io.IOException; @@ -42,30 +42,30 @@ import java.net.http.HttpResponse; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.net.ssl.SSLContext; -import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.String.format; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpResponse.BodyHandlers.ofString; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class HttpVersionsTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - Http2TestServer http2TestServer; - Http2TestServer https2TestServer; - String http2URI; - String https2URI; + static Http2TestServer http2TestServer; + static Http2TestServer https2TestServer; + static String http2URI; + static String https2URI; static final int ITERATIONS = 3; static final String[] BODY = new String[] { @@ -74,10 +74,9 @@ public class HttpVersionsTest { "I think I'll drink until I stink", "I'll drink until I cannot blink" }; - int nextBodyId; + static int nextBodyId; - @DataProvider(name = "scenarios") - public Object[][] scenarios() { + public static Object[][] scenarios() { return new Object[][] { { http2URI, true }, { https2URI, true }, @@ -87,9 +86,10 @@ public class HttpVersionsTest { } /** Checks that an HTTP/2 request receives an HTTP/2 response. */ - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") void testHttp2Get(String uri, boolean sameClient) throws Exception { - out.println(format("\n--- testHttp2Get uri:%s, sameClient:%s", uri, sameClient)); + out.printf("\n--- testHttp2Get uri:%s, sameClient:%s%n", uri, sameClient); HttpClient client = null; for (int i=0; i out.println("X-Received-Body:" + s)); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_1_1); - assertEquals(response.body(), ""); - assertEquals(response.headers().firstValue("X-Magic").get(), - "HTTP/1.1 request received by HTTP/2 server"); - assertEquals(response.headers().firstValue("X-Received-Body").get(), ""); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_1_1, response.version()); + assertEquals("", response.body()); + assertEquals("HTTP/1.1 request received by HTTP/2 server", response.headers().firstValue("X-Magic").get()); + assertEquals("", response.headers().firstValue("X-Received-Body").get()); if (uri.startsWith("https")) assertTrue(response.sslSession().isPresent()); } } - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") void testHttp1dot1Post(String uri, boolean sameClient) throws Exception { - out.println(format("\n--- testHttp1dot1Post uri:%s, sameClient:%s", uri, sameClient)); + out.printf("\n--- testHttp1dot1Post uri:%s, sameClient:%s%n", uri, sameClient); HttpClient client = null; for (int i=0; i out.println("X-Received-Body:" + s)); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_1_1); - assertEquals(response.body(), ""); - assertEquals(response.headers().firstValue("X-Magic").get(), - "HTTP/1.1 request received by HTTP/2 server"); - assertEquals(response.headers().firstValue("X-Received-Body").get(), msg); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_1_1, response.version()); + assertEquals("", response.body()); + assertEquals("HTTP/1.1 request received by HTTP/2 server", response.headers().firstValue("X-Magic").get()); + assertEquals(msg, response.headers().firstValue("X-Received-Body").get()); if (uri.startsWith("https")) assertTrue(response.sslSession().isPresent()); } @@ -203,8 +204,8 @@ public class HttpVersionsTest { static final ExecutorService executor = Executors.newCachedThreadPool(); - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { http2TestServer = new Http2TestServer("localhost", false, 0, executor, 50, null, null, true); http2TestServer.addHandler(new Http2VerEchoHandler(), "/http2/vts"); http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/vts"; @@ -217,8 +218,8 @@ public class HttpVersionsTest { https2TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { http2TestServer.stop(); https2TestServer.stop(); executor.shutdown(); diff --git a/test/jdk/java/net/httpclient/IdleConnectionTimeoutTest.java b/test/jdk/java/net/httpclient/IdleConnectionTimeoutTest.java index 78984964895..be7e1db0033 100644 --- a/test/jdk/java/net/httpclient/IdleConnectionTimeoutTest.java +++ b/test/jdk/java/net/httpclient/IdleConnectionTimeoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -30,8 +30,6 @@ import jdk.httpclient.test.lib.quic.QuicServerConnection; import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; @@ -56,7 +54,10 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.net.http.HttpClient.Version.HTTP_2; -import static org.testng.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /* * @test @@ -68,45 +69,45 @@ import static org.testng.Assert.assertEquals; * jdk.httpclient.test.lib.http2.Http2TestServer * jdk.httpclient.test.lib.http3.Http3TestServer * - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout=1 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout=1 * IdleConnectionTimeoutTest - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout=20 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout=20 * IdleConnectionTimeoutTest * - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=1 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=1 * IdleConnectionTimeoutTest - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=20 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=20 * IdleConnectionTimeoutTest - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=abc + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=abc * IdleConnectionTimeoutTest - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=-1 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h2=-1 * IdleConnectionTimeoutTest * - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=1 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=1 * IdleConnectionTimeoutTest - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=20 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=20 * IdleConnectionTimeoutTest - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=abc + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=abc * IdleConnectionTimeoutTest - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=-1 + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all -Djdk.httpclient.keepalive.timeout.h3=-1 * IdleConnectionTimeoutTest */ public class IdleConnectionTimeoutTest { - URI timeoutUriH2, noTimeoutUriH2, timeoutUriH3, noTimeoutUriH3, getH3; + private static URI timeoutUriH2, noTimeoutUriH2, timeoutUriH3, noTimeoutUriH3, getH3; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); static volatile QuicServerConnection latestServerConn; final String KEEP_ALIVE_PROPERTY = "jdk.httpclient.keepalive.timeout"; final String IDLE_CONN_PROPERTY_H2 = "jdk.httpclient.keepalive.timeout.h2"; final String IDLE_CONN_PROPERTY_H3 = "jdk.httpclient.keepalive.timeout.h3"; - final String TIMEOUT_PATH = "/serverTimeoutHandler"; - final String NO_TIMEOUT_PATH = "/noServerTimeoutHandler"; + static final String TIMEOUT_PATH = "/serverTimeoutHandler"; + static final String NO_TIMEOUT_PATH = "/noServerTimeoutHandler"; static Http2TestServer http2TestServer; static Http3TestServer http3TestServer; static final PrintStream testLog = System.err; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { http2TestServer = new Http2TestServer(false, 0); http2TestServer.addHandler(new ServerTimeoutHandlerH2(), TIMEOUT_PATH); http2TestServer.addHandler(new ServerNoTimeoutHandlerH2(), NO_TIMEOUT_PATH); @@ -212,7 +213,7 @@ public class IdleConnectionTimeoutTest { HttpRequest hreq = HttpRequest.newBuilder(uri).version(version).GET() .setOption(H3_DISCOVERY, config).build(); HttpResponse hresp = runRequest(hc, hreq, 2750); - assertEquals(hresp.statusCode(), 200, "idleConnectionTimeoutEvent was not expected but occurred"); + assertEquals(200, hresp.statusCode(), "idleConnectionTimeoutEvent was not expected but occurred"); } private void testNoTimeout(HttpClient hc, URI uri, Version version) { @@ -221,13 +222,13 @@ public class IdleConnectionTimeoutTest { HttpRequest hreq = HttpRequest.newBuilder(uri).version(version).GET() .setOption(H3_DISCOVERY, config).build(); HttpResponse hresp = runRequest(hc, hreq, 0); - assertEquals(hresp.statusCode(), 200, "idleConnectionTimeoutEvent was not expected but occurred"); + assertEquals(200, hresp.statusCode(), "idleConnectionTimeoutEvent was not expected but occurred"); } private HttpResponse runRequest(HttpClient hc, HttpRequest req, int sleepTime) { CompletableFuture> request = hc.sendAsync(req, HttpResponse.BodyHandlers.ofString(UTF_8)); HttpResponse hresp = request.join(); - assertEquals(hresp.statusCode(), 200); + assertEquals(200, hresp.statusCode()); try { Thread.sleep(sleepTime); } catch (InterruptedException e) { diff --git a/test/jdk/java/net/httpclient/ImmutableFlowItems.java b/test/jdk/java/net/httpclient/ImmutableFlowItems.java index 3ee4eb28768..e42e2e21a6e 100644 --- a/test/jdk/java/net/httpclient/ImmutableFlowItems.java +++ b/test/jdk/java/net/httpclient/ImmutableFlowItems.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,7 +28,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer * jdk.httpclient.test.lib.common.TestServerConfigurator - * @run testng/othervm ImmutableFlowItems + * @run junit/othervm ImmutableFlowItems */ import java.io.IOException; @@ -58,32 +58,33 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ImmutableFlowItems { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - Http2TestServer http2TestServer; // HTTP/2 ( h2c ) - Http2TestServer https2TestServer; // HTTP/2 ( h2 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; + private static HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpsServer httpsTestServer; // HTTPS/1.1 + private static Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + private static Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI_fixed }, { httpURI_chunk }, @@ -104,7 +105,8 @@ public class ImmutableFlowItems { .build(); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsString(String uri) throws Exception { HttpClient client = newHttpClient(); @@ -114,14 +116,14 @@ public class ImmutableFlowItems { BodyHandler handler = new CRSBodyHandler(); client.sendAsync(req, handler) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, BODY)) + .thenAccept(body -> assertEquals(BODY, body)) .join(); } static class CRSBodyHandler implements BodyHandler { @Override public BodySubscriber apply(HttpResponse.ResponseInfo rinfo) { - assertEquals(rinfo.statusCode(), 200); + assertEquals(200, rinfo.statusCode()); return new CRSBodySubscriber(); } } @@ -138,7 +140,7 @@ public class ImmutableFlowItems { public void onNext(List item) { assertUnmodifiableList(item); long c = item.stream().filter(ByteBuffer::isReadOnly).count(); - assertEquals(c, item.size(), "Unexpected writable buffer in: " +item); + assertEquals(item.size(), c, "Unexpected writable buffer in: " +item); ofString.onNext(item); } @@ -173,8 +175,8 @@ public class ImmutableFlowItems { + server.getAddress().getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 HttpHandler h1_fixedLengthHandler = new HTTP1_FixedLengthHandler(); HttpHandler h1_chunkHandler = new HTTP1_ChunkedHandler(); @@ -214,8 +216,8 @@ public class ImmutableFlowItems { https2TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(0); httpsTestServer.stop(0); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/InvalidInputStreamSubscriptionRequest.java b/test/jdk/java/net/httpclient/InvalidInputStreamSubscriptionRequest.java index 10df564bd41..c83d9d67528 100644 --- a/test/jdk/java/net/httpclient/InvalidInputStreamSubscriptionRequest.java +++ b/test/jdk/java/net/httpclient/InvalidInputStreamSubscriptionRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext ReferenceTracker * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm -Dtest.http.version=http3 + * @run junit/othervm -Dtest.http.version=http3 * -Djdk.internal.httpclient.debug=true * InvalidInputStreamSubscriptionRequest */ @@ -41,7 +41,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext ReferenceTracker * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm -Dtest.http.version=http2 InvalidInputStreamSubscriptionRequest + * @run junit/othervm -Dtest.http.version=http2 InvalidInputStreamSubscriptionRequest */ /* * @test id=http1 @@ -51,16 +51,11 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext ReferenceTracker * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm -Dtest.http.version=http1 InvalidInputStreamSubscriptionRequest + * @run junit/othervm -Dtest.http.version=http1 InvalidInputStreamSubscriptionRequest */ import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -96,26 +91,31 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; - String http3URI_fixed; - String http3URI_chunk; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; + private static String http3URI_fixed; + private static String http3URI_chunk; static final int ITERATION_COUNT = 3; // a shared executor helps reduce the amount of threads created by the test @@ -156,7 +156,7 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters } } - @AfterClass + @AfterAll static final void printFailedTests() { out.println("\n========================="); try { @@ -200,8 +200,7 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters static final Supplier> OF_INPUTSTREAM = BHS.of(BodyHandlers::ofInputStream, "BodyHandlers::ofInputStream"); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { Object[][] http3 = new Object[][]{ {http3URI_fixed, false, OF_INPUTSTREAM}, {http3URI_chunk, false, OF_INPUTSTREAM}, @@ -244,7 +243,7 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; HttpClient newHttpClient(String uri) { HttpClient.Builder builder = uri.contains("/http3/") ? newClientBuilderForH3() @@ -265,7 +264,8 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters return builder; } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testNoBody(String uri, boolean sameClient, BHS handlers) throws Exception { @@ -285,7 +285,7 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters HttpResponse response = client.send(req, badHandler); try (InputStream is = response.body()) { String body = new String(is.readAllBytes(), UTF_8); - assertEquals(body, ""); + assertEquals("", body); if (uri.endsWith("/chunk") && response.version() == HTTP_1_1) { // with /fixed and 0 length @@ -324,7 +324,8 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testNoBodyAsync(String uri, boolean sameClient, BHS handlers) throws Exception { @@ -352,7 +353,7 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters }); try { // Get the final result and compare it with the expected body - assertEquals(result.get(), ""); + assertEquals("", result.get()); if (uri.endsWith("/chunk") && response.get().version() == HTTP_1_1) { // with /fixed and 0 length @@ -390,7 +391,8 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsString(String uri, boolean sameClient, BHS handlers) throws Exception { @@ -409,7 +411,7 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters HttpResponse response = client.send(req, badHandler); try (InputStream is = response.body()) { String body = new String(is.readAllBytes(), UTF_8); - assertEquals(body, WITH_BODY); + assertEquals(WITH_BODY, body); throw new RuntimeException("Expected IAE not thrown"); } } catch (Exception x) { @@ -443,7 +445,8 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsStringAsync(String uri, boolean sameClient, BHS handlers) throws Exception { @@ -470,7 +473,7 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters // Get the final result and compare it with the expected body try { String body = result.get(); - assertEquals(body, WITH_BODY); + assertEquals(WITH_BODY, body); throw new RuntimeException("Expected IAE not thrown"); } catch (Exception x) { Throwable cause = x; @@ -563,8 +566,8 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters + server.getAddress().getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 HttpTestHandler h1_fixedLengthHandler = new HTTP_FixedLengthHandler(); HttpTestHandler h1_chunkHandler = new HTTP_VariableLengthHandler(); @@ -613,8 +616,8 @@ public class InvalidInputStreamSubscriptionRequest implements HttpServerAdapters http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { AssertionError fail = TRACKER.check(1500); try { httpTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/InvalidSSLContextTest.java b/test/jdk/java/net/httpclient/InvalidSSLContextTest.java index be4d3297dd7..0027a7956d5 100644 --- a/test/jdk/java/net/httpclient/InvalidSSLContextTest.java +++ b/test/jdk/java/net/httpclient/InvalidSSLContextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,7 +27,7 @@ * when SSL context is not valid. * @library /test/lib * @build jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.internal.httpclient.debug=true InvalidSSLContextTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true InvalidSSLContextTest */ import java.io.IOException; @@ -40,7 +40,6 @@ import java.util.concurrent.CompletionException; import java.net.SocketException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; import java.net.http.HttpClient; @@ -49,32 +48,32 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class InvalidSSLContextTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - volatile SSLServerSocket sslServerSocket; - volatile String uri; + static volatile SSLServerSocket sslServerSocket; + static volatile String uri; - @DataProvider(name = "versions") - public Object[][] versions() { + public static Object[][] versions() { return new Object[][]{ { HTTP_1_1 }, { HTTP_2 } }; } - @Test(dataProvider = "versions") + @ParameterizedTest + @MethodSource("versions") public void testSync(Version version) throws Exception { // client-side uses a different context to that of the server-side HttpClient client = HttpClient.newBuilder() @@ -88,14 +87,15 @@ public class InvalidSSLContextTest { try { HttpResponse response = client.send(request, BodyHandlers.discarding()); - Assert.fail("UNEXPECTED response" + response); + Assertions.fail("UNEXPECTED response" + response); } catch (IOException ex) { System.out.println("Caught expected: " + ex); assertExceptionOrCause(SSLException.class, ex); } } - @Test(dataProvider = "versions") + @ParameterizedTest + @MethodSource("versions") public void testAsync(Version version) throws Exception { // client-side uses a different context to that of the server-side HttpClient client = HttpClient.newBuilder() @@ -115,13 +115,13 @@ public class InvalidSSLContextTest { CompletableFuture stage) { stage.handle((result, error) -> { if (result != null) { - Assert.fail("UNEXPECTED result: " + result); + Assertions.fail("UNEXPECTED result: " + result); return null; } if (error instanceof CompletionException) { Throwable cause = error.getCause(); if (cause == null) { - Assert.fail("Unexpected null cause: " + error); + Assertions.fail("Unexpected null cause: " + error); } assertExceptionOrCause(clazz, cause); } else { @@ -133,7 +133,7 @@ public class InvalidSSLContextTest { static void assertExceptionOrCause(Class clazz, Throwable t) { if (t == null) { - Assert.fail("Expected " + clazz + ", caught nothing"); + Assertions.fail("Expected " + clazz + ", caught nothing"); } final Throwable original = t; do { @@ -142,11 +142,11 @@ public class InvalidSSLContextTest { } } while ((t = t.getCause()) != null); original.printStackTrace(System.out); - Assert.fail("Expected " + clazz + "in " + original); + Assertions.fail("Expected " + clazz + "in " + original); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // server-side uses a different context to that of the client-side sslServerSocket = (SSLServerSocket)sslContext .getServerSocketFactory() @@ -169,7 +169,7 @@ public class InvalidSSLContextTest { Thread.sleep(500); s.startHandshake(); s.close(); - Assert.fail("SERVER: UNEXPECTED "); + Assertions.fail("SERVER: UNEXPECTED "); } catch (SSLException | SocketException se) { System.out.println("SERVER: caught expected " + se); } catch (IOException e) { @@ -187,8 +187,8 @@ public class InvalidSSLContextTest { t.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { sslServerSocket.close(); } } diff --git a/test/jdk/java/net/httpclient/InvalidSubscriptionRequest.java b/test/jdk/java/net/httpclient/InvalidSubscriptionRequest.java index c16c9f1747c..692e4d0bea4 100644 --- a/test/jdk/java/net/httpclient/InvalidSubscriptionRequest.java +++ b/test/jdk/java/net/httpclient/InvalidSubscriptionRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -30,21 +30,15 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext ReferenceTracker * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm InvalidSubscriptionRequest + * @run junit/othervm InvalidSubscriptionRequest */ -import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.InetAddress; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -74,26 +68,31 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class InvalidSubscriptionRequest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; - String http3URI_fixed; - String http3URI_chunk; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; + private static String http3URI_fixed; + private static String http3URI_chunk; static final int ITERATION_COUNT = 3; // a shared executor helps reduce the amount of threads created by the test @@ -126,8 +125,7 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { static final Supplier>>> OF_PUBLISHER_API = BHS.of(BodyHandlers::ofPublisher, "BodyHandlers::ofPublisher"); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { http3URI_fixed, false, OF_PUBLISHER_API }, { http3URI_chunk, false, OF_PUBLISHER_API }, @@ -154,7 +152,7 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { }; } - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; HttpClient newHttpClient(String uri) { HttpClient.Builder builder = uri.contains("/http3/") ? newClientBuilderForH3() @@ -175,8 +173,9 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { return builder; } - @Test(dataProvider = "variants") - public void testNoBody(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testNoBody(String uri, boolean sameClient, BHS handlers) throws Exception { HttpClient client = null; Throwable failed = null; for (int i=0; i< ITERATION_COUNT; i++) { @@ -196,7 +195,7 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { // Get the final result and compare it with the expected body try { String body = ofString.getBody().toCompletableFuture().get(); - assertEquals(body, ""); + assertEquals("", body); if (uri.endsWith("/chunk") && response.version() == HTTP_1_1) { // with /fixed and 0 length @@ -231,8 +230,9 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testNoBodyAsync(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testNoBodyAsync(String uri, boolean sameClient, BHS handlers) { HttpClient client = null; Throwable failed = null; for (int i=0; i< ITERATION_COUNT; i++) { @@ -257,7 +257,7 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { }); try { // Get the final result and compare it with the expected body - assertEquals(result.get(), ""); + assertEquals("", result.get()); if (uri.endsWith("/chunk") && response.get().version() == HTTP_1_1) { // with /fixed and 0 length @@ -292,8 +292,9 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testAsString(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testAsString(String uri, boolean sameClient, BHS handlers) throws Exception { HttpClient client = null; Throwable failed = null; for (int i=0; i< ITERATION_COUNT; i++) { @@ -314,7 +315,7 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { // Get the final result and compare it with the expected body try { String body = ofString.getBody().toCompletableFuture().get(); - assertEquals(body, WITH_BODY); + assertEquals(WITH_BODY, body); throw new RuntimeException("Expected IAE not thrown"); } catch (Exception x) { Throwable cause = x; @@ -344,8 +345,9 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testAsStringAsync(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testAsStringAsync(String uri, boolean sameClient, BHS handlers) { HttpClient client = null; Throwable failed = null; for (int i=0; i< ITERATION_COUNT; i++) { @@ -369,7 +371,7 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { // Get the final result and compare it with the expected body try { String body = result.get(); - assertEquals(body, WITH_BODY); + assertEquals(WITH_BODY, body); throw new RuntimeException("Expected IAE not thrown"); } catch (Exception x) { Throwable cause = x; @@ -454,13 +456,8 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { } } - static String serverAuthority(HttpServer server) { - return InetAddress.getLoopbackAddress().getHostName() + ":" - + server.getAddress().getPort(); - } - - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 HttpTestHandler h1_fixedLengthHandler = new HTTP_FixedLengthHandler(); HttpTestHandler h1_chunkHandler = new HTTP_VariableLengthHandler(); @@ -509,8 +506,8 @@ public class InvalidSubscriptionRequest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { AssertionError fail = TRACKER.check(500); try { httpTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/LineBodyHandlerTest.java b/test/jdk/java/net/httpclient/LineBodyHandlerTest.java index 45df8d13f7e..ddca2e1f7d2 100644 --- a/test/jdk/java/net/httpclient/LineBodyHandlerTest.java +++ b/test/jdk/java/net/httpclient/LineBodyHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -54,10 +54,6 @@ import java.util.stream.Stream; import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; @@ -66,11 +62,16 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_16; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.net.http.HttpRequest.BodyPublishers.ofString; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -81,29 +82,28 @@ import static org.testng.Assert.assertTrue; * @library /test/lib /test/jdk/java/net/httpclient/lib * @build ReferenceTracker jdk.httpclient.test.lib.http2.Http2TestServer * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=1 LineBodyHandlerTest + * @run junit/othervm -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=1 LineBodyHandlerTest */ public class LineBodyHandlerTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - final AtomicInteger clientCount = new AtomicInteger(); - HttpClient sharedClient; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final AtomicInteger clientCount = new AtomicInteger(); + private static HttpClient sharedClient; - @DataProvider(name = "uris") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { http3URI }, { httpURI }, @@ -171,7 +171,7 @@ public class LineBodyHandlerTest implements HttpServerAdapters { StandardCharsets.US_ASCII, "")); } - private static final List lines(String text, String eol) { + private static List lines(String text, String eol) { if (eol == null) { return new BufferedReader(new StringReader(text)).lines().collect(Collectors.toList()); } else { @@ -210,7 +210,8 @@ public class LineBodyHandlerTest implements HttpServerAdapters { return builder; } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testStringWithFinisher(String url) { String body = "May the luck of the Irish be with you!"; HttpClient client = newClient(); @@ -226,12 +227,13 @@ public class LineBodyHandlerTest implements HttpServerAdapters { HttpResponse response = cf.join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, body); - assertEquals(subscriber.list, lines(body, "\n")); + assertEquals(200, response.statusCode()); + assertEquals(body, text); + assertEquals(lines(body, "\n"), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsStream(String url) { String body = "May the luck of the Irish be with you!"; HttpClient client = newClient(); @@ -247,13 +249,14 @@ public class LineBodyHandlerTest implements HttpServerAdapters { List list = stream.collect(Collectors.toList()); String text = list.stream().collect(Collectors.joining("|")); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, body); - assertEquals(list, List.of(body)); - assertEquals(list, lines(body, null)); + assertEquals(200, response.statusCode()); + assertEquals(body, text); + assertEquals(List.of(body), list); + assertEquals(lines(body, null), list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testStringWithFinisher2(String url) { String body = "May the luck\r\n\r\n of the Irish be with you!"; HttpClient client = newClient(); @@ -270,12 +273,13 @@ public class LineBodyHandlerTest implements HttpServerAdapters { HttpResponse response = cf.join(); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, body.replace("\r\n", "\n")); - assertEquals(subscriber.list, lines(body, null)); + assertEquals(200, response.statusCode()); + assertEquals(body.replace("\r\n", "\n"), text); + assertEquals(lines(body, null), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsStreamWithCRLF(String url) { String body = "May the luck\r\n\r\n of the Irish be with you!"; HttpClient client = newClient(); @@ -291,15 +295,16 @@ public class LineBodyHandlerTest implements HttpServerAdapters { List list = stream.collect(Collectors.toList()); String text = list.stream().collect(Collectors.joining("|")); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, "May the luck|| of the Irish be with you!"); - assertEquals(list, List.of("May the luck", + assertEquals(200, response.statusCode()); + assertEquals("May the luck|| of the Irish be with you!", text); + assertEquals(List.of("May the luck", "", - " of the Irish be with you!")); - assertEquals(list, lines(body, null)); + " of the Irish be with you!"), list); + assertEquals(lines(body, null), list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testStringWithFinisherBlocking(String url) throws Exception { String body = "May the luck of the Irish be with you!"; HttpClient client = newClient(); @@ -311,12 +316,13 @@ public class LineBodyHandlerTest implements HttpServerAdapters { BodyHandlers.fromLineSubscriber(subscriber, Supplier::get, "\n")); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, "May the luck of the Irish be with you!"); - assertEquals(subscriber.list, lines(body, "\n")); + assertEquals(200, response.statusCode()); + assertEquals("May the luck of the Irish be with you!", text); + assertEquals(lines(body, "\n"), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testStringWithoutFinisherBlocking(String url) throws Exception { String body = "May the luck of the Irish be with you!"; HttpClient client = newClient(); @@ -328,14 +334,15 @@ public class LineBodyHandlerTest implements HttpServerAdapters { BodyHandlers.fromLineSubscriber(subscriber)); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, "May the luck of the Irish be with you!"); - assertEquals(subscriber.list, lines(body, null)); + assertEquals(200, response.statusCode()); + assertEquals("May the luck of the Irish be with you!", text); + assertEquals(lines(body, null), subscriber.list); } // Subscriber - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsStreamWithMixedCRLF(String url) { String body = "May\r\n the wind\r\n always be\rat your back.\r\r"; HttpClient client = newClient(); @@ -351,18 +358,19 @@ public class LineBodyHandlerTest implements HttpServerAdapters { List list = stream.collect(Collectors.toList()); String text = list.stream().collect(Collectors.joining("|")); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May| the wind| always be|at your back.|"); - assertEquals(list, List.of("May", + assertEquals("May| the wind| always be|at your back.|", text); + assertEquals(List.of("May", " the wind", " always be", "at your back.", - "")); - assertEquals(list, lines(body, null)); + ""), list); + assertEquals(lines(body, null), list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsStreamWithMixedCRLF_UTF8(String url) { String body = "May\r\n the wind\r\n always be\rat your back.\r\r"; HttpClient client = newClient(); @@ -378,17 +386,18 @@ public class LineBodyHandlerTest implements HttpServerAdapters { List list = stream.collect(Collectors.toList()); String text = list.stream().collect(Collectors.joining("|")); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May| the wind| always be|at your back.|"); - assertEquals(list, List.of("May", + assertEquals("May| the wind| always be|at your back.|", text); + assertEquals(List.of("May", " the wind", " always be", - "at your back.", "")); - assertEquals(list, lines(body, null)); + "at your back.", ""), list); + assertEquals(lines(body, null), list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsStreamWithMixedCRLF_UTF16(String url) { String body = "May\r\n the wind\r\n always be\rat your back.\r\r"; HttpClient client = newClient(); @@ -404,18 +413,19 @@ public class LineBodyHandlerTest implements HttpServerAdapters { List list = stream.collect(Collectors.toList()); String text = list.stream().collect(Collectors.joining("|")); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May| the wind| always be|at your back.|"); - assertEquals(list, List.of("May", + assertEquals("May| the wind| always be|at your back.|", text); + assertEquals(List.of("May", " the wind", " always be", "at your back.", - "")); - assertEquals(list, lines(body, null)); + ""), list); + assertEquals(lines(body, null), list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithFinisher(String url) { String body = "May\r\n the wind\r\n always be\rat your back."; HttpClient client = newClient(); @@ -431,16 +441,17 @@ public class LineBodyHandlerTest implements HttpServerAdapters { HttpResponse response = cf.join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May\n the wind\n always be\rat your back."); - assertEquals(subscriber.list, List.of("May", + assertEquals("May\n the wind\n always be\rat your back.", text); + assertEquals(List.of("May", " the wind", - " always be\rat your back.")); - assertEquals(subscriber.list, lines(body, "\r\n")); + " always be\rat your back."), subscriber.list); + assertEquals(lines(body, "\r\n"), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithFinisher_UTF16(String url) { String body = "May\r\n the wind\r\n always be\rat your back.\r\r"; HttpClient client = newClient(); @@ -455,18 +466,19 @@ public class LineBodyHandlerTest implements HttpServerAdapters { HttpResponse response = cf.join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May\n the wind\n always be\nat your back.\n"); - assertEquals(subscriber.list, List.of("May", + assertEquals("May\n the wind\n always be\nat your back.\n", text); + assertEquals(List.of("May", " the wind", " always be", "at your back.", - "")); - assertEquals(subscriber.list, lines(body, null)); + ""), subscriber.list); + assertEquals(lines(body, null), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithoutFinisher(String url) { String body = "May\r\n the wind\r\n always be\rat your back."; HttpClient client = newClient(); @@ -482,17 +494,18 @@ public class LineBodyHandlerTest implements HttpServerAdapters { HttpResponse response = cf.join(); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May\n the wind\n always be\nat your back."); - assertEquals(subscriber.list, List.of("May", + assertEquals("May\n the wind\n always be\nat your back.", text); + assertEquals(List.of("May", " the wind", " always be", - "at your back.")); - assertEquals(subscriber.list, lines(body, null)); + "at your back."), subscriber.list); + assertEquals(lines(body, null), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithFinisherBlocking(String url) throws Exception { String body = "May\r\n the wind\r\n always be\nat your back."; HttpClient client = newClient(); @@ -507,16 +520,17 @@ public class LineBodyHandlerTest implements HttpServerAdapters { "\r\n")); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May\n the wind\n always be\nat your back."); - assertEquals(subscriber.list, List.of("May", + assertEquals("May\n the wind\n always be\nat your back.", text); + assertEquals(List.of("May", " the wind", - " always be\nat your back.")); - assertEquals(subscriber.list, lines(body, "\r\n")); + " always be\nat your back."), subscriber.list); + assertEquals(lines(body, "\r\n"), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testObjectWithoutFinisherBlocking(String url) throws Exception { String body = "May\r\n the wind\r\n always be\nat your back."; HttpClient client = newClient(); @@ -529,14 +543,14 @@ public class LineBodyHandlerTest implements HttpServerAdapters { BodyHandlers.fromLineSubscriber(subscriber)); String text = subscriber.get(); System.out.println(text); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(text.length() != 0); // what else can be asserted! - assertEquals(text, "May\n the wind\n always be\nat your back."); - assertEquals(subscriber.list, List.of("May", + assertEquals("May\n the wind\n always be\nat your back.", text); + assertEquals(List.of("May", " the wind", " always be", - "at your back.")); - assertEquals(subscriber.list, lines(body, null)); + "at your back."), subscriber.list); + assertEquals(lines(body, null), subscriber.list); } static private final String LINE = "Bient\u00f4t nous plongerons dans les" + @@ -551,7 +565,8 @@ public class LineBodyHandlerTest implements HttpServerAdapters { return res.toString(); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testBigTextFromLineSubscriber(String url) { HttpClient client = newClient(); String bigtext = bigtext(); @@ -567,12 +582,13 @@ public class LineBodyHandlerTest implements HttpServerAdapters { HttpResponse response = cf.join(); String text = response.body(); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, bigtext.replace("\r\n", "\n")); - assertEquals(subscriber.list, lines(bigtext, "\r\n")); + assertEquals(200, response.statusCode()); + assertEquals(bigtext.replace("\r\n", "\n"), text); + assertEquals(lines(bigtext, "\r\n"), subscriber.list); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testBigTextAsStream(String url) { HttpClient client = newClient(); String bigtext = bigtext(); @@ -588,10 +604,10 @@ public class LineBodyHandlerTest implements HttpServerAdapters { List list = stream.collect(Collectors.toList()); String text = list.stream().collect(Collectors.joining("|")); System.out.println(text); - assertEquals(response.statusCode(), 200); - assertEquals(text, bigtext.replace("\r\n", "|")); - assertEquals(list, List.of(bigtext.split("\r\n"))); - assertEquals(list, lines(bigtext, null)); + assertEquals(200, response.statusCode()); + assertEquals(bigtext.replace("\r\n", "|"), text); + assertEquals(List.of(bigtext.split("\r\n")), list); + assertEquals(lines(bigtext, null), list); } /** An abstract Subscriber that converts all received data into a String. */ @@ -675,8 +691,8 @@ public class LineBodyHandlerTest implements HttpServerAdapters { return Executors.newCachedThreadPool(factory); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1, null, executorFor("HTTP/1.1 Server Thread")); httpTestServer.addHandler(new HttpTestEchoHandler(), "/http1/echo"); @@ -706,8 +722,8 @@ public class LineBodyHandlerTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { sharedClient = null; try { System.gc(); diff --git a/test/jdk/java/net/httpclient/LineStreamsAndSurrogatesTest.java b/test/jdk/java/net/httpclient/LineStreamsAndSurrogatesTest.java index e082398f3c7..01a7ee9fdbf 100644 --- a/test/jdk/java/net/httpclient/LineStreamsAndSurrogatesTest.java +++ b/test/jdk/java/net/httpclient/LineStreamsAndSurrogatesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -37,10 +37,11 @@ import java.util.concurrent.SubmissionPublisher; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.testng.annotations.Test; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_16; -import static org.testng.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; /* * @test @@ -48,7 +49,7 @@ import static org.testng.Assert.assertEquals; * In particular tests that surrogate characters are handled * correctly. * @modules java.net.http java.logging - * @run testng/othervm LineStreamsAndSurrogatesTest + * @run junit/othervm LineStreamsAndSurrogatesTest */ public class LineStreamsAndSurrogatesTest { @@ -56,7 +57,7 @@ public class LineStreamsAndSurrogatesTest { static final Class NPE = NullPointerException.class; - private static final List lines(String text) { + private static List lines(String text) { return new BufferedReader(new StringReader(text)).lines().collect(Collectors.toList()); } @@ -100,14 +101,14 @@ public class LineStreamsAndSurrogatesTest { String resp2 = reader.lines().collect(Collectors.joining("")); System.out.println("***** Got2: " + resp2); - assertEquals(resp, resp2); - assertEquals(list, List.of("Bient\u00f4t", + assertEquals(resp2, resp); + assertEquals(List.of("Bient\u00f4t", " nous plongerons", " dans", " les", "", " fr\u00f4\ud801\udc00des", - " t\u00e9n\u00e8bres\ufffd")); + " t\u00e9n\u00e8bres\ufffd"), list); } catch (ExecutionException x) { Throwable cause = x.getCause(); if (cause instanceof MalformedInputException) { @@ -151,18 +152,18 @@ public class LineStreamsAndSurrogatesTest { List list = stream.collect(Collectors.toList()); String resp = list.stream().collect(Collectors.joining("|")); System.out.println("***** Got: " + resp); - assertEquals(resp, text.replace("\r\n", "|") + assertEquals(text.replace("\r\n", "|") .replace("\n","|") - .replace("\r","|")); - assertEquals(list, List.of("Bient\u00f4t", + .replace("\r","|"), resp); + assertEquals(List.of("Bient\u00f4t", " nous plongerons", " dans", "", " les", "", " fr\u00f4\ud801\udc00des", - " t\u00e9n\u00e8bres")); - assertEquals(list, lines(text)); + " t\u00e9n\u00e8bres"), list); + assertEquals(lines(text), list); if (errorRef.get() != null) { throw new RuntimeException("Unexpected exception", errorRef.get()); } @@ -201,15 +202,15 @@ public class LineStreamsAndSurrogatesTest { System.out.println("***** Got: " + resp); String expected = Stream.of(text.split("\r\n|\r|\n")) .collect(Collectors.joining("")); - assertEquals(resp, expected); - assertEquals(list, List.of("Bient\u00f4t", + assertEquals(expected, resp); + assertEquals(List.of("Bient\u00f4t", " nous plongerons", " dans", "", " les fr\u00f4\ud801\udc00des", " t\u00e9n\u00e8bres", - "")); - assertEquals(list, lines(text)); + ""), list); + assertEquals(lines(text), list); if (errorRef.get() != null) { throw new RuntimeException("Unexpected exception", errorRef.get()); } @@ -246,16 +247,16 @@ public class LineStreamsAndSurrogatesTest { List list = stream.collect(Collectors.toList()); String resp = list.stream().collect(Collectors.joining("")); System.out.println("***** Got: " + resp); - assertEquals(resp, text.replace("\n","").replace("\r","")); - assertEquals(list, List.of("Bient\u00f4t", + assertEquals(text.replace("\n","").replace("\r",""), resp); + assertEquals(List.of("Bient\u00f4t", " nous plongerons", " dans", "", " les", "", " fr\u00f4\ud801\udc00des", - " t\u00e9n\u00e8bres")); - assertEquals(list, lines(text)); + " t\u00e9n\u00e8bres"), list); + assertEquals(lines(text), list); if (errorRef.get() != null) { throw new RuntimeException("Unexpected exception", errorRef.get()); } @@ -294,15 +295,15 @@ public class LineStreamsAndSurrogatesTest { System.out.println("***** Got: " + resp); String expected = Stream.of(text.split("\r\n|\r|\n")) .collect(Collectors.joining("")); - assertEquals(resp, expected); - assertEquals(list, List.of("Bient\u00f4t", + assertEquals(expected, resp); + assertEquals(List.of("Bient\u00f4t", " nous plongerons", " dans", "", " les fr\u00f4\ud801\udc00des", " t\u00e9n\u00e8bres", - "")); - assertEquals(list, lines(text)); + ""), list); + assertEquals(lines(text), list); if (errorRef.get() != null) { throw new RuntimeException("Unexpected exception", errorRef.get()); } diff --git a/test/jdk/java/net/httpclient/LineSubscribersAndSurrogatesTest.java b/test/jdk/java/net/httpclient/LineSubscribersAndSurrogatesTest.java index 543ff6e94a1..7932744a4db 100644 --- a/test/jdk/java/net/httpclient/LineSubscribersAndSurrogatesTest.java +++ b/test/jdk/java/net/httpclient/LineSubscribersAndSurrogatesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,10 +42,11 @@ import java.util.concurrent.SubmissionPublisher; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.testng.annotations.Test; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_16; -import static org.testng.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; /* * @test @@ -53,7 +54,7 @@ import static org.testng.Assert.assertEquals; * In particular tests that surrogate characters are handled * correctly. * @modules java.net.http java.logging - * @run testng/othervm LineSubscribersAndSurrogatesTest + * @run junit/othervm LineSubscribersAndSurrogatesTest */ public class LineSubscribersAndSurrogatesTest { @@ -61,7 +62,7 @@ public class LineSubscribersAndSurrogatesTest { static final Class NPE = NullPointerException.class; - private static final List lines(String text, String eol) { + private static List lines(String text, String eol) { if (eol == null) { return new BufferedReader(new StringReader(text)).lines().collect(Collectors.toList()); } else { @@ -104,14 +105,14 @@ public class LineSubscribersAndSurrogatesTest { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); BufferedReader reader = new BufferedReader(new InputStreamReader(bais, UTF_8)); String resp2 = reader.lines().collect(Collectors.joining("")); - assertEquals(resp, resp2); - assertEquals(subscriber.list, List.of("Bient\u00f4t", + assertEquals(resp2, resp); + assertEquals(List.of("Bient\u00f4t", " nous plongerons", " dans", " les", "", " fr\u00f4\ud801\udc00des", - " t\u00e9n\u00e8bres\ufffd")); + " t\u00e9n\u00e8bres\ufffd"), subscriber.list); } catch (ExecutionException x) { Throwable cause = x.getCause(); if (cause instanceof MalformedInputException) { @@ -147,10 +148,10 @@ public class LineSubscribersAndSurrogatesTest { "", " fr\u00f4\ud801\udc00des\r", " t\u00e9n\u00e8bres\r"); - assertEquals(subscriber.list, expected); - assertEquals(resp, Stream.of(text.split("\n")).collect(Collectors.joining(""))); - assertEquals(resp, expected.stream().collect(Collectors.joining(""))); - assertEquals(subscriber.list, lines(text, "\n")); + assertEquals(expected, subscriber.list); + assertEquals(Stream.of(text.split("\n")).collect(Collectors.joining("")), resp); + assertEquals(expected.stream().collect(Collectors.joining("")), resp); + assertEquals(lines(text, "\n"), subscriber.list); } @@ -172,14 +173,14 @@ public class LineSubscribersAndSurrogatesTest { publisher.close(); String resp = bodySubscriber.getBody().toCompletableFuture().get(); System.out.println("***** Got: " + resp); - assertEquals(resp, text.replace("\r", "")); - assertEquals(subscriber.list, List.of("Bient\u00f4t", + assertEquals(text.replace("\r", ""), resp); + assertEquals(List.of("Bient\u00f4t", "\n nous plongerons", "\n dans", " les fr\u00f4\ud801\udc00des", "\n t\u00e9n\u00e8bres", - "")); - assertEquals(subscriber.list, lines(text, "\r")); + ""), subscriber.list); + assertEquals(lines(text, "\r"), subscriber.list); } @Test @@ -200,12 +201,12 @@ public class LineSubscribersAndSurrogatesTest { publisher.close(); String resp = bodySubscriber.getBody().toCompletableFuture().get(); System.out.println("***** Got: " + resp); - assertEquals(resp, text.replace("\r\n","")); - assertEquals(subscriber.list, List.of("Bient\u00f4t", + assertEquals(text.replace("\r\n",""), resp); + assertEquals(List.of("Bient\u00f4t", " nous plongerons", " dans\r les fr\u00f4\ud801\udc00des", - " t\u00e9n\u00e8bres")); - assertEquals(subscriber.list, lines(text, "\r\n")); + " t\u00e9n\u00e8bres"), subscriber.list); + assertEquals(lines(text, "\r\n"), subscriber.list); } @@ -234,9 +235,9 @@ public class LineSubscribersAndSurrogatesTest { "", " fr\u00f4\ud801\udc00des", " t\u00e9n\u00e8bres"); - assertEquals(subscriber.list, expected); - assertEquals(resp, expected.stream().collect(Collectors.joining(""))); - assertEquals(subscriber.list, lines(text, null)); + assertEquals(expected, subscriber.list); + assertEquals(expected.stream().collect(Collectors.joining("")), resp); + assertEquals(lines(text, null), subscriber.list); } @Test @@ -265,9 +266,9 @@ public class LineSubscribersAndSurrogatesTest { " fr\u00f4\ud801\udc00des", " t\u00e9n\u00e8bres", ""); - assertEquals(resp, expected.stream().collect(Collectors.joining(""))); - assertEquals(subscriber.list, expected); - assertEquals(subscriber.list, lines(text, null)); + assertEquals(expected.stream().collect(Collectors.joining("")), resp); + assertEquals(expected, subscriber.list); + assertEquals(lines(text, null), subscriber.list); } void testStringWithoutFinisherBR() throws Exception { @@ -293,9 +294,9 @@ public class LineSubscribersAndSurrogatesTest { "", " fr\u00f4\ud801\udc00des", " t\u00e9n\u00e8bres"); - assertEquals(subscriber.text, expected.stream().collect(Collectors.joining(""))); - assertEquals(subscriber.list, expected); - assertEquals(subscriber.list, lines(text, null)); + assertEquals(expected.stream().collect(Collectors.joining("")), subscriber.text); + assertEquals(expected, subscriber.list); + assertEquals(lines(text, null), subscriber.list); } diff --git a/test/jdk/java/net/httpclient/MappingResponseSubscriber.java b/test/jdk/java/net/httpclient/MappingResponseSubscriber.java index 445b4a59e24..75454923e49 100644 --- a/test/jdk/java/net/httpclient/MappingResponseSubscriber.java +++ b/test/jdk/java/net/httpclient/MappingResponseSubscriber.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -27,7 +27,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer * jdk.httpclient.test.lib.common.TestServerConfigurator - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * MappingResponseSubscriber */ @@ -64,30 +64,32 @@ import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.test.lib.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class MappingResponseSubscriber { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - Http2TestServer http2TestServer; // HTTP/2 ( h2c ) - Http2TestServer https2TestServer; // HTTP/2 ( h2 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; + private static HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpsServer httpsTestServer; // HTTPS/1.1 + private static Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + private static Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; static final int ITERATION_COUNT = 3; // a shared executor helps reduce the amount of threads created by the test @@ -95,8 +97,7 @@ public class MappingResponseSubscriber { static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI_fixed, false }, { httpURI_chunk, false }, @@ -125,7 +126,8 @@ public class MappingResponseSubscriber { .build(); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsBytes(String uri, boolean sameClient) throws Exception { HttpClient client = null; for (int i = 0; i < ITERATION_COUNT; i++) { @@ -137,7 +139,7 @@ public class MappingResponseSubscriber { BodyHandler handler = new CRSBodyHandler(); HttpResponse response = client.send(req, handler); byte[] body = response.body(); - assertEquals(body, bytes); + Assertions.assertArrayEquals(bytes, body); // if sameClient we will reuse the client for the next // operation, so there's nothing more to do. @@ -163,7 +165,7 @@ public class MappingResponseSubscriber { static class CRSBodyHandler implements BodyHandler { @Override public BodySubscriber apply(HttpResponse.ResponseInfo rinfo) { - assertEquals(rinfo.statusCode(), 200); + assertEquals(200, rinfo.statusCode()); return BodySubscribers.mapping( new CRSBodySubscriber(), (s) -> s.getBytes(UTF_8) ); @@ -213,8 +215,8 @@ public class MappingResponseSubscriber { + server.getAddress().getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 HttpHandler h1_fixedLengthHandler = new HTTP1_FixedLengthHandler(); HttpHandler h1_chunkHandler = new HTTP1_ChunkedHandler(); @@ -254,8 +256,8 @@ public class MappingResponseSubscriber { https2TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(0); httpsTestServer.stop(0); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/MaxStreams.java b/test/jdk/java/net/httpclient/MaxStreams.java index b25f931be9d..00875aceb6f 100644 --- a/test/jdk/java/net/httpclient/MaxStreams.java +++ b/test/jdk/java/net/httpclient/MaxStreams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,7 +27,7 @@ * @summary Should HttpClient support SETTINGS_MAX_CONCURRENT_STREAMS from the server * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm MaxStreams + * @run junit/othervm MaxStreams */ import java.io.IOException; @@ -53,24 +53,24 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class MaxStreams { - Http2TestServer http2TestServer; // HTTP/2 ( h2c ) - Http2TestServer https2TestServer; // HTTP/2 ( h2 ) - final Http2FixedHandler handler = new Http2FixedHandler(); + private static Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + private static Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + private static final Http2FixedHandler handler = new Http2FixedHandler(); private static final SSLContext ctx = SimpleSSLContext.findSSLContext(); - String http2FixedURI; - String https2FixedURI; - ExecutorService exec; + private static String http2FixedURI; + private static String https2FixedURI; + private static ExecutorService exec; // we send an initial warm up request, then MAX_STREAMS+1 requests // in parallel. The last of them should hit the limit. @@ -81,8 +81,7 @@ public class MaxStreams { static final int MAX_STREAMS = 10; static final String RESPONSE = "Hello world"; - @DataProvider(name = "uris") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ {http2FixedURI}, {https2FixedURI}, @@ -92,7 +91,8 @@ public class MaxStreams { } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsString(String uri) throws Exception { CountDownLatch latch = new CountDownLatch(1); handler.setLatch(latch); @@ -161,8 +161,8 @@ public class MaxStreams { System.err.println("Test OK"); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { exec = Executors.newCachedThreadPool(); InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); @@ -180,13 +180,13 @@ public class MaxStreams { https2TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { System.err.println("Stopping test server now"); http2TestServer.stop(); } - class Http2FixedHandler implements Http2Handler { + static class Http2FixedHandler implements Http2Handler { final AtomicInteger counter = new AtomicInteger(0); volatile CountDownLatch latch; diff --git a/test/jdk/java/net/httpclient/NoBodyPartOne.java b/test/jdk/java/net/httpclient/NoBodyPartOne.java index 7c7a51c92e7..980f4d8d100 100644 --- a/test/jdk/java/net/httpclient/NoBodyPartOne.java +++ b/test/jdk/java/net/httpclient/NoBodyPartOne.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -27,7 +27,7 @@ * @summary Test response body handlers/subscribers when there is no body * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=all * NoBodyPartOne @@ -42,16 +42,21 @@ import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodyHandlers; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +// @TestInstance(TestInstance.Lifecycle.PER_CLASS) +// is inherited from the super class public class NoBodyPartOne extends AbstractNoBody { - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsString(String uri, boolean sameClient) throws Exception { printStamp(START, "testAsString(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -70,12 +75,13 @@ public class NoBodyPartOne extends AbstractNoBody { : BodyHandlers.ofString(UTF_8); HttpResponse response = client.send(req, handler); String body = response.body(); - assertEquals(body, ""); + assertEquals("", body); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsFile(String uri, boolean sameClient) throws Exception { printStamp(START, "testAsFile(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -94,14 +100,15 @@ public class NoBodyPartOne extends AbstractNoBody { Path p = Paths.get("NoBody_testAsFile.txt"); HttpResponse response = client.send(req, BodyHandlers.ofFile(p)); Path bodyPath = response.body(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(Files.exists(bodyPath)); - assertEquals(Files.size(bodyPath), 0, Files.readString(bodyPath)); + assertEquals(0, Files.size(bodyPath), Files.readString(bodyPath)); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsByteArray(String uri, boolean sameClient) throws Exception { printStamp(START, "testAsByteArray(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -119,7 +126,7 @@ public class NoBodyPartOne extends AbstractNoBody { .build(); HttpResponse response = client.send(req, BodyHandlers.ofByteArray()); byte[] body = response.body(); - assertEquals(body.length, 0); + assertEquals(0, body.length); } } } diff --git a/test/jdk/java/net/httpclient/NoBodyPartThree.java b/test/jdk/java/net/httpclient/NoBodyPartThree.java index d5e310d1914..7020f7e2c25 100644 --- a/test/jdk/java/net/httpclient/NoBodyPartThree.java +++ b/test/jdk/java/net/httpclient/NoBodyPartThree.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -27,7 +27,7 @@ * @summary Test request and response body handlers/subscribers when there is no body * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=quic,errors * -Djdk.httpclient.HttpClient.log=all * NoBodyPartThree @@ -46,19 +46,24 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import jdk.internal.net.http.common.Utils; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +// @TestInstance(TestInstance.Lifecycle.PER_CLASS) +// is inherited from the super class public class NoBodyPartThree extends AbstractNoBody { static final AtomicInteger REQID = new AtomicInteger(); volatile boolean consumerHasBeenCalled; - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsByteArrayPublisher(String uri, boolean sameClient) throws Exception { printStamp(START, "testAsByteArrayPublisher(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -83,7 +88,7 @@ public class NoBodyPartThree extends AbstractNoBody { consumerHasBeenCalled = false; var response = client.send(req, BodyHandlers.ofByteArrayConsumer(consumer)); assertTrue(consumerHasBeenCalled); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); u = uri + "/testAsByteArrayPublisher/second/" + REQID.getAndIncrement(); req = newRequestBuilder(u + "?echo") @@ -93,12 +98,13 @@ public class NoBodyPartThree extends AbstractNoBody { consumerHasBeenCalled = false; response = client.send(req, BodyHandlers.ofByteArrayConsumer(consumer)); assertTrue(consumerHasBeenCalled); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testStringPublisher(String uri, boolean sameClient) throws Exception { printStamp(START, "testStringPublisher(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -116,14 +122,15 @@ public class NoBodyPartThree extends AbstractNoBody { .build(); System.out.println("sending " + req); HttpResponse response = client.send(req, BodyHandlers.ofInputStream()); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); byte[] body = response.body().readAllBytes(); - assertEquals(body.length, 0); + assertEquals(0, body.length); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testInputStreamPublisherBuffering(String uri, boolean sameClient) throws Exception { printStamp(START, "testInputStreamPublisherBuffering(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -142,14 +149,15 @@ public class NoBodyPartThree extends AbstractNoBody { System.out.println("sending " + req); HttpResponse response = client.send(req, BodyHandlers.buffering(BodyHandlers.ofByteArray(), 1024)); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); byte[] body = response.body(); - assertEquals(body.length, 0); + assertEquals(0, body.length); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testEmptyArrayPublisher(String uri, boolean sameClient) throws Exception { printStamp(START, "testEmptyArrayPublisher(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -167,8 +175,8 @@ public class NoBodyPartThree extends AbstractNoBody { .build(); System.out.println("sending " + req); var response = client.send(req, BodyHandlers.ofLines()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body().toList(), List.of()); + assertEquals(200, response.statusCode()); + assertEquals(List.of(), response.body().toList()); } } } diff --git a/test/jdk/java/net/httpclient/NoBodyPartTwo.java b/test/jdk/java/net/httpclient/NoBodyPartTwo.java index f7d331cb526..9e7ceb11c55 100644 --- a/test/jdk/java/net/httpclient/NoBodyPartTwo.java +++ b/test/jdk/java/net/httpclient/NoBodyPartTwo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -27,7 +27,7 @@ * @summary Test response body handlers/subscribers when there is no body * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=all * NoBodyPartTwo @@ -44,17 +44,22 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import jdk.internal.net.http.common.Utils; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +// @TestInstance(TestInstance.Lifecycle.PER_CLASS) +// is inherited from the super class public class NoBodyPartTwo extends AbstractNoBody { volatile boolean consumerHasBeenCalled; - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsByteArrayConsumer(String uri, boolean sameClient) throws Exception { printStamp(START, "testAsByteArrayConsumer(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -81,7 +86,8 @@ public class NoBodyPartTwo extends AbstractNoBody { } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testAsInputStream(String uri, boolean sameClient) throws Exception { printStamp(START, "testAsInputStream(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -98,12 +104,13 @@ public class NoBodyPartTwo extends AbstractNoBody { .build(); HttpResponse response = client.send(req, BodyHandlers.ofInputStream()); byte[] body = response.body().readAllBytes(); - assertEquals(body.length, 0); + assertEquals(0, body.length); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testBuffering(String uri, boolean sameClient) throws Exception { printStamp(START, "testBuffering(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -121,12 +128,13 @@ public class NoBodyPartTwo extends AbstractNoBody { HttpResponse response = client.send(req, BodyHandlers.buffering(BodyHandlers.ofByteArray(), 1024)); byte[] body = response.body(); - assertEquals(body.length, 0); + assertEquals(0, body.length); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testDiscard(String uri, boolean sameClient) throws Exception { printStamp(START, "testDiscard(\"%s\", %s)", uri, sameClient); HttpClient client = null; @@ -143,7 +151,7 @@ public class NoBodyPartTwo extends AbstractNoBody { .build(); Object obj = new Object(); HttpResponse response = client.send(req, BodyHandlers.replacing(obj)); - assertEquals(response.body(), obj); + assertEquals(obj, response.body()); } } } diff --git a/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java b/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java index 0a0da6366ad..2e53479a6ee 100644 --- a/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java +++ b/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * @compile -encoding utf-8 NonAsciiCharsInURI.java - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=requests,headers,errors,quic * NonAsciiCharsInURI */ @@ -51,10 +51,6 @@ import java.util.List; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.err; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; @@ -63,24 +59,29 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class NonAsciiCharsInURI implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; - String http3URI_head; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; + private static String http3URI_head; - private volatile HttpClient sharedClient; + private static volatile HttpClient sharedClient; // € = '\u20AC' => 0xE20x820xAC static final String[][] pathsAndQueryStrings = new String[][] { @@ -93,8 +94,7 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { { "/006/x?url=https://ja.wikipedia.org/wiki/エリザベス1世_(イングランド女王)" }, }; - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { List list = new ArrayList<>(); for (boolean sameClient : new boolean[] { false, true }) { @@ -136,7 +136,7 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { return null; } - HttpRequest.Builder newRequestBuilder(String uri) { + static HttpRequest.Builder newRequestBuilder(String uri) { var builder = HttpRequest.newBuilder(URI.create(uri)); if (version(uri) == HTTP_3) { builder.version(HTTP_3); @@ -145,7 +145,7 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { return builder; } - HttpResponse headRequest(HttpClient client) + static HttpResponse headRequest(HttpClient client) throws IOException, InterruptedException { out.println("\n" + now() + "--- Sending HEAD request ----\n"); @@ -154,25 +154,26 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { var request = newRequestBuilder(http3URI_head) .HEAD().version(HTTP_2).build(); var response = client.send(request, BodyHandlers.ofString()); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_2); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_2, response.version()); out.println("\n" + now() + "--- HEAD request succeeded ----\n"); err.println("\n" + now() + "--- HEAD request succeeded ----\n"); return response; } - private HttpClient makeNewClient() { - return newClientBuilderForH3() + private static HttpClient makeNewClient() { + return HttpServerAdapters.createClientBuilderForH3() .proxy(NO_PROXY) .sslContext(sslContext) .build(); } - HttpClient newHttpClient(boolean share) { + private static final Object zis = new Object(); + static HttpClient newHttpClient(boolean share) { if (!share) return makeNewClient(); HttpClient shared = sharedClient; if (shared != null) return shared; - synchronized (this) { + synchronized (zis) { shared = sharedClient; if (shared == null) { shared = sharedClient = makeNewClient(); @@ -191,7 +192,8 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { static final int ITERATION_COUNT = 3; // checks upgrade and re-use - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void test(String uriString, boolean sameClient) throws Exception { out.println("\n--- Starting "); // The single-argument factory requires any illegal characters in its @@ -215,7 +217,7 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { out.println("Got response: " + resp); out.println("Got body: " + resp.body()); - assertEquals(resp.statusCode(), 200, + assertEquals(200, resp.statusCode(), "Expected 200, got:" + resp.statusCode()); // the response body should contain the toASCIIString @@ -228,12 +230,13 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { } else { out.println("Found expected " + resp.body() + " in " + expectedURIString); } - assertEquals(resp.version(), version(uriString)); + assertEquals(version(uriString), resp.version()); } } } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void testAsync(String uriString, boolean sameClient) throws Exception { out.println("\n--- Starting "); URI uri = URI.create(uriString); @@ -254,8 +257,8 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { .thenApply(response -> { out.println("Got response: " + response); out.println("Got body: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), version(uriString)); + assertEquals(200, response.statusCode()); + assertEquals(version(uriString), response.version()); return response.body(); }) .thenAccept(body -> { @@ -276,8 +279,8 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println(now() + "begin setup"); HttpTestHandler handler = new HttpUriStringHandler(); @@ -324,8 +327,8 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { err.println(now() + "setup done"); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { sharedClient.close(); httpTestServer.stop(); httpsTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/RedirectMethodChange.java b/test/jdk/java/net/httpclient/RedirectMethodChange.java index b0b130921f5..73e27f6efff 100644 --- a/test/jdk/java/net/httpclient/RedirectMethodChange.java +++ b/test/jdk/java/net/httpclient/RedirectMethodChange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ * @summary Method change during redirection * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm RedirectMethodChange + * @run junit/othervm RedirectMethodChange */ import javax.net.ssl.SSLContext; @@ -41,33 +41,34 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.US_ASCII; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class RedirectMethodChange implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpClient client; + private static HttpClient client; - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; static final String RESPONSE = "Hello world"; static final String POST_BODY = "This is the POST body 123909090909090"; @@ -86,8 +87,7 @@ public class RedirectMethodChange implements HttpServerAdapters { } } - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][] { { http3URI, "GET", 301, "GET" }, { http3URI, "GET", 302, "GET" }, @@ -180,7 +180,8 @@ public class RedirectMethodChange implements HttpServerAdapters { return builder; } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void test(String uriString, String method, int redirectCode, @@ -195,15 +196,15 @@ public class RedirectMethodChange implements HttpServerAdapters { HttpResponse resp = client.send(req, BodyHandlers.ofString()); System.out.println("Response: " + resp + ", body: " + resp.body()); - assertEquals(resp.statusCode(), 200); - assertEquals(resp.body(), RESPONSE); + assertEquals(200, resp.statusCode()); + assertEquals(RESPONSE, resp.body()); } // -- Infrastructure - @BeforeTest - public void setup() throws Exception { - client = newClientBuilderForH3() + @BeforeAll + public static void setup() throws Exception { + client = HttpServerAdapters.createClientBuilderForH3() .followRedirects(HttpClient.Redirect.NORMAL) .sslContext(sslContext) .build(); @@ -245,8 +246,8 @@ public class RedirectMethodChange implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { client.close(); httpTestServer.stop(); httpsTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/RedirectTimeoutTest.java b/test/jdk/java/net/httpclient/RedirectTimeoutTest.java index e51f394f845..8c2fa8707ae 100644 --- a/test/jdk/java/net/httpclient/RedirectTimeoutTest.java +++ b/test/jdk/java/net/httpclient/RedirectTimeoutTest.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 @@ -29,16 +29,11 @@ * an HttpTimeoutException during the redirected request. * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.httpclient.HttpClient.log=errors,trace -Djdk.internal.httpclient.debug=false RedirectTimeoutTest + * @run junit/othervm -Djdk.httpclient.HttpClient.log=errors,trace -Djdk.internal.httpclient.debug=false RedirectTimeoutTest */ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.TestException; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.OutputStream; @@ -64,6 +59,12 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static jdk.test.lib.Utils.adjustTimeout; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.fail; + public class RedirectTimeoutTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); @@ -75,8 +76,8 @@ public class RedirectTimeoutTest implements HttpServerAdapters { public static final int ITERATIONS = 4; private static final PrintStream out = System.out; - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { h1TestServer = HttpTestServer.create(HTTP_1_1); h2TestServer = HttpTestServer.create(HTTP_2); h3TestServer = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); @@ -101,15 +102,14 @@ public class RedirectTimeoutTest implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { h1TestServer.stop(); h2TestServer.stop(); h3TestServer.stop(); } - @DataProvider(name = "testData") - public Object[][] testData() { + public static Object[][] testData() { return new Object[][] { { HTTP_3, h3Uri, h3RedirectUri }, { HTTP_1_1, h1Uri, h1RedirectUri }, @@ -117,7 +117,8 @@ public class RedirectTimeoutTest implements HttpServerAdapters { }; } - @Test(dataProvider = "testData") + @ParameterizedTest + @MethodSource("testData") public void test(Version version, URI uri, URI redirectURI) throws InterruptedException { out.println("Testing for " + version); testRedirectURI = redirectURI; @@ -159,7 +160,7 @@ public class RedirectTimeoutTest implements HttpServerAdapters { } catch (IOException e) { if (e.getClass() == HttpTimeoutException.class) { e.printStackTrace(System.out); - throw new TestException("Timeout from original HttpRequest expired on redirect when it should have been cancelled."); + fail("Timeout from original HttpRequest expired on redirect when it should have been cancelled."); } else { throw new RuntimeException(e); } diff --git a/test/jdk/java/net/httpclient/RedirectWithCookie.java b/test/jdk/java/net/httpclient/RedirectWithCookie.java index b4a4df38721..ad94b124132 100644 --- a/test/jdk/java/net/httpclient/RedirectWithCookie.java +++ b/test/jdk/java/net/httpclient/RedirectWithCookie.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ * @summary Test for cookie handling when redirecting * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=trace,headers,requests * RedirectWithCookie */ @@ -45,10 +45,6 @@ import java.util.List; import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; @@ -56,28 +52,32 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class RedirectWithCookie implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; static final String MESSAGE = "BasicRedirectTest message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { http3URI, }, { httpURI, }, @@ -96,7 +96,8 @@ public class RedirectWithCookie implements HttpServerAdapters { return builder; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString) throws Exception { out.printf("%n---- starting (%s) ----%n", uriString); var builder = uriString.contains("/http3/") @@ -120,8 +121,8 @@ public class RedirectWithCookie implements HttpServerAdapters { out.println(" Got response: " + response); out.println(" Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MESSAGE); + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, response.body()); // asserts redirected URI in response.request().uri() assertTrue(response.uri().getPath().endsWith("message")); assertPreviousRedirectResponses(request, response); @@ -142,7 +143,7 @@ public class RedirectWithCookie implements HttpServerAdapters { response = response.previousResponse().get(); assertTrue(300 <= response.statusCode() && response.statusCode() <= 309, "Expected 300 <= code <= 309, got:" + response.statusCode()); - assertEquals(response.body(), null, "Unexpected body: " + response.body()); + assertEquals(null, response.body(), "Unexpected body: " + response.body()); String locationHeader = response.headers().firstValue("Location") .orElseThrow(() -> new RuntimeException("no previous Location")); assertTrue(uri.toString().endsWith(locationHeader), @@ -151,15 +152,15 @@ public class RedirectWithCookie implements HttpServerAdapters { } while (response.previousResponse().isPresent()); // initial - assertEquals(initialRequest, response.request(), + assertEquals(response.request(), initialRequest, String.format("Expected initial request [%s] to equal last prev req [%s]", initialRequest, response.request())); } // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new CookieRedirectHandler(), "/http1/cookie/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/cookie/redirect"; @@ -185,8 +186,8 @@ public class RedirectWithCookie implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/RequestBodyTest.java b/test/jdk/java/net/httpclient/RequestBodyTest.java index e43336610f0..e87cbc694c7 100644 --- a/test/jdk/java/net/httpclient/RequestBodyTest.java +++ b/test/jdk/java/net/httpclient/RequestBodyTest.java @@ -47,11 +47,11 @@ import static java.lang.System.out; import static java.nio.charset.StandardCharsets.*; import static java.nio.file.StandardOpenOption.*; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test @@ -62,18 +62,18 @@ import static org.testng.Assert.*; * @build LightWeightHttpServer * @build jdk.test.lib.Platform * @build jdk.test.lib.util.FileUtils - * @run testng/othervm RequestBodyTest + * @run junit/othervm RequestBodyTest */ public class RequestBodyTest { static final String fileroot = System.getProperty("test.src", ".") + "/docs"; static final String midSizedFilename = "/files/notsobigfile.txt"; static final String smallFilename = "/files/smallfile.txt"; - final ConcurrentHashMap failures = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap failures = new ConcurrentHashMap<>(); - HttpClient client; - String httpURI; - String httpsURI; + private static HttpClient client; + private static String httpURI; + private static String httpsURI; enum RequestBody { BYTE_ARRAY, @@ -95,8 +95,8 @@ public class RequestBodyTest { STRING_WITH_CHARSET, } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { LightWeightHttpServer.initServer(); httpURI = LightWeightHttpServer.httproot + "echo/foo"; httpsURI = LightWeightHttpServer.httpsroot + "echo/foo"; @@ -109,8 +109,8 @@ public class RequestBodyTest { .build(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { try { LightWeightHttpServer.stop(); } finally { @@ -127,8 +127,7 @@ public class RequestBodyTest { } } - @DataProvider - public Object[][] exchanges() throws Exception { + public static Object[][] exchanges() throws Exception { List values = new ArrayList<>(); for (boolean async : new boolean[] { false, true }) @@ -143,7 +142,8 @@ public class RequestBodyTest { return values.stream().toArray(Object[][]::new); } - @Test(dataProvider = "exchanges") + @ParameterizedTest + @MethodSource("exchanges") void exchange(String target, RequestBody requestBodyType, ResponseBody responseBodyType, @@ -239,8 +239,8 @@ public class RequestBodyTest { BodyHandler bh = BodyHandlers.ofByteArray(); if (bufferResponseBody) bh = BodyHandlers.buffering(bh, 50); HttpResponse bar = getResponse(client, request, bh, async); - assertEquals(bar.statusCode(), 200); - assertEquals(bar.body(), fileAsBytes); + assertEquals(200, bar.statusCode()); + assertArrayEquals(fileAsBytes, bar.body()); break; case BYTE_ARRAY_CONSUMER: ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -249,46 +249,46 @@ public class RequestBodyTest { if (bufferResponseBody) bh1 = BodyHandlers.buffering(bh1, 49); HttpResponse v = getResponse(client, request, bh1, async); byte[] ba = baos.toByteArray(); - assertEquals(v.statusCode(), 200); - assertEquals(ba, fileAsBytes); + assertEquals(200, v.statusCode()); + assertArrayEquals(fileAsBytes, ba); break; case DISCARD: Object o = new Object(); BodyHandler bh2 = BodyHandlers.replacing(o); if (bufferResponseBody) bh2 = BodyHandlers.buffering(bh2, 51); HttpResponse or = getResponse(client, request, bh2, async); - assertEquals(or.statusCode(), 200); + assertEquals(200, or.statusCode()); assertSame(or.body(), o); break; case FILE: BodyHandler bh3 = BodyHandlers.ofFile(tempFile); if (bufferResponseBody) bh3 = BodyHandlers.buffering(bh3, 48); HttpResponse fr = getResponse(client, request, bh3, async); - assertEquals(fr.statusCode(), 200); - assertEquals(Files.size(tempFile), fileAsString.length()); - assertEquals(Files.readAllBytes(tempFile), fileAsBytes); + assertEquals(200, fr.statusCode()); + assertEquals(fileAsString.length(), Files.size(tempFile)); + assertArrayEquals(fileAsBytes, Files.readAllBytes(tempFile)); break; case FILE_WITH_OPTION: BodyHandler bh4 = BodyHandlers.ofFile(tempFile, CREATE_NEW, WRITE); if (bufferResponseBody) bh4 = BodyHandlers.buffering(bh4, 52); fr = getResponse(client, request, bh4, async); - assertEquals(fr.statusCode(), 200); - assertEquals(Files.size(tempFile), fileAsString.length()); - assertEquals(Files.readAllBytes(tempFile), fileAsBytes); + assertEquals(200, fr.statusCode()); + assertEquals(fileAsString.length(), Files.size(tempFile)); + assertArrayEquals(fileAsBytes, Files.readAllBytes(tempFile)); break; case STRING: BodyHandler bh5 = BodyHandlers.ofString(); if(bufferResponseBody) bh5 = BodyHandlers.buffering(bh5, 47); HttpResponse sr = getResponse(client, request, bh5, async); - assertEquals(sr.statusCode(), 200); - assertEquals(sr.body(), fileAsString); + assertEquals(200, sr.statusCode()); + assertEquals(fileAsString, sr.body()); break; case STRING_WITH_CHARSET: BodyHandler bh6 = BodyHandlers.ofString(StandardCharsets.UTF_8); if (bufferResponseBody) bh6 = BodyHandlers.buffering(bh6, 53); HttpResponse r = getResponse(client, request, bh6, async); - assertEquals(r.statusCode(), 200); - assertEquals(r.body(), fileAsString); + assertEquals(200, r.statusCode()); + assertEquals(fileAsString, r.body()); break; default: throw new AssertionError("Unknown response body:" + responseBodyType); diff --git a/test/jdk/java/net/httpclient/RequestBuilderTest.java b/test/jdk/java/net/httpclient/RequestBuilderTest.java index a83c12f592e..12af908457b 100644 --- a/test/jdk/java/net/httpclient/RequestBuilderTest.java +++ b/test/jdk/java/net/httpclient/RequestBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,7 +25,7 @@ * @test * @bug 8276559 * @summary HttpRequest[.Builder] API and behaviour checks - * @run testng RequestBuilderTest + * @run junit RequestBuilderTest */ import java.net.URI; @@ -49,9 +49,9 @@ import static java.time.Duration.ofNanos; import static java.time.Duration.ofMinutes; import static java.time.Duration.ofSeconds; import static java.time.Duration.ZERO; -import static org.testng.Assert.*; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class RequestBuilderTest { @@ -70,12 +70,12 @@ public class RequestBuilderTest { newBuilder(uri).copy()); for (HttpRequest.Builder builder : builders) { assertFalse(builder.build().expectContinue()); - assertEquals(builder.build().method(), "GET"); + assertEquals("GET", builder.build().method()); assertFalse(builder.build().bodyPublisher().isPresent()); assertFalse(builder.build().version().isPresent()); assertFalse(builder.build().timeout().isPresent()); assertTrue(builder.build().headers() != null); - assertEquals(builder.build().headers().map().size(), 0); + assertEquals(0, builder.build().headers().map().size()); } } @@ -128,61 +128,61 @@ public class RequestBuilderTest { assertThrows(IAE, () -> newBuilder().uri(u)); } - assertEquals(newBuilder(uri).build().uri(), uri); - assertEquals(newBuilder().uri(uri).build().uri(), uri); + assertEquals(uri, newBuilder(uri).build().uri()); + assertEquals(uri, newBuilder().uri(uri).build().uri()); URI https = URI.create("https://foo.com"); - assertEquals(newBuilder(https).build().uri(), https); - assertEquals(newBuilder().uri(https).build().uri(), https); + assertEquals(https, newBuilder(https).build().uri()); + assertEquals(https, newBuilder().uri(https).build().uri()); } @Test public void testMethod() { HttpRequest request = newBuilder(uri).build(); - assertEquals(request.method(), "GET"); + assertEquals("GET", request.method()); assertTrue(!request.bodyPublisher().isPresent()); request = newBuilder(uri).GET().build(); - assertEquals(request.method(), "GET"); + assertEquals("GET", request.method()); assertTrue(!request.bodyPublisher().isPresent()); request = newBuilder(uri).POST(BodyPublishers.ofString("")).GET().build(); - assertEquals(request.method(), "GET"); + assertEquals("GET", request.method()); assertTrue(!request.bodyPublisher().isPresent()); request = newBuilder(uri).PUT(BodyPublishers.ofString("")).GET().build(); - assertEquals(request.method(), "GET"); + assertEquals("GET", request.method()); assertTrue(!request.bodyPublisher().isPresent()); request = newBuilder(uri).DELETE().GET().build(); - assertEquals(request.method(), "GET"); + assertEquals("GET", request.method()); assertTrue(!request.bodyPublisher().isPresent()); request = newBuilder(uri).POST(BodyPublishers.ofString("")).build(); - assertEquals(request.method(), "POST"); + assertEquals("POST", request.method()); assertTrue(request.bodyPublisher().isPresent()); request = newBuilder(uri).PUT(BodyPublishers.ofString("")).build(); - assertEquals(request.method(), "PUT"); + assertEquals("PUT", request.method()); assertTrue(request.bodyPublisher().isPresent()); request = newBuilder(uri).DELETE().build(); - assertEquals(request.method(), "DELETE"); + assertEquals("DELETE", request.method()); assertTrue(!request.bodyPublisher().isPresent()); request = newBuilder(uri).HEAD().build(); - assertEquals(request.method(), "HEAD"); + assertEquals("HEAD", request.method()); assertFalse(request.bodyPublisher().isPresent()); request = newBuilder(uri).GET().POST(BodyPublishers.ofString("")).build(); - assertEquals(request.method(), "POST"); + assertEquals("POST", request.method()); assertTrue(request.bodyPublisher().isPresent()); request = newBuilder(uri).GET().PUT(BodyPublishers.ofString("")).build(); - assertEquals(request.method(), "PUT"); + assertEquals("PUT", request.method()); assertTrue(request.bodyPublisher().isPresent()); request = newBuilder(uri).GET().DELETE().build(); - assertEquals(request.method(), "DELETE"); + assertEquals("DELETE", request.method()); assertTrue(!request.bodyPublisher().isPresent()); // CONNECT is disallowed in the implementation, since it is used for @@ -190,11 +190,11 @@ public class RequestBuilderTest { assertThrows(IAE, () -> newBuilder(uri).method("CONNECT", BodyPublishers.noBody()).build()); request = newBuilder(uri).method("GET", BodyPublishers.noBody()).build(); - assertEquals(request.method(), "GET"); + assertEquals("GET", request.method()); assertTrue(request.bodyPublisher().isPresent()); request = newBuilder(uri).method("POST", BodyPublishers.ofString("")).build(); - assertEquals(request.method(), "POST"); + assertEquals("POST", request.method()); assertTrue(request.bodyPublisher().isPresent()); } @@ -207,7 +207,7 @@ public class RequestBuilderTest { assertThrows(IAE, () -> builder.headers("1").build()); assertThrows(IAE, () -> builder.headers("1", "2", "3").build()); assertThrows(IAE, () -> builder.headers("1", "2", "3", "4", "5").build()); - assertEquals(builder.build().headers().map().size(),0); + assertEquals(0, builder.build().headers().map().size()); List requests = List.of( // same header built from different combinations of the API @@ -219,13 +219,13 @@ public class RequestBuilderTest { ); for (HttpRequest r : requests) { - assertEquals(r.headers().map().size(), 1); + assertEquals(1, r.headers().map().size()); assertTrue(r.headers().firstValue("A").isPresent()); assertTrue(r.headers().firstValue("a").isPresent()); - assertEquals(r.headers().firstValue("A").get(), "B"); - assertEquals(r.headers().allValues("A"), List.of("B")); - assertEquals(r.headers().allValues("C").size(), 0); - assertEquals(r.headers().map().get("A"), List.of("B")); + assertEquals("B", r.headers().firstValue("A").get()); + assertEquals(List.of("B"), r.headers().allValues("A")); + assertEquals(0, r.headers().allValues("C").size()); + assertEquals(List.of("B"), r.headers().map().get("A")); assertThrows(NFE, () -> r.headers().firstValueAsLong("A")); assertFalse(r.headers().firstValue("C").isPresent()); // a non-exhaustive list of mutators @@ -265,14 +265,14 @@ public class RequestBuilderTest { ); for (HttpRequest r : requests) { - assertEquals(r.headers().map().size(), 2); + assertEquals(2, r.headers().map().size()); assertTrue(r.headers().firstValue("A").isPresent()); - assertEquals(r.headers().firstValue("A").get(), "B"); - assertEquals(r.headers().allValues("A"), List.of("B")); + assertEquals("B", r.headers().firstValue("A").get()); + assertEquals(List.of("B"), r.headers().allValues("A")); assertTrue(r.headers().firstValue("C").isPresent()); - assertEquals(r.headers().firstValue("C").get(), "D"); - assertEquals(r.headers().allValues("C"), List.of("D")); - assertEquals(r.headers().map().get("C"), List.of("D")); + assertEquals("D", r.headers().firstValue("C").get()); + assertEquals(List.of("D"), r.headers().allValues("C")); + assertEquals(List.of("D"), r.headers().map().get("C")); assertThrows(NFE, () -> r.headers().firstValueAsLong("C")); assertFalse(r.headers().firstValue("E").isPresent()); // a smaller non-exhaustive list of mutators @@ -304,11 +304,11 @@ public class RequestBuilderTest { ); for (HttpRequest r : requests) { - assertEquals(r.headers().map().size(), 1); + assertEquals(1, r.headers().map().size()); assertTrue(r.headers().firstValue("A").isPresent()); assertTrue(r.headers().allValues("A").containsAll(List.of("B", "C"))); - assertEquals(r.headers().allValues("C").size(), 0); - assertEquals(r.headers().map().get("A"), List.of("B", "C")); + assertEquals(0, r.headers().allValues("C").size()); + assertEquals(List.of("B", "C"), r.headers().map().get("A")); assertThrows(NFE, () -> r.headers().firstValueAsLong("A")); assertFalse(r.headers().firstValue("C").isPresent()); // a non-exhaustive list of mutators @@ -340,11 +340,11 @@ public class RequestBuilderTest { "aCCept-EnCODing", "accepT-encodinG")) { assertTrue(r.headers().firstValue(name).isPresent()); assertTrue(r.headers().allValues(name).contains("gzip, deflate")); - assertEquals(r.headers().firstValue(name).get(), "gzip, deflate"); - assertEquals(r.headers().allValues(name).size(), 1); - assertEquals(r.headers().map().size(), 1); - assertEquals(r.headers().map().get(name).size(), 1); - assertEquals(r.headers().map().get(name).get(0), "gzip, deflate"); + assertEquals("gzip, deflate", r.headers().firstValue(name).get()); + assertEquals(1, r.headers().allValues(name).size()); + assertEquals(1, r.headers().map().size()); + assertEquals(1, r.headers().map().get(name).size()); + assertEquals("gzip, deflate", r.headers().map().get(name).get(0)); } } } @@ -391,7 +391,7 @@ public class RequestBuilderTest { .GET(), "x-" + name, value).build(); String v = req.headers().firstValue("x-" + name).orElseThrow( () -> new RuntimeException("header x-" + name + " not set")); - assertEquals(v, value); + assertEquals(value, v); try { f.withHeader(HttpRequest.newBuilder(uri) .GET(), name, value).build(); @@ -419,27 +419,27 @@ public class RequestBuilderTest { builder.GET().timeout(ofSeconds(5)).version(HTTP_2).setHeader("A", "C"); HttpRequest copyRequest = copy.build(); - assertEquals(copyRequest.uri(), uri); - assertEquals(copyRequest.expectContinue(), true); - assertEquals(copyRequest.headers().map().get("A"), List.of("B")); - assertEquals(copyRequest.method(), "POST"); - assertEquals(copyRequest.bodyPublisher().isPresent(), true); - assertEquals(copyRequest.timeout().get(), ofSeconds(30)); + assertEquals(uri, copyRequest.uri()); + assertEquals(true, copyRequest.expectContinue()); + assertEquals(List.of("B"), copyRequest.headers().map().get("A")); + assertEquals("POST", copyRequest.method()); + assertEquals(true, copyRequest.bodyPublisher().isPresent()); + assertEquals(ofSeconds(30), copyRequest.timeout().get()); assertTrue(copyRequest.version().isPresent()); - assertEquals(copyRequest.version().get(), HTTP_1_1); + assertEquals(HTTP_1_1, copyRequest.version().get()); assertTrue(copyRequest.getOption(H3_DISCOVERY).isPresent()); - assertEquals(copyRequest.getOption(H3_DISCOVERY).get(), HTTP_3_URI_ONLY); + assertEquals(HTTP_3_URI_ONLY, copyRequest.getOption(H3_DISCOVERY).get()); // lazy set URI ( maybe builder as a template ) copyRequest = newBuilder().copy().uri(uri).build(); - assertEquals(copyRequest.uri(), uri); + assertEquals(uri, copyRequest.uri()); builder = newBuilder().header("C", "D"); copy = builder.copy(); copy.uri(uri); copyRequest = copy.build(); - assertEquals(copyRequest.uri(), uri); - assertEquals(copyRequest.headers().firstValue("C").get(), "D"); + assertEquals(uri, copyRequest.uri()); + assertEquals("D", copyRequest.headers().firstValue("C").get()); } @Test @@ -449,42 +449,41 @@ public class RequestBuilderTest { assertThrows(IAE, () -> builder.timeout(ofSeconds(0))); assertThrows(IAE, () -> builder.timeout(ofSeconds(-1))); assertThrows(IAE, () -> builder.timeout(ofNanos(-100))); - assertEquals(builder.timeout(ofNanos(15)).build().timeout().get(), ofNanos(15)); - assertEquals(builder.timeout(ofSeconds(50)).build().timeout().get(), ofSeconds(50)); - assertEquals(builder.timeout(ofMinutes(30)).build().timeout().get(), ofMinutes(30)); + assertEquals(ofNanos(15), builder.timeout(ofNanos(15)).build().timeout().get()); + assertEquals(ofSeconds(50), builder.timeout(ofSeconds(50)).build().timeout().get()); + assertEquals(ofMinutes(30), builder.timeout(ofMinutes(30)).build().timeout().get()); } @Test public void testExpect() { HttpRequest.Builder builder = newBuilder(uri); - assertEquals(builder.build().expectContinue(), false); - assertEquals(builder.expectContinue(true).build().expectContinue(), true); - assertEquals(builder.expectContinue(false).build().expectContinue(), false); - assertEquals(builder.expectContinue(true).build().expectContinue(), true); + assertEquals(false, builder.build().expectContinue()); + assertEquals(true, builder.expectContinue(true).build().expectContinue()); + assertEquals(false, builder.expectContinue(false).build().expectContinue()); + assertEquals(true, builder.expectContinue(true).build().expectContinue()); } @Test public void testEquals() { - assertNotEquals(newBuilder(URI.create("http://foo.com")), - newBuilder(URI.create("http://bar.com"))); + assertNotEquals( newBuilder(URI.create("http://bar.com")), newBuilder(URI.create("http://foo.com"))); HttpRequest.Builder builder = newBuilder(uri); assertEquals(builder.build(), builder.build()); - assertEquals(builder.build(), newBuilder(uri).build()); + assertEquals(newBuilder(uri).build(), builder.build()); builder.POST(BodyPublishers.noBody()); assertEquals(builder.build(), builder.build()); - assertEquals(builder.build(), newBuilder(uri).POST(BodyPublishers.noBody()).build()); - assertEquals(builder.build(), newBuilder(uri).POST(BodyPublishers.ofString("")).build()); - assertNotEquals(builder.build(), newBuilder(uri).build()); - assertNotEquals(builder.build(), newBuilder(uri).GET().build()); - assertNotEquals(builder.build(), newBuilder(uri).PUT(BodyPublishers.noBody()).build()); + assertEquals(newBuilder(uri).POST(BodyPublishers.noBody()).build(), builder.build()); + assertEquals(newBuilder(uri).POST(BodyPublishers.ofString("")).build(), builder.build()); + assertNotEquals(newBuilder(uri).build(), builder.build()); + assertNotEquals(newBuilder(uri).GET().build(), builder.build()); + assertNotEquals(newBuilder(uri).PUT(BodyPublishers.noBody()).build(), builder.build()); builder = newBuilder(uri).header("x", "y"); assertEquals(builder.build(), builder.build()); - assertEquals(builder.build(), newBuilder(uri).header("x", "y").build()); - assertNotEquals(builder.build(), newBuilder(uri).header("x", "Z").build()); - assertNotEquals(builder.build(), newBuilder(uri).header("z", "y").build()); + assertEquals(newBuilder(uri).header("x", "y").build(), builder.build()); + assertNotEquals(newBuilder(uri).header("x", "Z").build(), builder.build()); + assertNotEquals(newBuilder(uri).header("z", "y").build(), builder.build()); } @Test diff --git a/test/jdk/java/net/httpclient/Response1xxTest.java b/test/jdk/java/net/httpclient/Response1xxTest.java index 5cff7a69259..9b54f852510 100644 --- a/test/jdk/java/net/httpclient/Response1xxTest.java +++ b/test/jdk/java/net/httpclient/Response1xxTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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,10 +42,6 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; @@ -54,39 +50,44 @@ import static java.net.http.HttpClient.Version.valueOf; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -/** +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/* * @test * @bug 8292044 * @summary Tests behaviour of HttpClient when server responds with 102 or 103 status codes * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=headers,requests,responses,errors Response1xxTest */ public class Response1xxTest implements HttpServerAdapters { private static final String EXPECTED_RSP_BODY = "Hello World"; - private ServerSocket serverSocket; - private Http11Server server; - private String http1RequestURIBase; + private static ServerSocket serverSocket; + private static Http11Server server; + private static String http1RequestURIBase; - private HttpTestServer http2Server; // h2c - private String http2RequestURIBase; + private static HttpTestServer http2Server; // h2c + private static String http2RequestURIBase; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer https2Server; // h2 - private String https2RequestURIBase; + private static HttpTestServer https2Server; // h2 + private static String https2RequestURIBase; - private HttpTestServer http3Server; // h3 - private String http3RequestURIBase; + private static HttpTestServer http3Server; // h3 + private static String http3RequestURIBase; - private final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - @BeforeClass - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { serverSocket = new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); server = new Http11Server(serverSocket); new Thread(server).start(); @@ -130,8 +131,8 @@ public class Response1xxTest implements HttpServerAdapters { } - @AfterClass - public void teardown() throws Throwable { + @AfterAll + public static void teardown() throws Throwable { try { assertNoOutstandingClientOps(); } finally { @@ -269,7 +270,7 @@ public class Response1xxTest implements HttpServerAdapters { static String readRequestLine(final Socket sock) throws IOException { final InputStream is = sock.getInputStream(); - final StringBuilder sb = new StringBuilder(""); + final StringBuilder sb = new StringBuilder(); byte[] buf = new byte[1024]; while (!sb.toString().endsWith("\r\n\r\n")) { final int numRead = is.read(buf); @@ -424,10 +425,10 @@ public class Response1xxTest implements HttpServerAdapters { System.out.println("Issuing request to " + requestURI); final HttpResponse response = client.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); - Assert.assertEquals(response.version(), version, + Assertions.assertEquals(version, response.version(), "Unexpected HTTP version in response"); - Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); - Assert.assertEquals(response.body(), EXPECTED_RSP_BODY, "Unexpected response body"); + Assertions.assertEquals(200, response.statusCode(), "Unexpected response code"); + Assertions.assertEquals(EXPECTED_RSP_BODY, response.body(), "Unexpected response body"); } } @@ -484,7 +485,7 @@ public class Response1xxTest implements HttpServerAdapters { .build(); System.out.println("Issuing request to " + requestURI); // we expect the request to timeout - Assert.assertThrows(HttpTimeoutException.class, () -> { + Assertions.assertThrows(HttpTimeoutException.class, () -> { client.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); }); } @@ -561,7 +562,7 @@ public class Response1xxTest implements HttpServerAdapters { final HttpRequest request = requestBuilder.build(); System.out.println("Issuing request to " + requestURI); // we expect the request to fail because the server sent an unexpected 101 - Assert.assertThrows(ProtocolException.class, + Assertions.assertThrows(ProtocolException.class, () -> client.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8))); } @@ -571,11 +572,11 @@ public class Response1xxTest implements HttpServerAdapters { final HttpRequest request = HttpRequest.newBuilder(requestURI).build(); System.out.println("Issuing (warmup) request to " + requestURI); final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); - Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); + Assertions.assertEquals(200, response.statusCode(), "Unexpected response code"); } // verifies that the HttpClient being tracked has no outstanding operations - private void assertNoOutstandingClientOps() throws AssertionError { + private static void assertNoOutstandingClientOps() throws AssertionError { System.gc(); final AssertionError refCheckFailure = TRACKER.check(1000); if (refCheckFailure != null) { diff --git a/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java b/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java index 123e47e2ac9..19393d99ee9 100644 --- a/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java +++ b/test/jdk/java/net/httpclient/ResponseBodyBeforeError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,7 +28,7 @@ * @modules java.net.http/jdk.internal.net.http.common * @library /test/lib * @build jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm/timeout=480 ResponseBodyBeforeError + * @run junit/othervm/timeout=480 ResponseBodyBeforeError */ import java.io.Closeable; @@ -54,10 +54,6 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.Flow; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocketFactory; @@ -67,28 +63,32 @@ import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpResponse.BodyHandlers.ofString; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ResponseBodyBeforeError { - ReplyingServer variableLengthServer; - ReplyingServer variableLengthHttpsServer; - ReplyingServer fixedLengthServer; - ReplyingServer fixedLengthHttpsServer; + private static ReplyingServer variableLengthServer; + private static ReplyingServer variableLengthHttpsServer; + private static ReplyingServer fixedLengthServer; + private static ReplyingServer fixedLengthHttpsServer; - String httpURIVarLen; - String httpsURIVarLen; - String httpURIFixLen; - String httpsURIFixLen; + private static String httpURIVarLen; + private static String httpsURIVarLen; + private static String httpURIFixLen; + private static String httpsURIFixLen; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); static final String EXPECTED_RESPONSE_BODY = "

Heading

Some Text

"; - @DataProvider(name = "sanity") - public Object[][] sanity() { + public static Object[][] sanity() { return new Object[][]{ { httpURIVarLen + "?length=all" }, { httpsURIVarLen + "?length=all" }, @@ -97,7 +97,8 @@ public class ResponseBodyBeforeError { }; } - @Test(dataProvider = "sanity") + @ParameterizedTest + @MethodSource("sanity") void sanity(String url) throws Exception { HttpClient client = HttpClient.newBuilder() .proxy(NO_PROXY) @@ -106,15 +107,14 @@ public class ResponseBodyBeforeError { HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); HttpResponse response = client.send(request, ofString()); String body = response.body(); - assertEquals(body, EXPECTED_RESPONSE_BODY); + assertEquals(EXPECTED_RESPONSE_BODY, body); client.sendAsync(request, ofString()) .thenApply(resp -> resp.body()) - .thenAccept(b -> assertEquals(b, EXPECTED_RESPONSE_BODY)) + .thenAccept(b -> assertEquals(EXPECTED_RESPONSE_BODY, b)) .join(); } - @DataProvider(name = "uris") - public Object[][] variants() { + public static Object[][] variants() { Object[][] cases = new Object[][] { // The length query string is the total number of response body // bytes in the reply, before the server closes the connection. The @@ -182,7 +182,8 @@ public class ResponseBodyBeforeError { static final int ITERATION_COUNT = 3; static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testSynchronousAllRequestBody(String url, String expectedPatrialBody, boolean sameClient) @@ -210,7 +211,7 @@ public class ResponseBodyBeforeError { } catch (IOException expected) { String pm = bs.receivedAsString(); out.println("partial body received: " + pm); - assertEquals(pm, expectedPatrialBody); + assertEquals(expectedPatrialBody, pm); } } } finally { @@ -221,7 +222,8 @@ public class ResponseBodyBeforeError { } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsynchronousAllRequestBody(String url, String expectedPatrialBody, boolean sameClient) @@ -250,7 +252,7 @@ public class ResponseBodyBeforeError { if (ee.getCause() instanceof IOException) { String pm = bs.receivedAsString(); out.println("partial body received: " + pm); - assertEquals(pm, expectedPatrialBody); + assertEquals(expectedPatrialBody, pm); } else { throw ee; } @@ -536,8 +538,8 @@ public class ResponseBodyBeforeError { + server.getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { SSLContext.setDefault(sslContext); variableLengthServer = new PlainVariableLengthServer(); @@ -557,8 +559,8 @@ public class ResponseBodyBeforeError { + "/https1/fixed/foz"; } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { variableLengthServer.close(); variableLengthHttpsServer.close(); fixedLengthServer.close(); diff --git a/test/jdk/java/net/httpclient/ResponsePublisher.java b/test/jdk/java/net/httpclient/ResponsePublisher.java index ce86a0a3cf5..be135e84b31 100644 --- a/test/jdk/java/net/httpclient/ResponsePublisher.java +++ b/test/jdk/java/net/httpclient/ResponsePublisher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,22 +28,16 @@ * immediately with a Publisher> * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm/timeout=480 ResponsePublisher + * @run junit/othervm/timeout=480 ResponsePublisher */ -import com.sun.net.httpserver.HttpServer; import jdk.internal.net.http.common.OperationTrackers; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.InetAddress; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -73,28 +67,33 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ResponsePublisher implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI_fixed; - String httpURI_chunk; - String httpsURI_fixed; - String httpsURI_chunk; - String http2URI_fixed; - String http2URI_chunk; - String https2URI_fixed; - String https2URI_chunk; - String http3URI_fixed; - String http3URI_chunk; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI_fixed; + private static String httpURI_chunk; + private static String httpsURI_fixed; + private static String httpsURI_chunk; + private static String http2URI_fixed; + private static String http2URI_chunk; + private static String https2URI_fixed; + private static String https2URI_chunk; + private static String http3URI_fixed; + private static String http3URI_chunk; static final int ITERATION_COUNT = 3; // a shared executor helps reduce the amount of threads created by the test @@ -139,8 +138,7 @@ public class ResponsePublisher implements HttpServerAdapters { static final Supplier>>> OF_PUBLISHER_TEST = BHS.of(PublishingBodyHandler::new, "PublishingBodyHandler::new"); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { http3URI_fixed, false, OF_PUBLISHER_API }, { http3URI_chunk, false, OF_PUBLISHER_API }, @@ -190,7 +188,7 @@ public class ResponsePublisher implements HttpServerAdapters { }; } - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; HttpClient newHttpClient(String uri) { var builder = uri.contains("/http3/") ? newClientBuilderForH3() @@ -210,8 +208,9 @@ public class ResponsePublisher implements HttpServerAdapters { return builder; } - @Test(dataProvider = "variants") - public void testExceptions(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testExceptions(String uri, boolean sameClient, BHS handlers) throws Exception { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) @@ -247,7 +246,7 @@ public class ResponsePublisher implements HttpServerAdapters { } // Get the final result and compare it with the expected body String body = ofString.getBody().toCompletableFuture().get(); - assertEquals(body, ""); + assertEquals("", body); // ensure client closes before next iteration if (!sameClient) { var tracker = TRACKER.getTracker(client); @@ -257,8 +256,9 @@ public class ResponsePublisher implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testNoBody(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testNoBody(String uri, boolean sameClient, BHS handlers) throws Exception { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) @@ -276,7 +276,7 @@ public class ResponsePublisher implements HttpServerAdapters { response.body().subscribe(ofString); // Get the final result and compare it with the expected body String body = ofString.getBody().toCompletableFuture().get(); - assertEquals(body, ""); + assertEquals("", body); // ensure client closes before next iteration if (!sameClient) { var tracker = TRACKER.getTracker(client); @@ -286,8 +286,9 @@ public class ResponsePublisher implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testNoBodyAsync(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testNoBodyAsync(String uri, boolean sameClient, BHS handlers) throws Exception { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) @@ -308,7 +309,7 @@ public class ResponsePublisher implements HttpServerAdapters { return ofString.getBody(); }); // Get the final result and compare it with the expected body - assertEquals(result.get(), ""); + assertEquals("", result.get()); // ensure client closes before next iteration if (!sameClient) { var tracker = TRACKER.getTracker(client); @@ -318,8 +319,9 @@ public class ResponsePublisher implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testAsString(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testAsString(String uri, boolean sameClient, BHS handlers) throws Exception { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) @@ -337,7 +339,7 @@ public class ResponsePublisher implements HttpServerAdapters { response.body().subscribe(ofString); // Get the final result and compare it with the expected body String body = ofString.getBody().toCompletableFuture().get(); - assertEquals(body, WITH_BODY); + assertEquals(WITH_BODY, body); // ensure client closes before next iteration if (!sameClient) { var tracker = TRACKER.getTracker(client); @@ -347,8 +349,9 @@ public class ResponsePublisher implements HttpServerAdapters { } } - @Test(dataProvider = "variants") - public void testAsStringAsync(String uri, boolean sameClient, BHS handlers) throws Exception { + @ParameterizedTest + @MethodSource("variants") + void testAsStringAsync(String uri, boolean sameClient, BHS handlers) throws Exception { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { if (!sameClient || client == null) @@ -369,7 +372,7 @@ public class ResponsePublisher implements HttpServerAdapters { }); // Get the final result and compare it with the expected body String body = result.get(); - assertEquals(body, WITH_BODY); + assertEquals(WITH_BODY, body); // ensure client closes before next iteration if (!sameClient) { var tracker = TRACKER.getTracker(client); @@ -383,7 +386,7 @@ public class ResponsePublisher implements HttpServerAdapters { static class PublishingBodyHandler implements BodyHandler>> { @Override public BodySubscriber>> apply(HttpResponse.ResponseInfo rinfo) { - assertEquals(rinfo.statusCode(), 200); + assertEquals(200, rinfo.statusCode()); return new PublishingBodySubscriber(); } } @@ -392,7 +395,7 @@ public class ResponsePublisher implements HttpServerAdapters { static class PublishingBodySubscriber implements BodySubscriber>> { private final CompletableFuture subscriptionCF = new CompletableFuture<>(); private final CompletableFuture>> subscribedCF = new CompletableFuture<>(); - private AtomicReference>> subscriberRef = new AtomicReference<>(); + private final AtomicReference>> subscriberRef = new AtomicReference<>(); private final CompletionStage>> body = subscriptionCF.thenCompose((s) -> CompletableFuture.completedStage(this::subscribe)); //CompletableFuture.completedStage(this::subscribe); @@ -449,13 +452,8 @@ public class ResponsePublisher implements HttpServerAdapters { } } - static String serverAuthority(HttpServer server) { - return InetAddress.getLoopbackAddress().getHostName() + ":" - + server.getAddress().getPort(); - } - - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 HttpTestHandler h1_fixedLengthHandler = new HTTP_FixedLengthHandler(); HttpTestHandler h1_chunkHandler = new HTTP_VariableLengthHandler(); @@ -504,8 +502,8 @@ public class ResponsePublisher implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.check(500); try { diff --git a/test/jdk/java/net/httpclient/RetryPost.java b/test/jdk/java/net/httpclient/RetryPost.java index ccd5dbb2922..67969306acc 100644 --- a/test/jdk/java/net/httpclient/RetryPost.java +++ b/test/jdk/java/net/httpclient/RetryPost.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,8 +24,8 @@ /* * @test * @summary Ensure that the POST method is retied when the property is set. - * @run testng/othervm -Djdk.httpclient.enableAllMethodRetry RetryPost - * @run testng/othervm -Djdk.httpclient.enableAllMethodRetry=true RetryPost + * @run junit/othervm -Djdk.httpclient.enableAllMethodRetry RetryPost + * @run junit/othervm -Djdk.httpclient.enableAllMethodRetry=true RetryPost */ import java.io.IOException; @@ -41,26 +41,26 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpResponse.BodyHandlers.ofString; import static java.nio.charset.StandardCharsets.US_ASCII; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class RetryPost { - FixedLengthServer fixedLengthServer; - String httpURIFixLen; + private static FixedLengthServer fixedLengthServer; + private static String httpURIFixLen; static final String RESPONSE_BODY = "You use a glass mirror to see your face: you use works of art to see your soul."; - @DataProvider(name = "uris") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][] { { httpURIFixLen, true }, { httpURIFixLen, false }, @@ -71,7 +71,8 @@ public class RetryPost { static final String REQUEST_BODY = "Body"; - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testSynchronousPOST(String url, boolean sameClient) throws Exception { out.print("---\n"); HttpClient client = null; @@ -84,12 +85,13 @@ public class RetryPost { HttpResponse response = client.send(request, ofString()); String body = response.body(); out.println(response + ": " + body); - assertEquals(response.statusCode(), 200); - assertEquals(body, RESPONSE_BODY); + assertEquals(200, response.statusCode()); + assertEquals(RESPONSE_BODY, body); } } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("variants") void testAsynchronousPOST(String url, boolean sameClient) { out.print("---\n"); HttpClient client = null; @@ -101,9 +103,9 @@ public class RetryPost { .build(); client.sendAsync(request, ofString()) .thenApply(r -> { out.println(r + ": " + r.body()); return r; }) - .thenApply(r -> { assertEquals(r.statusCode(), 200); return r; }) + .thenApply(r -> { assertEquals(200, r.statusCode()); return r; }) .thenApply(HttpResponse::body) - .thenAccept(b -> assertEquals(b, RESPONSE_BODY)) + .thenAccept(b -> assertEquals(RESPONSE_BODY, b)) .join(); } } @@ -223,15 +225,15 @@ public class RetryPost { + server.getPort(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { fixedLengthServer = new FixedLengthServer(); httpURIFixLen = "http://" + serverAuthority(fixedLengthServer) + "/http1/fixed/baz"; } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { fixedLengthServer.close(); } } diff --git a/test/jdk/java/net/httpclient/RetryWithCookie.java b/test/jdk/java/net/httpclient/RetryWithCookie.java index 782d8a0b956..8638693b50d 100644 --- a/test/jdk/java/net/httpclient/RetryWithCookie.java +++ b/test/jdk/java/net/httpclient/RetryWithCookie.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,16 +29,12 @@ * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=trace,headers,requests * RetryWithCookie */ import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -67,28 +63,32 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class RetryWithCookie implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; static final String MESSAGE = "BasicRedirectTest message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { http3URI, }, { httpURI, }, @@ -99,7 +99,7 @@ public class RetryWithCookie implements HttpServerAdapters { } static final AtomicLong requestCounter = new AtomicLong(); - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; private HttpRequest.Builder newRequestBuilder(URI uri) { var builder = HttpRequest.newBuilder(uri); @@ -110,7 +110,8 @@ public class RetryWithCookie implements HttpServerAdapters { return builder; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString) throws Exception { out.printf("%n---- starting (%s) ----%n", uriString); CookieManager cookieManager = new CookieManager(); @@ -145,9 +146,9 @@ public class RetryWithCookie implements HttpServerAdapters { out.println(" Got response: " + response); out.println(" Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MESSAGE); - assertEquals(response.headers().allValues("X-Request-Cookie"), cookies); + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, response.body()); + assertEquals(cookies, response.headers().allValues("X-Request-Cookie")); request = newRequestBuilder(uri) .header("X-uuid", "uuid-" + requestCounter.incrementAndGet()) .build(); @@ -156,8 +157,8 @@ public class RetryWithCookie implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new CookieRetryHandler(), "/http1/cookie/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/cookie/retry"; @@ -183,8 +184,8 @@ public class RetryWithCookie implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.check(500); try { diff --git a/test/jdk/java/net/httpclient/SSLExceptionTest.java b/test/jdk/java/net/httpclient/SSLExceptionTest.java index 4fa17c875c3..cf94d1b50f7 100644 --- a/test/jdk/java/net/httpclient/SSLExceptionTest.java +++ b/test/jdk/java/net/httpclient/SSLExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -24,9 +24,11 @@ import java.io.UncheckedIOException; import java.net.http.HttpClient; import java.security.NoSuchAlgorithmException; -import org.testng.annotations.Test; -import static org.testng.Assert.expectThrows; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; /* * @test @@ -34,7 +36,7 @@ import static org.testng.Assert.fail; * @summary This test verifies exception when resources for * SSLcontext used by HttpClient are not available * @build SSLExceptionTest - * @run testng/othervm -Djdk.tls.client.protocols="InvalidTLSv1.4" + * @run junit/othervm -Djdk.tls.client.protocols="InvalidTLSv1.4" * SSLExceptionTest */ @@ -47,12 +49,12 @@ public class SSLExceptionTest { @Test public void testHttpClientsslException() { for (int i = 0; i < ITERATIONS; i++) { - excp = expectThrows(UncheckedIOException.class, HttpClient.newBuilder()::build); + excp = Assertions.assertThrows(UncheckedIOException.class, HttpClient.newBuilder()::build); noSuchAlgo = excp.getCause().getCause(); if ( !(noSuchAlgo instanceof NoSuchAlgorithmException) ) { fail("Test failed due to wrong exception cause : " + noSuchAlgo); } - excp = expectThrows(UncheckedIOException.class, HttpClient::newHttpClient); + excp = Assertions.assertThrows(UncheckedIOException.class, HttpClient::newHttpClient); noSuchAlgo = excp.getCause().getCause(); if ( !(noSuchAlgo instanceof NoSuchAlgorithmException) ) { fail("Test failed due to wrong exception cause : " + noSuchAlgo); diff --git a/test/jdk/java/net/httpclient/SendResponseHeadersTest.java b/test/jdk/java/net/httpclient/SendResponseHeadersTest.java index df8bc3b0844..2998522616f 100644 --- a/test/jdk/java/net/httpclient/SendResponseHeadersTest.java +++ b/test/jdk/java/net/httpclient/SendResponseHeadersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -27,16 +27,13 @@ * @library /test/lib * @summary Check that sendResponseHeaders throws an IOException when headers * have already been sent - * @run testng/othervm SendResponseHeadersTest + * @run junit/othervm SendResponseHeadersTest */ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.URIBuilder; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; @@ -54,16 +51,21 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.expectThrows; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class SendResponseHeadersTest { - URI uri; - HttpServer server; - ExecutorService executor; + private static URI uri; + private static HttpServer server; + private static ExecutorService executor; - @BeforeTest - public void setUp() throws IOException, URISyntaxException { + @BeforeAll + public static void setUp() throws IOException, URISyntaxException { var loopback = InetAddress.getLoopbackAddress(); var addr = new InetSocketAddress(loopback, 0); server = HttpServer.create(addr, 0); @@ -94,8 +96,8 @@ public class SendResponseHeadersTest { fail(response.body()); } - @AfterTest - public void tearDown() { + @AfterAll + public static void tearDown() { server.stop(0); executor.shutdown(); } @@ -108,7 +110,7 @@ public class SendResponseHeadersTest { is.readAllBytes(); exchange.sendResponseHeaders(200, 0); try { - IOException io = expectThrows(IOException.class, + IOException io = Assertions.assertThrows(IOException.class, () -> exchange.sendResponseHeaders(200, 0)); System.out.println("Got expected exception: " + io); } catch (Throwable t) { diff --git a/test/jdk/java/net/httpclient/ServerCloseTest.java b/test/jdk/java/net/httpclient/ServerCloseTest.java index 65eb6cfc589..d5e762b15a4 100644 --- a/test/jdk/java/net/httpclient/ServerCloseTest.java +++ b/test/jdk/java/net/httpclient/ServerCloseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,16 +28,11 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm -Djdk.tls.acknowledgeCloseNotify=true ServerCloseTest + * @run junit/othervm -Djdk.tls.acknowledgeCloseNotify=true ServerCloseTest */ //* -Djdk.internal.httpclient.debug=true import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; @@ -70,18 +65,22 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import jdk.httpclient.test.lib.http2.Http2TestServer; import static java.lang.System.out; import static java.nio.charset.StandardCharsets.UTF_8; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class ServerCloseTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ] - DummyServer httpsDummyServer; // HTTPS/1.1 - String httpDummy; - String httpsDummy; + private static DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ] + private static DummyServer httpsDummyServer; // HTTPS/1.1 + private static String httpDummy; + private static String httpsDummy; static final int ITERATION_COUNT = 3; // a shared executor helps reduce the amount of threads created by the test @@ -99,7 +98,7 @@ public class ServerCloseTest implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - private volatile HttpClient sharedClient; + private static volatile HttpClient sharedClient; static class TestExecutor implements Executor { final AtomicLong tasks = new AtomicLong(); @@ -125,8 +124,8 @@ public class ServerCloseTest implements HttpServerAdapters { } } - @AfterClass - static final void printFailedTests() { + @AfterAll + static void printFailedTests() { out.println("\n========================="); try { out.printf("%n%sCreated %d servers and %d clients%n", @@ -145,15 +144,14 @@ public class ServerCloseTest implements HttpServerAdapters { } } - private String[] uris() { + private static String[] uris() { return new String[] { httpDummy, httpsDummy, }; } - @DataProvider(name = "servers") - public Object[][] noThrows() { + public static Object[][] noThrows() { String[] uris = uris(); Object[][] result = new Object[uris.length * 2][]; //Object[][] result = new Object[uris.length][]; @@ -192,7 +190,8 @@ public class ServerCloseTest implements HttpServerAdapters { final String ENCODED = "/01%252F03/"; - @Test(dataProvider = "servers") + @ParameterizedTest + @MethodSource("noThrows") public void testServerClose(String uri, boolean sameClient) { HttpClient client = null; out.printf("%n%s testServerClose(%s, %b)%n", now(), uri, sameClient); @@ -224,8 +223,8 @@ public class ServerCloseTest implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); // DummyServer @@ -240,8 +239,8 @@ public class ServerCloseTest implements HttpServerAdapters { httpsDummyServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { sharedClient = null; httpDummyServer.stopServer(); httpsDummyServer.stopServer(); @@ -324,7 +323,7 @@ public class ServerCloseTest implements HttpServerAdapters { // Read all headers until we find the empty line that // signals the end of all headers. String line = requestLine; - while (!line.equals("")) { + while (!line.isEmpty()) { System.out.println(now() + getName() + ": Reading header: " + (line = readLine(ccis))); headers.append(line).append("\r\n"); @@ -338,7 +337,7 @@ public class ServerCloseTest implements HttpServerAdapters { byte[] b = uri.toString().getBytes(UTF_8); if (index >= 0) { index = index + "content-length: ".length(); - String cl = headers.toString().substring(index); + String cl = headers.substring(index); StringTokenizer tk = new StringTokenizer(cl); int len = Integer.parseInt(tk.nextToken()); assert len < b.length * 2; diff --git a/test/jdk/java/net/httpclient/ShutdownNow.java b/test/jdk/java/net/httpclient/ShutdownNow.java index 4f9eabbeb4d..e48667d2c69 100644 --- a/test/jdk/java/net/httpclient/ShutdownNow.java +++ b/test/jdk/java/net/httpclient/ShutdownNow.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 @@ -32,7 +32,7 @@ * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * jdk.test.lib.RandomFactory jdk.test.lib.Utils * ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * ShutdownNow @@ -66,10 +66,6 @@ import javax.net.ssl.SSLContext; import jdk.test.lib.RandomFactory; import jdk.test.lib.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Builder.NO_PROXY; @@ -80,9 +76,14 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ShutdownNow implements HttpServerAdapters { @@ -92,25 +93,24 @@ public class ShutdownNow implements HttpServerAdapters { static final Random RANDOM = RandomFactory.getRandom(); private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) - HttpTestServer h3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String h2h3URI; - String h2h3Head; - String h3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer h2h3TestServer; // HTTP/3 ( h2 + h3 ) + private static HttpTestServer h3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String h2h3URI; + private static String h2h3Head; + private static String h3URI; static final String MESSAGE = "ShutdownNow message body"; static final int ITERATIONS = 3; - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { h2h3URI, HTTP_3, h2h3TestServer.h3DiscoveryConfig()}, { h3URI, HTTP_3, h3TestServer.h3DiscoveryConfig()}, @@ -122,7 +122,7 @@ public class ShutdownNow implements HttpServerAdapters { } static final AtomicLong requestCounter = new AtomicLong(); - final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; static Throwable getCause(Throwable t) { while (t instanceof CompletionException || t instanceof ExecutionException) { @@ -137,7 +137,7 @@ public class ShutdownNow implements HttpServerAdapters { .HEAD() .build(); var resp = client.send(request, BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } static boolean hasExpectedMessage(IOException io) { @@ -176,7 +176,8 @@ public class ShutdownNow implements HttpServerAdapters { throw new AssertionError(what + ": Unexpected exception: " + cause, cause); } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testConcurrent(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting concurrent (%s, %s, %s) ----%n%n", uriString, version, config); HttpClient client = newClientBuilderForH3() @@ -217,8 +218,8 @@ public class ShutdownNow implements HttpServerAdapters { var cf = responseCF.thenApply((response) -> { out.println(si + ": Got response: " + response); out.println(si + ": Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MESSAGE); + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, response.body()); return response; }).exceptionally((t) -> { Throwable cause = getCause(t); @@ -243,7 +244,8 @@ public class ShutdownNow implements HttpServerAdapters { } } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void testSequential(String uriString, Version version, Http3DiscoveryMode config) throws Exception { out.printf("%n---- starting sequential (%s, %s, %s) ----%n%n", uriString, version, config); @@ -284,8 +286,8 @@ public class ShutdownNow implements HttpServerAdapters { responseCF.thenApply((response) -> { out.println(si + ": Got response: " + response); out.println(si + ": Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MESSAGE); + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, response.body()); return response; }).handle((r,t) -> { if (t != null) { @@ -318,8 +320,8 @@ public class ShutdownNow implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { out.println("\n**** Setup ****\n"); httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new ServerRequestHandler(), "/http1/exec/"); @@ -352,8 +354,8 @@ public class ShutdownNow implements HttpServerAdapters { h3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { Thread.sleep(100); AssertionError fail = TRACKER.check(5000); try { diff --git a/test/jdk/java/net/httpclient/StreamCloseTest.java b/test/jdk/java/net/httpclient/StreamCloseTest.java index fcb00188581..7b228e69de2 100644 --- a/test/jdk/java/net/httpclient/StreamCloseTest.java +++ b/test/jdk/java/net/httpclient/StreamCloseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, NTT DATA. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -30,7 +30,7 @@ * @library /test/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm StreamCloseTest + * @run junit/othervm StreamCloseTest */ import java.io.InputStream; @@ -44,10 +44,10 @@ import java.net.http.HttpResponse.BodyHandlers; import java.net.URI; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import org.testng.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class StreamCloseTest { @@ -82,8 +82,8 @@ public class StreamCloseTest { private static HttpServerAdapters.HttpTestServer httpTestServer; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpServerAdapters.HttpTestServer.create(Version.HTTP_1_1); httpTestServer.addHandler(new HttpServerAdapters.HttpTestEchoHandler(), "/"); URI uri = URI.create("http://" + httpTestServer.serverAuthority() + "/"); @@ -96,8 +96,8 @@ public class StreamCloseTest { requestBuilder = HttpRequest.newBuilder(uri); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); } @@ -108,7 +108,7 @@ public class StreamCloseTest { .POST(BodyPublishers.ofInputStream(() -> in)) .build(); client.send(request, BodyHandlers.discarding()); - Assert.assertTrue(in.closeCalled, "InputStream was not closed!"); + Assertions.assertTrue(in.closeCalled, "InputStream was not closed!"); } @Test @@ -120,9 +120,9 @@ public class StreamCloseTest { try { client.send(request, BodyHandlers.discarding()); } catch (IOException e) { // expected - Assert.assertTrue(in.closeCalled, "InputStream was not closed!"); + Assertions.assertTrue(in.closeCalled, "InputStream was not closed!"); return; } - Assert.fail("IOException should be occurred!"); + Assertions.fail("IOException should be occurred!"); } } diff --git a/test/jdk/java/net/httpclient/SubscriberAPIExceptions.java b/test/jdk/java/net/httpclient/SubscriberAPIExceptions.java index bf0f7c0bda7..93f55d066e9 100644 --- a/test/jdk/java/net/httpclient/SubscriberAPIExceptions.java +++ b/test/jdk/java/net/httpclient/SubscriberAPIExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -36,26 +36,26 @@ import java.net.http.HttpResponse.BodySubscriber; import java.net.http.HttpResponse.BodySubscribers; import java.util.function.Function; -import org.testng.annotations.Test; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.DELETE_ON_CLOSE; import static java.nio.file.StandardOpenOption.WRITE; import static java.nio.file.StandardOpenOption.READ; -import static org.testng.Assert.assertThrows; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; /* * @test * @summary Basic tests for API specified exceptions from Handler, * and Subscriber convenience static factory methods. - * @run testng SubscriberAPIExceptions + * @run junit SubscriberAPIExceptions */ public class SubscriberAPIExceptions { static final Class NPE = NullPointerException.class; static final Class IAE = IllegalArgumentException.class; - static final Class IOB = IndexOutOfBoundsException.class; @Test public void handlerAPIExceptions() throws Exception { diff --git a/test/jdk/java/net/httpclient/TestKitTest.java b/test/jdk/java/net/httpclient/TestKitTest.java index 25f39d90305..2aba794426f 100644 --- a/test/jdk/java/net/httpclient/TestKitTest.java +++ b/test/jdk/java/net/httpclient/TestKitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -21,7 +21,6 @@ * questions. */ -import org.testng.annotations.Test; import java.io.IOException; import java.util.ArrayList; @@ -33,15 +32,16 @@ import java.util.LinkedList; import java.util.Map; import java.util.Set; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; /* * @test * @compile TestKit.java - * @run testng TestKitTest + * @run junit TestKitTest */ public final class TestKitTest { @@ -50,15 +50,14 @@ public final class TestKitTest { Integer integer = TestKit.assertNotThrows( () -> TestKit.assertNotThrows(() -> 1) ); - assertEquals(integer, Integer.valueOf(1)); + assertEquals(Integer.valueOf(1), integer); RuntimeException re = TestKit.assertThrows( RuntimeException.class, () -> TestKit.assertNotThrows(() -> { throw new IOException(); }) ); - assertEquals(re.getMessage(), - "Expected to run normally, but threw " - + "java.io.IOException"); + assertEquals("Expected to run normally, but threw " + + "java.io.IOException", re.getMessage()); TestKit.assertNotThrows( () -> TestKit.assertNotThrows(() -> { }) @@ -68,9 +67,8 @@ public final class TestKitTest { RuntimeException.class, () -> TestKit.assertNotThrows((TestKit.ThrowingProcedure) () -> { throw new IOException(); }) ); - assertEquals(re.getMessage(), - "Expected to run normally, but threw " - + "java.io.IOException"); + assertEquals("Expected to run normally, but threw " + + "java.io.IOException", re.getMessage()); } @Test @@ -87,13 +85,13 @@ public final class TestKitTest { () -> TestKit.assertThrows(IOException.class, null) ); assertNotNull(npe); - assertEquals(npe.getMessage(), "code"); + assertEquals("code", npe.getMessage()); npe = TestKit.assertThrows( NullPointerException.class, () -> TestKit.assertThrows(null, () -> { }) ); - assertEquals(npe.getMessage(), "clazz"); + assertEquals("clazz", npe.getMessage()); npe = TestKit.assertThrows( NullPointerException.class, @@ -101,16 +99,15 @@ public final class TestKitTest { ); assertNotNull(npe); assertNull(npe.getMessage()); - assertEquals(npe.getClass(), NullPointerException.class); + assertEquals(NullPointerException.class, npe.getClass()); RuntimeException re = TestKit.assertThrows( RuntimeException.class, () -> TestKit.assertThrows(NullPointerException.class, () -> { }) ); - assertEquals(re.getClass(), RuntimeException.class); - assertEquals(re.getMessage(), - "Expected to catch an exception of type " - + "java.lang.NullPointerException, but caught nothing"); + assertEquals(RuntimeException.class, re.getClass()); + assertEquals("Expected to catch an exception of type " + + "java.lang.NullPointerException, but caught nothing", re.getMessage()); re = TestKit.assertThrows( RuntimeException.class, @@ -118,7 +115,7 @@ public final class TestKitTest { ); assertNotNull(re); assertNull(re.getMessage()); - assertEquals(re.getClass(), NullPointerException.class); + assertEquals(NullPointerException.class, re.getClass()); re = TestKit.assertThrows( RuntimeException.class, @@ -127,10 +124,9 @@ public final class TestKitTest { () -> { throw new IndexOutOfBoundsException(); } )); assertNotNull(re); - assertEquals(re.getClass(), RuntimeException.class); - assertEquals(re.getMessage(), - "Expected to catch an exception of type java.util.IllegalFormatException" - + ", but caught java.lang.IndexOutOfBoundsException"); + assertEquals(RuntimeException.class, re.getClass()); + assertEquals("Expected to catch an exception of type java.util.IllegalFormatException" + + ", but caught java.lang.IndexOutOfBoundsException", re.getMessage()); } @Test diff --git a/test/jdk/java/net/httpclient/TlsContextTest.java b/test/jdk/java/net/httpclient/TlsContextTest.java index 8a5cc99aad3..95cb501d24c 100644 --- a/test/jdk/java/net/httpclient/TlsContextTest.java +++ b/test/jdk/java/net/httpclient/TlsContextTest.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 @@ -36,19 +36,22 @@ import javax.net.ssl.SSLParameters; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Version; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpResponse.BodyHandlers.ofString; -import static org.testng.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; + import jdk.test.lib.security.SecurityUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* * @test * @bug 8239594 8371887 @@ -56,7 +59,7 @@ import jdk.test.lib.security.SecurityUtils; * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext TlsContextTest * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm -Dtest.requiresHost=true + * @run junit/othervm -Dtest.requiresHost=true * -Djdk.httpclient.HttpClient.log=headers * -Djdk.internal.httpclient.disableHostnameVerification * -Djdk.internal.httpclient.debug=false @@ -67,11 +70,11 @@ public class TlsContextTest implements HttpServerAdapters { static HttpTestServer https2Server; static String https2URI; - SSLContext server; + static SSLContext server; final static Integer ITERATIONS = 3; - @BeforeTest - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { // Re-enable TLSv1 and TLSv1.1 since test depends on them SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLSv1.1"); @@ -86,8 +89,7 @@ public class TlsContextTest implements HttpServerAdapters { https2URI = "https://" + https2Server.serverAuthority() + "/server/"; } - @DataProvider(name = "scenarios") - public Object[][] scenarios() throws Exception { + public static Object[][] scenarios() throws Exception { return new Object[][]{ { SimpleSSLContext.findSSLContext("TLS"), HTTP_2, "TLSv1.3" }, { SimpleSSLContext.findSSLContext("TLSv1.2"), HTTP_2, "TLSv1.2" }, @@ -102,7 +104,8 @@ public class TlsContextTest implements HttpServerAdapters { /** * Tests various scenarios between client and server tls handshake with valid http */ - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") public void testVersionProtocolsNoParams(SSLContext context, Version version, String expectedProtocol) throws Exception { @@ -113,7 +116,8 @@ public class TlsContextTest implements HttpServerAdapters { * Tests various scenarios between client and server tls handshake with valid http, * but with empty SSLParameters */ - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") public void testVersionProtocolsEmptyParams(SSLContext context, Version version, String expectedProtocol) throws Exception { @@ -150,24 +154,24 @@ public class TlsContextTest implements HttpServerAdapters { private void testAllProtocols(HttpResponse response, String expectedProtocol, - Version clientVersion) throws Exception { + Version clientVersion) { String protocol = response.sslSession().get().getProtocol(); int statusCode = response.statusCode(); Version version = response.version(); out.println("Got Body " + response.body()); out.println("The protocol negotiated is :" + protocol); - assertEquals(statusCode, 200); - assertEquals(protocol, expectedProtocol); + assertEquals(200, statusCode); + assertEquals(expectedProtocol, protocol); if (clientVersion == HTTP_3) { - assertEquals(version, expectedProtocol.equals("TLSv1.1") ? HTTP_1_1 : - expectedProtocol.equals("TLSv1.2") ? HTTP_2 : HTTP_3); + assertEquals(expectedProtocol.equals("TLSv1.1") ? HTTP_1_1 : + expectedProtocol.equals("TLSv1.2") ? HTTP_2 : HTTP_3, version); } else { - assertEquals(version, expectedProtocol.equals("TLSv1.1") ? HTTP_1_1 : HTTP_2); + assertEquals(expectedProtocol.equals("TLSv1.1") ? HTTP_1_1 : HTTP_2, version); } } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { https2Server.stop(); } diff --git a/test/jdk/java/net/httpclient/UnauthorizedTest.java b/test/jdk/java/net/httpclient/UnauthorizedTest.java index 628130f9ffc..1c72bac6cc9 100644 --- a/test/jdk/java/net/httpclient/UnauthorizedTest.java +++ b/test/jdk/java/net/httpclient/UnauthorizedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,16 +32,12 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext ReferenceTracker - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=headers * UnauthorizedTest */ import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -65,24 +61,29 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class UnauthorizedTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; - HttpClient authClient; - HttpClient noAuthClient; + private static HttpTestServer httpTestServer; // HTTP/1.1 + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; + private static HttpClient authClient; + private static HttpClient noAuthClient; static final int ITERATIONS = 3; @@ -100,8 +101,7 @@ public class UnauthorizedTest implements HttpServerAdapters { return new WeakReference<>(client); } - @DataProvider(name = "all") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { http3URI + "/server", UNAUTHORIZED, true, ref(authClient)}, { http3URI + "/server", UNAUTHORIZED, false, ref(authClient)}, @@ -144,8 +144,6 @@ public class UnauthorizedTest implements HttpServerAdapters { }; } - static final AtomicLong requestCounter = new AtomicLong(); - static final Authenticator authenticator = new Authenticator() { }; @@ -158,7 +156,8 @@ public class UnauthorizedTest implements HttpServerAdapters { return builder; } - @Test(dataProvider = "all") + @ParameterizedTest + @MethodSource("positive") void test(String uriString, int code, boolean async, WeakReference clientRef) throws Throwable { HttpClient client = clientRef.get(); out.printf("%n---- starting (%s, %d, %s, %s) ----%n", @@ -195,9 +194,8 @@ public class UnauthorizedTest implements HttpServerAdapters { } out.println(" Got response: " + response); - assertEquals(response.statusCode(), code); - assertEquals(response.body(), - (code == UNAUTHORIZED ? "WWW-" : "Proxy-") + MESSAGE); + assertEquals(code, response.statusCode()); + assertEquals( (code == UNAUTHORIZED ? "WWW-" : "Proxy-") + MESSAGE, response.body()); if (shouldThrow) { throw new RuntimeException("Expected IOException not thrown."); } @@ -205,8 +203,8 @@ public class UnauthorizedTest implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new UnauthorizedHandler(), "/http1/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1"; @@ -225,13 +223,13 @@ public class UnauthorizedTest implements HttpServerAdapters { http3TestServer.addHandler(new UnauthorizedHandler(), "/http3/"); http3URI = "https://" + http3TestServer.serverAuthority() + "/http3"; - authClient = newClientBuilderForH3() + authClient = HttpServerAdapters.createClientBuilderForH3() .proxy(HttpClient.Builder.NO_PROXY) .sslContext(sslContext) .authenticator(authenticator) .build(); - noAuthClient = newClientBuilderForH3() + noAuthClient = HttpServerAdapters.createClientBuilderForH3() .proxy(HttpClient.Builder.NO_PROXY) .sslContext(sslContext) .build(); @@ -243,8 +241,8 @@ public class UnauthorizedTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { // authClient.close(); // noAuthClient.close(); var TRACKER = ReferenceTracker.INSTANCE; diff --git a/test/jdk/java/net/httpclient/UserCookieTest.java b/test/jdk/java/net/httpclient/UserCookieTest.java index 3d202914050..f49f44c157c 100644 --- a/test/jdk/java/net/httpclient/UserCookieTest.java +++ b/test/jdk/java/net/httpclient/UserCookieTest.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 @@ -28,7 +28,7 @@ * server-cookies for HTTP/2 vs HTTP/1.1 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm + * @run junit/othervm * -Djdk.tls.acknowledgeCloseNotify=true * -Djdk.httpclient.HttpClient.log=trace,headers,requests * UserCookieTest @@ -68,10 +68,6 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; @@ -80,25 +76,30 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class UserCookieTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 7 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - DummyServer httpDummyServer; - DummyServer httpsDummyServer; - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; - String httpDummy; - String httpsDummy; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 7 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static DummyServer httpDummyServer; + private static DummyServer httpsDummyServer; + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; + private static String httpDummy; + private static String httpsDummy; static final String MESSAGE = "Basic CookieHeaderTest message body"; static final int ITERATIONS = 3; @@ -111,8 +112,7 @@ public class UserCookieTest implements HttpServerAdapters { return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); } - @DataProvider(name = "positive") - public Object[][] positive() { + public static Object[][] positive() { return new Object[][] { { http3URI, HTTP_3 }, { httpURI, HTTP_1_1 }, @@ -130,7 +130,8 @@ public class UserCookieTest implements HttpServerAdapters { static final AtomicLong requestCounter = new AtomicLong(); - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString, HttpClient.Version version) throws Exception { out.printf("%n---- starting (%s) ----%n", uriString); ConcurrentHashMap> cookieHeaders @@ -178,12 +179,13 @@ public class UserCookieTest implements HttpServerAdapters { out.println(" Got response: " + response); out.println(" Got body Path: " + response.body()); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MESSAGE); - assertEquals(response.headers().allValues("X-Request-Cookie"), - expectedCookies.stream() - .filter(s -> !s.startsWith("LOC")) - .toList()); + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, response.body()); + List expectedCookieList = expectedCookies.stream() + .filter(s -> !s.startsWith("LOC")).toList(); + List actualCookieList = response.headers() + .allValues("X-Request-Cookie"); + assertEquals(expectedCookieList, actualCookieList); requestBuilder = HttpRequest.newBuilder(uri) .header("X-uuid", "uuid-" + requestCounter.incrementAndGet()) .header("Cookie", userCookie); @@ -200,8 +202,8 @@ public class UserCookieTest implements HttpServerAdapters { // -- Infrastructure - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new CookieValidationHandler(), "/http1/cookie/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/cookie/retry"; @@ -236,8 +238,8 @@ public class UserCookieTest implements HttpServerAdapters { httpsDummyServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); @@ -575,6 +577,5 @@ public class UserCookieTest implements HttpServerAdapters { return new DummyServer(ss, true); } - } } From 79456110fb6dd11ef19e9637c6f40ee7ce329481 Mon Sep 17 00:00:00 2001 From: Bradford Wetmore Date: Tue, 24 Feb 2026 19:27:24 +0000 Subject: [PATCH 018/636] 8377914: Typos in HKDFParameterSpec.Builder::thenExpand Reviewed-by: hchao --- .../javax/crypto/spec/HKDFParameterSpec.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/javax/crypto/spec/HKDFParameterSpec.java b/src/java.base/share/classes/javax/crypto/spec/HKDFParameterSpec.java index c4ca9e1e183..c5d4f4e5f8f 100644 --- a/src/java.base/share/classes/javax/crypto/spec/HKDFParameterSpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/HKDFParameterSpec.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 @@ -50,7 +50,8 @@ import java.util.Objects; *

* Examples: * {@snippet lang = java: - * // this usage depicts the initialization of an HKDF-Extract AlgorithmParameterSpec + * // this usage depicts the initialization of an HKDF-Extract + * // AlgorithmParameterSpec * AlgorithmParameterSpec derivationSpec = * HKDFParameterSpec.ofExtract() * .addIKM(label) @@ -58,12 +59,14 @@ import java.util.Objects; * .addSalt(salt).extractOnly(); *} * {@snippet lang = java: - * // this usage depicts the initialization of an HKDF-Expand AlgorithmParameterSpec + * // this usage depicts the initialization of an HKDF-Expand + * // AlgorithmParameterSpec * AlgorithmParameterSpec derivationSpec = * HKDFParameterSpec.expandOnly(prk, info, 32); *} * {@snippet lang = java: - * // this usage depicts the initialization of an HKDF-ExtractExpand AlgorithmParameterSpec + * // this usage depicts the initialization of an HKDF-ExtractExpand + * // AlgorithmParameterSpec * AlgorithmParameterSpec derivationSpec = * HKDFParameterSpec.ofExtract() * .addIKM(ikm) @@ -112,8 +115,8 @@ public interface HKDFParameterSpec extends AlgorithmParameterSpec { * * @implNote HKDF implementations will enforce that the length * is not greater than 255 * HMAC length. HKDF implementations - * will also enforce that a {code null} info value is treated as - * zero-length byte array. + * will also enforce that a {@code null} info value is + * treated as zero-length byte array. * * @param info * the optional context and application specific information @@ -261,8 +264,8 @@ public interface HKDFParameterSpec extends AlgorithmParameterSpec { * @implNote HKDF implementations will enforce that the length is * not greater than 255 * HMAC length. Implementations will also * enforce that the prk argument is at least as many bytes as the - * HMAC length. Implementations will also enforce that a {code null} - * info value is treated as zero-length byte array. + * HMAC length. Implementations will also enforce that a + * {@code null} info value is treated as zero-length byte array. * * @param prk * the pseudorandom key (PRK); must not be {@code null} @@ -358,7 +361,7 @@ public interface HKDFParameterSpec extends AlgorithmParameterSpec { * @param prk * the pseudorandom key (PRK); in the case of * {@code ExtractThenExpand}, the {@code prk} argument may be - * {@null} since the output of extract phase is used + * {@code null} since the output of extract phase is used * @param info * the optional context and application specific information * (may be {@code null}); the byte array is cloned to prevent From 46737815234a95226a54cbb7544f4367fa13fd86 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Tue, 24 Feb 2026 19:29:47 +0000 Subject: [PATCH 019/636] 8378549: Incorrect assert in JvmtiThreadState::update_for_pop_top_frame Reviewed-by: lmesnik --- src/hotspot/share/prims/jvmtiThreadState.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index fc965e568f7..f46feb95002 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -479,8 +479,6 @@ void JvmtiThreadState::update_for_pop_top_frame() { } // force stack depth to be recalculated invalidate_cur_stack_depth(); - } else { - assert(!is_enabled(JVMTI_EVENT_FRAME_POP), "Must have no framepops set"); } } From 9f89fa5b67cb05166fa0d396412a2d6c48ca0ca6 Mon Sep 17 00:00:00 2001 From: Leonid Mesnik Date: Tue, 24 Feb 2026 19:41:31 +0000 Subject: [PATCH 020/636] 8376295: "assert(BytecodeVerificationRemote) failed: Should not be here" when running class redefinition test with -XX:-BytecodeVerificationRemote Reviewed-by: dholmes, coleenp, sspitsyn --- src/hotspot/share/classfile/verifier.cpp | 5 +---- .../jvmti/RedefineClasses/RedefineVerifyError.java | 11 +++++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 30f147b9ae7..76d09161fdd 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -620,9 +620,6 @@ TypeOrigin ClassVerifier::ref_ctx(const char* sig) { void ClassVerifier::verify_class(TRAPS) { log_info(verification)("Verifying class %s with new format", _klass->external_name()); - // Either verifying both local and remote classes or just remote classes. - assert(BytecodeVerificationRemote, "Should not be here"); - Array* methods = _klass->methods(); int num_methods = methods->length(); diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.java index fa6bf0b98d4..eeedd9b583c 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.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 @@ -23,7 +23,7 @@ /* * @test - * @bug 6402717 8330606 + * @bug 6402717 8330606 8376295 * @summary Redefine VerifyError to get a VerifyError should not throw SOE * @requires vm.jvmti * @library /test/lib @@ -33,10 +33,17 @@ * java.instrument * jdk.jartool/sun.tools.jar * @run main RedefineClassHelper + * * @run main/othervm/timeout=180 * -javaagent:redefineagent.jar * -Xlog:class+init,exceptions * RedefineVerifyError + * + * @run main/othervm/timeout=180 + * -javaagent:redefineagent.jar + * -Xlog:class+init,exceptions + * -XX:-BytecodeVerificationRemote + * RedefineVerifyError */ import org.objectweb.asm.AnnotationVisitor; From 49158d354b0d31cb8821b9a35554fe46a388a036 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Tue, 24 Feb 2026 20:50:57 +0000 Subject: [PATCH 021/636] 8378387: Remove AppContext from several macOS AWT classes Reviewed-by: serb, dnguyen --- .../macosx/classes/sun/lwawt/LWToolkit.java | 9 ++------- .../sun/lwawt/macosx/CCheckboxMenuItem.java | 4 ++-- .../classes/sun/lwawt/macosx/CInputMethod.java | 8 ++++---- .../macosx/classes/sun/lwawt/macosx/CMenuItem.java | 4 ++-- .../macosx/classes/sun/lwawt/macosx/CTrayIcon.java | 4 ++-- .../classes/sun/lwawt/macosx/LWCToolkit.java | 14 +++++--------- 6 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java index 287de9e1801..0b5e37c5ce5 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -157,11 +157,6 @@ public abstract class LWToolkit extends SunToolkit implements Runnable { while (getRunState() < STATE_SHUTDOWN) { try { platformRunMessage(); - if (Thread.currentThread().isInterrupted()) { - if (AppContext.getAppContext().isDisposed()) { - break; - } - } } catch (Throwable t) { // TODO: log System.err.println("Exception on the toolkit thread"); @@ -456,7 +451,7 @@ public abstract class LWToolkit extends SunToolkit implements Runnable { public abstract LWCursorManager getCursorManager(); public static void postEvent(AWTEvent event) { - postEvent(targetToAppContext(event.getSource()), event); + SunToolkit.postEvent(event); } @Override diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java index 30ed1d1bba0..492c32b9703 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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,7 +58,7 @@ public final class CCheckboxMenuItem extends CMenuItem implements CheckboxMenuIt } }); ItemEvent event = new ItemEvent(target, ItemEvent.ITEM_STATE_CHANGED, target.getLabel(), state ? ItemEvent.SELECTED : ItemEvent.DESELECTED); - SunToolkit.postEvent(SunToolkit.targetToAppContext(getTarget()), event); + SunToolkit.postEvent(event); } public void setIsIndeterminate(final boolean indeterminate) { diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java index 1eb896bf291..dd0ac6e64f7 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -450,7 +450,7 @@ public final class CInputMethod extends InputMethodAdapter { aString.length(), theCaret, theCaret); - LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event); + LWCToolkit.postEvent(event); fCurrentText = null; fCurrentTextAsString = null; fCurrentTextLength = 0; @@ -563,7 +563,7 @@ public final class CInputMethod extends InputMethodAdapter { 0, theCaret, visiblePosition); - LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event); + LWCToolkit.postEvent(event); if (pressAndHold) selectNextGlyph(); } @@ -583,7 +583,7 @@ public final class CInputMethod extends InputMethodAdapter { fCurrentTextLength, theCaret, visiblePosition); - LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event); + LWCToolkit.postEvent(event); fCurrentText = null; fCurrentTextAsString = null; fCurrentTextLength = 0; diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java index c4b2640efb8..9e3fcdb0600 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -157,7 +157,7 @@ public class CMenuItem extends CMenuComponent implements MenuItemPeer { public void run() { final String cmd = ((MenuItem)getTarget()).getActionCommand(); final ActionEvent event = new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, cmd, when, modifiers); - SunToolkit.postEvent(SunToolkit.targetToAppContext(getTarget()), event); + SunToolkit.postEvent(event); } }); } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTrayIcon.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTrayIcon.java index 77639a262d5..a25b3177063 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTrayIcon.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTrayIcon.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -224,7 +224,7 @@ public final class CTrayIcon extends CFRetainedResource implements TrayIconPeer private void postEvent(final AWTEvent event) { SunToolkit.executeOnEventHandlerThread(target, new Runnable() { public void run() { - SunToolkit.postEvent(SunToolkit.targetToAppContext(target), event); + SunToolkit.postEvent(event); } }); } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java index 0e5bfc44bc5..8dad6335548 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -99,7 +99,6 @@ import javax.swing.UIManager; import com.apple.laf.AquaMenuBarUI; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.CGraphicsDevice; import sun.awt.LightweightFrame; import sun.awt.PlatformGraphicsInfo; @@ -700,10 +699,9 @@ public final class LWCToolkit extends LWToolkit { }, true); - AppContext appContext = SunToolkit.targetToAppContext(component); - SunToolkit.postEvent(appContext, invocationEvent); + SunToolkit.postEvent(invocationEvent); // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock - SunToolkit.flushPendingEvents(appContext); + SunToolkit.flushPendingEvents(); doAWTRunLoop(mediator, false); checkException(invocationEvent); @@ -715,10 +713,9 @@ public final class LWCToolkit extends LWToolkit { InvocationEvent invocationEvent = new InvocationEvent(component, event); - AppContext appContext = SunToolkit.targetToAppContext(component); - SunToolkit.postEvent(SunToolkit.targetToAppContext(component), invocationEvent); + SunToolkit.postEvent(invocationEvent); // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock - SunToolkit.flushPendingEvents(appContext); + SunToolkit.flushPendingEvents(); checkException(invocationEvent); } @@ -930,7 +927,6 @@ public final class LWCToolkit extends LWToolkit { @Override public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) { //TODO: FileDialog blocks excluded windows... - //TODO: Test: 2 file dialogs, separate AppContexts: a) Dialog 1 blocked, shouldn't be. Frame 4 blocked (shouldn't be). return (modalityType == null) || (modalityType == Dialog.ModalityType.MODELESS) || (modalityType == Dialog.ModalityType.DOCUMENT_MODAL) || From 276b23d4e6b8fe59a84ff6ab18969474ba004571 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Tue, 24 Feb 2026 21:30:35 +0000 Subject: [PATCH 022/636] 8377671: Step Over doesn't stop after receiving MethodExitEvent Reviewed-by: sspitsyn, amenkov --- .../share/native/libjdwp/stepControl.c | 45 ++++++++++--------- .../share/native/libjdwp/stepControl.h | 4 +- test/jdk/com/sun/jdi/JdbMethodExitTest.java | 5 ++- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c index cf330d74d29..d4f4003a43d 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -171,18 +171,16 @@ initState(JNIEnv *env, jthread thread, StepRequest *step) * Initial values that may be changed below */ step->fromLine = -1; - step->fromNative = JNI_FALSE; + step->notifyFramePopFailed = JNI_FALSE; step->frameExited = JNI_FALSE; step->fromStackDepth = getFrameCount(thread); if (step->fromStackDepth <= 0) { /* - * If there are no stack frames, treat the step as though - * from a native frame. This is most likely to occur at the - * beginning of a debug session, right after the VM_INIT event, - * so we need to do something intelligent. + * If there are no stack frames, there is nothing more to do here. If we are + * doing a step INTO, initEvents() will enable stepping. Otherwise it is + * not enabled because there is nothing to step OVER or OUT of. */ - step->fromNative = JNI_TRUE; return JVMTI_ERROR_NONE; } @@ -196,7 +194,13 @@ initState(JNIEnv *env, jthread thread, StepRequest *step) error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop) (gdata->jvmti, thread, 0); if (error == JVMTI_ERROR_OPAQUE_FRAME) { - step->fromNative = JNI_TRUE; + // OPAQUE_FRAME doesn't always mean native method. It's rare that it doesn't, and + // means that there is something about the frame's state that prevents setting up + // a NotifyFramePop. One example is a frame that is in the process of returning, + // which can happen if we start single stepping after getting a MethodExit event. + // In either any case, we need to be aware that there will be no FramePop event + // when this frame exits. + step->notifyFramePopFailed = JNI_TRUE; error = JVMTI_ERROR_NONE; /* continue without error */ } else if (error == JVMTI_ERROR_DUPLICATE) { @@ -761,31 +765,28 @@ initEvents(jthread thread, StepRequest *step) } } + /* - * Initially enable stepping: - * 1) For step into, always - * 2) For step over, unless right after the VM_INIT. - * Enable stepping for STEP_MIN or STEP_LINE with or without line numbers. - * If the class is redefined then non EMCP methods may not have line - * number info. So enable line stepping for non line number so that it - * behaves like STEP_MIN/STEP_OVER. - * 3) For step out, only if stepping from native, except right after VM_INIT - * - * (right after VM_INIT, a step->over or out is identical to running - * forever) + * Enable step events if necessary. Note that right after VM_INIT, a + * step OVER or OUT is identical to running forever, so we only enable + * step events if fromStackDepth > 0. */ switch (step->depth) { case JDWP_STEP_DEPTH(INTO): enableStepping(thread); break; case JDWP_STEP_DEPTH(OVER): - if (step->fromStackDepth > 0 && !step->fromNative ) { + // We need to always enable for OVER (except right after VM_INIT). + // If we are in a native method, that is the only way to find out + // that we have returned to a java method. + if (step->fromStackDepth > 0) { enableStepping(thread); } break; case JDWP_STEP_DEPTH(OUT): - if (step->fromNative && - (step->fromStackDepth > 0)) { + // We rely on the FramePop event to tell us when we exit the current frame. + // If NotifyFramePop failed, then we need to enable stepping. + if (step->notifyFramePopFailed && (step->fromStackDepth > 0)) { enableStepping(thread); } break; diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.h b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.h index 63f97fb6231..566b8b00ea0 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -37,7 +37,7 @@ typedef struct { /* State */ jboolean pending; jboolean frameExited; /* for depth == STEP_OVER or STEP_OUT */ - jboolean fromNative; + jboolean notifyFramePopFailed; jint fromStackDepth; /* for all but STEP_INTO STEP_INSTRUCTION */ jint fromLine; /* for granularity == STEP_LINE */ jmethodID method; /* Where line table came from. */ diff --git a/test/jdk/com/sun/jdi/JdbMethodExitTest.java b/test/jdk/com/sun/jdi/JdbMethodExitTest.java index 77e12b5f4a6..da76ddb4ab6 100644 --- a/test/jdk/com/sun/jdi/JdbMethodExitTest.java +++ b/test/jdk/com/sun/jdi/JdbMethodExitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -239,6 +239,7 @@ public class JdbMethodExitTest extends JdbTest { // trace exit of methods with all the return values // (but just check a couple of them) jdb.command(JdbCommand.traceMethodExits(true, threadId)); + execCommand(JdbCommand.trace()); execCommand(JdbCommand.cont()) .shouldContain("instance of JdbMethodExitTestTarg") .shouldContain("return value = 8"); @@ -252,7 +253,7 @@ public class JdbMethodExitTest extends JdbTest { .shouldContain("Method entered:"); execCommand(JdbCommand.cont()) .shouldContain("Method exited: return value = \"traceMethods\""); - jdb.command(JdbCommand.stepUp()); + jdb.command(JdbCommand.next()); List reply = new LinkedList<>(); From a890dd1fcd992ad744c3b26a6203f98d41161fb0 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 25 Feb 2026 01:08:44 +0000 Subject: [PATCH 023/636] 8362268: NPE thrown from SASL GSSAPI impl when TLS is used with QOP auth-int against Active Directory Reviewed-by: dfuchs, aefimov --- .../classes/com/sun/jndi/ldap/Connection.java | 17 +- .../com/sun/jndi/ldap/LdapRequest.java | 31 +- .../ldap/SkipAbandonRequestOnClosedConn.java | 270 ++++++++++++++++++ 3 files changed, 306 insertions(+), 12 deletions(-) create mode 100644 test/jdk/com/sun/jndi/ldap/SkipAbandonRequestOnClosedConn.java diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java index 6eebaf4d6eb..dcb739a8697 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -523,7 +523,15 @@ public final class Connection implements Runnable { void abandonRequest(LdapRequest ldr, Control[] reqCtls) { // Remove from queue removeRequest(ldr); - + // an optimistic check to avoid having to construct the BER + // messages for the "abandon request". we repeat + // this check later when holding the lock, before actually + // writing out the "abandon request", and that check actually + // determines whether or not the "abandon request" is actually + // sent + if (!ldr.shouldAbandonRequest()) { + return; + } BerEncoder ber = new BerEncoder(256); int abandonMsgId = getMsgId(); @@ -547,6 +555,9 @@ public final class Connection implements Runnable { lock.lock(); try { + if (!ldr.shouldAbandonRequest()) { + return; + } outStream.write(ber.getBuf(), 0, ber.getDataLen()); outStream.flush(); } finally { @@ -680,7 +691,7 @@ public final class Connection implements Runnable { if (nparent) { LdapRequest ldr = pendingRequests; while (ldr != null) { - ldr.close(); + ldr.connectionClosed(); ldr = ldr.next; } } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java index 2fdd685376c..a8694ed3c7a 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -33,7 +33,7 @@ import java.util.concurrent.TimeUnit; final class LdapRequest { - private static final BerDecoder CLOSED_MARKER = new BerDecoder(new byte[]{}, -1, 0); + private static final BerDecoder CONN_CLOSED_MARKER = new BerDecoder(new byte[]{}, -1, 0); private static final BerDecoder CANCELLED_MARKER = new BerDecoder(new byte[]{}, -1, 0); private static final String CLOSE_MSG = "LDAP connection has been closed"; @@ -44,7 +44,7 @@ final class LdapRequest { private final boolean pauseAfterReceipt; private volatile boolean cancelled; - private volatile boolean closed; + private volatile boolean connectionClosed; private volatile boolean completed; LdapRequest(int msgId, boolean pause, int replyQueueCapacity) { @@ -62,9 +62,22 @@ final class LdapRequest { replies.offer(CANCELLED_MARKER); } - void close() { - closed = true; - replies.offer(CLOSED_MARKER); + /* + * Invoked when the connection on which this (pending) request was made + * is closed. + */ + void connectionClosed() { + connectionClosed = true; + replies.offer(CONN_CLOSED_MARKER); + } + + /** + * {@return true if an "abandon request" may be sent for this request, false otherwise} + */ + boolean shouldAbandonRequest() { + // if the connection to which this request belonged was closed, then + // don't send any further "abandon request" message + return !connectionClosed; } boolean addReplyBer(BerDecoder ber) { @@ -73,7 +86,7 @@ final class LdapRequest { // this is merely a best effort basis check and if we do add the reply // due to a race, that's OK since the replies queue would have necessary // markers for cancelled/closed state and those will be detected by getReplyBer(). - if (cancelled || closed) { + if (cancelled || connectionClosed) { return false; } // if the request is not already completed, check if the reply being added @@ -127,7 +140,7 @@ final class LdapRequest { throw new CommunicationException("Request: " + msgId + " cancelled"); } - if (closed) { + if (connectionClosed) { throw new IOException(CLOSE_MSG); } } @@ -146,7 +159,7 @@ final class LdapRequest { throw new CommunicationException("Request: " + msgId + " cancelled"); } - if (result == CLOSED_MARKER) { + if (result == CONN_CLOSED_MARKER) { throw new IOException(CLOSE_MSG); } return result; diff --git a/test/jdk/com/sun/jndi/ldap/SkipAbandonRequestOnClosedConn.java b/test/jdk/com/sun/jndi/ldap/SkipAbandonRequestOnClosedConn.java new file mode 100644 index 00000000000..cb2eef49bd2 --- /dev/null +++ b/test/jdk/com/sun/jndi/ldap/SkipAbandonRequestOnClosedConn.java @@ -0,0 +1,270 @@ +/* + * 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. + */ + +import java.io.FilterInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.net.Socket; +import java.util.Arrays; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import com.sun.jndi.ldap.Connection; +import com.sun.jndi.ldap.LdapClient; +import com.sun.jndi.ldap.LdapCtx; +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @bug 8362268 + * @summary Verify that unexpected exceptions aren't propagated to LdapCtx callers + * when the LdapCtx's (internal) connection is closed due to an exception + * when reading/writing over the connection's stream + * @modules java.naming/com.sun.jndi.ldap:+open + * @library /test/lib lib/ + * @build jdk.test.lib.net.URIBuilder + * BaseLdapServer LdapMessage + * @run junit/othervm ${test.main.class} + */ +class SkipAbandonRequestOnClosedConn { + + private static final String LOOKUP_NAME = "ou=People,o=FooBar"; + + private static final class Server extends BaseLdapServer { + + private Server() throws IOException { + super(); + } + + // handles and responds to the incoming LDAP request + @Override + protected void handleRequest(final Socket socket, + final LdapMessage request, + final OutputStream out) throws IOException { + switch (request.getOperation()) { + case SEARCH_REQUEST: { + System.err.println("responding to SEARCH_REQUEST with id: " + + request.getMessageID() + " on socket " + socket); + // write out some bytes as a response. it doesn't matter what those + // bytes are - in this test they aren't expected to reach + // the application code. + final byte[] irrelevantResponse = new byte[]{0x42, 0x42, 0x42}; + System.err.println("Response: " + Arrays.toString(irrelevantResponse)); + out.write(irrelevantResponse); + out.flush(); + break; + } + default: { + throw new IOException("unexpected operation type: " + request.getOperation() + + ", request: " + request); + } + } + } + } + + private static Server server; + + @BeforeAll + static void beforeAll() throws Exception { + server = new Server(); + server.start(); + System.err.println("server started at " + server.getInetAddress() + + ":" + server.getPort()); + } + + @AfterAll + static void afterAll() throws Exception { + if (server != null) { + System.err.println("stopping server " + server.getInetAddress() + + ":" + server.getPort()); + server.close(); + } + } + + + /* + * Creates a com.sun.jndi.ldap.LdapCtx and configures its internal com.sun.jndi.ldap.Connection + * instance with an InputStream that throws an IOException in its read() methods. The test + * then initiates a Context.lookup() so that a LDAP request is internally issued and a LDAP + * response is waited for. Due to the configured InputStream throwing an exception from its + * read(), the com.sun.jndi.ldap.Connection will get closed (internally) when reading the + * response and the main thread which was waiting for the LDAP response is woken up and notices + * the connection closure. This test verifies that a NamingException gets thrown from the + * lookup() in this scenario. + */ + @Test + void testNamingException() throws Exception { + final Hashtable envProps = new Hashtable<>(); + envProps.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + final String providerUrl = URIBuilder.newBuilder() + .scheme("ldap") + .host(server.getInetAddress().getHostAddress()) + .port(server.getPort()) + .build().toString(); + envProps.put(Context.PROVIDER_URL, providerUrl); + // explicitly set LDAP version to 3 to prevent LDAP BIND requests + // during LdapCtx instantiation + envProps.put("java.naming.ldap.version", "3"); + // create a (custom) InitialContext which allows us to access the + // LdapCtx's internal connection instance + try (final CustomContext ctx = new CustomContext(envProps)) { + // replace the InputStream and OutputStream of the LdapCtx's + // internal connection, to allow us to raise exception from + // the InputStream and OutputStream as necessary + ctx.replaceStreams(); + + System.err.println("issuing ldap request against " + providerUrl + + " using context " + ctx); + // trigger the LDAP SEARCH request through the lookup call. we are not + // interested in the returned value and are merely interested in the + // Context.lookup(...) failing with a NamingException + assertLookupThrowsNamingException(ctx); + } + } + + // assert that Context.lookup(...) raises a NamingException + private static void assertLookupThrowsNamingException(final Context ctx) { + try { + final Object result = ctx.lookup(LOOKUP_NAME); + fail("Context.lookup() was expected to throw NamingException" + + " but returned result " + result); + } catch (NamingException ne) { + // verify the NamingException is for the right reason + if (!ne.toString().contains("LDAP connection has been closed")) { + // unexpected exception, propagate it + fail("NamingException is missing \"LDAP connection has been closed\" message", ne); + } + // expected + System.err.println("got expected exception: " + ne); + } + } + + private static final class CustomContext extends InitialContext implements AutoCloseable { + + private CustomContext(final Hashtable environment) throws NamingException { + super(environment); + } + + private LdapCtx getLdapCtx() throws NamingException { + final Context ctx = getDefaultInitCtx(); + if (ctx instanceof LdapCtx ldapCtx) { + return ldapCtx; + } + throw new IllegalStateException("Not a LdapCtx: " + ctx.getClass().getName()); + } + + // using reflection, return the com.sun.jndi.ldap.Connection instance + // from within the com.sun.jndi.ldap.LdapCtx + private Connection getConnection() throws Exception { + final LdapCtx ldapCtx = getLdapCtx(); + final Field clientField = ldapCtx.getClass().getDeclaredField("clnt"); + clientField.setAccessible(true); + final LdapClient ldapClient = (LdapClient) clientField.get(ldapCtx); + final Field connField = ldapClient.getClass().getDeclaredField("conn"); + connField.setAccessible(true); + return (Connection) connField.get(ldapClient); + } + + private void replaceStreams() throws Exception { + final Connection conn = getConnection(); + assertNotNull(conn, "com.sun.jndi.ldap.Connection instance is null"); + final InputStream originalInputStream = conn.inStream; + final OutputStream originalOutputStream = conn.outStream; + // replace the connection's streams with our test specific streams + conn.replaceStreams(new In(originalInputStream), new Out(originalOutputStream)); + System.err.println("replaced streams on connection: " + conn); + } + } + + // an OutputStream which intentionally throws a NullPointerException + // from its write(...) methods if the OutputStream has been closed + private static final class Out extends FilterOutputStream { + private volatile boolean closed; + + private Out(final OutputStream underlying) { + super(underlying); + } + + @Override + public void write(final int b) throws IOException { + if (closed) { + throw new NullPointerException("OutputStream is closed - intentional" + + " NullPointerException instead of IOException"); + } + super.write(b); + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + if (closed) { + throw new NullPointerException("OutputStream is closed - intentional" + + " NullPointerException instead of IOException"); + } + super.write(b, off, len); + } + + @Override + public void close() throws IOException { + System.err.println("closing output stream " + this); + closed = true; + super.close(); + } + } + + // an InputStream which intentionally throws an exception + // from its read(...) methods. + private static final class In extends FilterInputStream { + + private In(InputStream underlying) { + super(underlying); + } + + @Override + public int read() throws IOException { + final int v = super.read(); + System.err.println("read " + v + " from " + in + + ", will now intentionally throw an exception"); + throw new IOException("intentional IOException from " + In.class.getName() + ".read()"); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + final int v = super.read(b, off, len); + System.err.println("read " + v + " byte(s) from " + in + + ", will now intentionally throw an exception"); + throw new IOException("intentional IOException from " + In.class.getName() + + ".read(byte[], int, int)"); + } + } +} From 9a92e144a9217006976f955de4341fbe59c38121 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 25 Feb 2026 04:20:07 +0000 Subject: [PATCH 024/636] 8377726: Remove unused macros in register.hpp Reviewed-by: ayang, mhaessig --- src/hotspot/cpu/zero/register_zero.hpp | 6 ++---- src/hotspot/share/asm/register.hpp | 20 ++------------------ 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/hotspot/cpu/zero/register_zero.hpp b/src/hotspot/cpu/zero/register_zero.hpp index fd30f206762..846b649eebd 100644 --- a/src/hotspot/cpu/zero/register_zero.hpp +++ b/src/hotspot/cpu/zero/register_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -48,7 +48,6 @@ class RegisterImpl : public AbstractRegisterImpl { }; // construction - inline friend Register as_Register(int encoding); VMReg as_VMReg(); // derived registers, offsets, and addresses @@ -113,7 +112,6 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl { static const int max_fpr; }; -CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); -#define noreg ((Register)(noreg_RegisterEnumValue)) +const Register noreg = as_Register(-1); #endif // CPU_ZERO_REGISTER_ZERO_HPP diff --git a/src/hotspot/share/asm/register.hpp b/src/hotspot/share/asm/register.hpp index f406995b8ac..95c7c7922cf 100644 --- a/src/hotspot/share/asm/register.hpp +++ b/src/hotspot/share/asm/register.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -49,15 +49,7 @@ class AbstractRegisterImpl { // Macros to help define all kinds of registers -#ifndef USE_POINTERS_TO_REGISTER_IMPL_ARRAY - -#define AS_REGISTER(type,name) ((type)name##_##type##EnumValue) - -#define CONSTANT_REGISTER_DECLARATION(type, name, value) \ -const type name = ((type)value); \ -enum { name##_##type##EnumValue = (value) } - -#else // USE_POINTERS_TO_REGISTER_IMPL_ARRAY +#ifdef USE_POINTERS_TO_REGISTER_IMPL_ARRAY #define REGISTER_IMPL_DECLARATION(type, impl_type, reg_count) \ inline constexpr type as_ ## type(int encoding) { \ @@ -69,16 +61,8 @@ inline constexpr type impl_type::first() { return all_ ## type ## s + 1; } #define REGISTER_IMPL_DEFINITION(type, impl_type, reg_count) \ impl_type all_ ## type ## s[reg_count + 1]; -#define CONSTANT_REGISTER_DECLARATION(type, name, value) \ -constexpr type name = as_ ## type(value); - #endif // USE_POINTERS_TO_REGISTER_IMPL_ARRAY - -#define REGISTER_DECLARATION(type, name, value) \ -const type name = ((type)value) - - // For definitions of RegisterImpl* instances. To be redefined in an // OS-specific way. #ifdef __GNUC__ From e92726c352f2d9e9ccb074441d9c09eef781a492 Mon Sep 17 00:00:00 2001 From: Valerie Peng Date: Wed, 25 Feb 2026 04:45:48 +0000 Subject: [PATCH 025/636] 8373690: Unexpected Keystore message using jdk.crypto.disabledAlgorithms Reviewed-by: mullan, coffeys --- .../share/classes/java/security/KeyStore.java | 16 ++++-- .../security/KeyStore/DisabledKnownType.java | 53 +++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 test/jdk/java/security/KeyStore/DisabledKnownType.java diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 83414082cab..434aa57e3ac 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -1870,6 +1870,7 @@ public class KeyStore { } KeyStore keystore = null; + String matched = null; try (DataInputStream dataStream = new DataInputStream( @@ -1893,8 +1894,10 @@ public class KeyStore { if (CryptoAlgorithmConstraints.permits( "KEYSTORE", ksAlgo)) { keystore = new KeyStore(impl, p, ksAlgo); - break; + } else { + matched = ksAlgo; } + break; } } catch (NoSuchAlgorithmException e) { // ignore @@ -1924,9 +1927,14 @@ public class KeyStore { return keystore; } } - - throw new KeyStoreException("Unrecognized keystore format. " - + "Please load it with a specified type"); + if (matched == null) { + throw new KeyStoreException("Unrecognized keystore format. " + + "Please load it with a specified type"); + } else { + throw new KeyStoreException("Keystore format " + + matched + + " disabled by jdk.crypto.disabledAlgorithms property"); + } } /** diff --git a/test/jdk/java/security/KeyStore/DisabledKnownType.java b/test/jdk/java/security/KeyStore/DisabledKnownType.java new file mode 100644 index 00000000000..70b7228f4c2 --- /dev/null +++ b/test/jdk/java/security/KeyStore/DisabledKnownType.java @@ -0,0 +1,53 @@ +/* + * 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 8373690 + * @summary verify that the exception message indicates the keystore type + * when the type is disabled instead of being unrecognized + * @run main/othervm -Djdk.crypto.disabledAlgorithms=KeyStore.PKCS12 DisabledKnownType + */ + +import java.security.KeyStore; +import java.security.KeyStoreException; + +public class DisabledKnownType { + public static void main(String[] args) throws Exception { + String cacertsPath = System.getProperty("java.home") + + "/lib/security/cacerts"; + try { + KeyStore ks = KeyStore.getInstance(new java.io.File(cacertsPath), + "changeit".toCharArray()); + throw new RuntimeException("Expected KeyStoreException not thrown"); + } catch (KeyStoreException kse) { + if (kse.getMessage().contains("PKCS12")) { + System.out.println("Passed: expected ex thrown: " + kse); + } else { + // pass it up + throw kse; + } + } + } +} + From 6c39d1bb7325ba1dcd79b0f32dd6b103802f4d1c Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 25 Feb 2026 07:06:55 +0000 Subject: [PATCH 026/636] 8371683: TYPE_USE annotation on var lambda parameter should be rejected Reviewed-by: vromero --- .../com/sun/tools/javac/code/Flags.java | 12 ++++- .../com/sun/tools/javac/code/Source.java | 3 +- .../com/sun/tools/javac/comp/Attr.java | 3 +- .../com/sun/tools/javac/comp/Check.java | 11 ++-- .../com/sun/tools/javac/comp/MemberEnter.java | 4 +- .../sun/tools/javac/parser/JavacParser.java | 14 +++-- .../com/sun/tools/javac/tree/JCTree.java | 15 ++++-- .../failures/target/VarVariables-old.out | 9 ++++ .../failures/target/VarVariables.java | 52 +++++++++++++++++++ .../failures/target/VarVariables.out | 9 ++++ 10 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.java create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 5b59e47027e..ac1bcc84194 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -156,12 +156,22 @@ public class Flags { @Use({FlagTarget.CLASS}) public static final int IMPLICIT_CLASS = 1<<19; + /** Variable with implicit/inferred type. + */ + @Use(FlagTarget.VARIABLE) + public static final int VAR_VARIABLE = 1<<19; + /** Flag is set for compiler-generated anonymous method symbols * that `own' an initializer block. */ @Use({FlagTarget.METHOD}) public static final int BLOCK = 1<<20; + /** A parameter of a lambda function. + */ + @Use(FlagTarget.VARIABLE) + public static final int LAMBDA_PARAMETER = 1<<20; + /** Flag is set for ClassSymbols that are being compiled from source. */ @Use({FlagTarget.CLASS}) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java index 84a823f785f..cf5e5ca83f5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -272,6 +272,7 @@ public enum Source { CASE_NULL(JDK21, Fragments.FeatureCaseNull, DiagKind.NORMAL), PATTERN_SWITCH(JDK21, Fragments.FeaturePatternSwitch, DiagKind.PLURAL), REDUNDANT_STRICTFP(JDK17), + TYPE_ANNOTATIONS_ON_VAR_LAMBDA_PARAMETER(MIN, JDK19), UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK21, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL), RECORD_PATTERNS(JDK21, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL), IMPLICIT_CLASSES(JDK25, Fragments.FeatureImplicitClasses, DiagKind.PLURAL), diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index d4bfbce0699..48e0238fb07 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4224,7 +4224,8 @@ public class Attr extends JCTree.Visitor { type = resultInfo.pt; } tree.type = tree.var.type = type; - BindingSymbol v = new BindingSymbol(tree.var.mods.flags, tree.var.name, type, env.info.scope.owner); + BindingSymbol v = new BindingSymbol(tree.var.mods.flags | tree.var.declKind.additionalSymbolFlags, + tree.var.name, type, env.info.scope.owner); v.pos = tree.pos; tree.var.sym = v; if (chk.checkUnique(tree.var.pos(), v, env.info.scope)) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 94b14f3122f..0af30be4671 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -3544,7 +3544,10 @@ public class Check { if (s.kind == PCK) applicableTargets.add(names.PACKAGE); } else if (target == names.TYPE_USE) { - if (s.kind == VAR && s.owner.kind == MTH && s.type.hasTag(NONE)) { + if (s.kind == VAR && + (s.flags() & Flags.VAR_VARIABLE) != 0 && + (!Feature.TYPE_ANNOTATIONS_ON_VAR_LAMBDA_PARAMETER.allowedInSource(source) || + ((s.flags() & Flags.LAMBDA_PARAMETER) == 0))) { //cannot type annotate implicitly typed locals continue; } else if (s.kind == TYP || s.kind == VAR || @@ -5632,8 +5635,8 @@ public class Check { } case JCVariableDecl variableDecl -> { if (variableDecl.vartype != null && - (variableDecl.sym.flags_field & RECORD) == 0 || - (variableDecl.sym.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0) { + ((variableDecl.sym.flags_field & RECORD) == 0 || + (variableDecl.sym.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0)) { /* we don't want to warn twice so if this variable is a compiler generated parameter of * a canonical record constructor, we don't want to issue a warning as we will warn the * corresponding compiler generated private record field anyways diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java index d63ba1677d6..e02fb9849c9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -281,7 +281,7 @@ public class MemberEnter extends JCTree.Visitor { : tree.vartype.type; Name name = tree.name; VarSymbol v = new VarSymbol(0, name, vartype, enclScope.owner); - v.flags_field = chk.checkFlags(tree.mods.flags, v, tree); + v.flags_field = chk.checkFlags(tree.mods.flags | tree.declKind.additionalSymbolFlags, v, tree); tree.sym = v; if (tree.init != null) { v.flags_field |= HASINIT; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index e78537c10f5..40f91a004fd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -3902,7 +3902,7 @@ public class JavacParser implements Parser { if (allowThisIdent || !lambdaParameter || LAX_IDENTIFIER.test(token.kind) || - mods.flags != Flags.PARAMETER || + (mods.flags & ~(Flags.PARAMETER | Flags.LAMBDA_PARAMETER)) != 0 || mods.annotations.nonEmpty()) { JCExpression pn; if (token.kind == UNDERSCORE && (catchParameter || lambdaParameter)) { @@ -5405,7 +5405,13 @@ public class JavacParser implements Parser { * LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter */ protected JCVariableDecl formalParameter(boolean lambdaParameter, boolean recordComponent) { - JCModifiers mods = !recordComponent ? optFinal(Flags.PARAMETER) : modifiersOpt(); + JCModifiers mods; + + if (recordComponent) { + mods = modifiersOpt(); + } else { + mods = optFinal(Flags.PARAMETER | (lambdaParameter ? Flags.LAMBDA_PARAMETER : 0)); + } if (recordComponent && mods.flags != 0) { log.error(mods.pos, Errors.RecordCantDeclareFieldModifiers); } @@ -5436,7 +5442,7 @@ public class JavacParser implements Parser { } protected JCVariableDecl implicitParameter() { - JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER); + JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER | Flags.LAMBDA_PARAMETER); return variableDeclaratorId(mods, null, false, true, false); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index e0a99a6f103..c54507bed3f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -1012,9 +1012,16 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public static class JCVariableDecl extends JCStatement implements VariableTree { public enum DeclKind { - EXPLICIT, // "SomeType name" - IMPLICIT, // "name" - VAR, // "var name" + EXPLICIT(0), // "SomeType name" + IMPLICIT(Flags.VAR_VARIABLE), // "name" + VAR(Flags.VAR_VARIABLE), // "var name" + ; + + public final long additionalSymbolFlags; + + private DeclKind(long additionalSymbolFlags) { + this.additionalSymbolFlags = additionalSymbolFlags; + } } /** variable modifiers */ diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out new file mode 100644 index 00000000000..5b16d28d053 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out @@ -0,0 +1,9 @@ +VarVariables.java:34:36: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.deconstruction.patterns), 19, 21 +VarVariables.java:18:9: compiler.err.annotation.type.not.applicable +VarVariables.java:24:14: compiler.err.annotation.type.not.applicable +VarVariables.java:27:14: compiler.err.annotation.type.not.applicable +VarVariables.java:32:14: compiler.err.annotation.type.not.applicable +VarVariables.java:36:37: compiler.err.annotation.type.not.applicable +VarVariables.java:32:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.AutoCloseable +VarVariables.java:36:41: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.String +8 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.java new file mode 100644 index 00000000000..a47b8b31da7 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.java @@ -0,0 +1,52 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8371683 + * @summary Test that type annotations cannot appears on 'var' variables + * @compile/fail/ref=VarVariables.out -XDrawDiagnostics VarVariables.java + * @compile/fail/ref=VarVariables-old.out --release 19 -XDrawDiagnostics -XDshould-stop.at=FLOW VarVariables.java + */ + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.util.List; +import java.util.function.Consumer; + +class VarVariables { + private void test(Object o) { + @DA var v1 = ""; + @DTA var v2 = ""; + @TA var v3 = ""; + Consumer c1 = (@DA var v) -> {}; + Consumer c2 = (@DTA var v) -> {}; + Consumer c3 = (@TA var v) -> {}; + for (@DA var v = ""; !v.isEmpty(); ) {} + for (@DTA var v = ""; !v.isEmpty(); ) {} + for (@TA var v = ""; !v.isEmpty(); ) {} + for (@DA var v : List.of("")) {} + for (@DTA var v : List.of("")) {} + for (@TA var v : List.of("")) {} + try (@DA var v = open()) { + } catch (Exception ex) {} + try (@DTA var v = open()) { + } catch (Exception ex) {} + try (@TA var v = open()) { + } catch (Exception ex) {} + boolean b1 = o instanceof R(@DA var v); + boolean b2 = o instanceof R(@DTA var v); + boolean b3 = o instanceof R(@TA var v); + } + + private AutoCloseable open() { + return null; + } + record R(String str) {} + + @Target(ElementType.TYPE_USE) + @interface TA { } + + @Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER}) + @interface DTA { } + + @Target({ElementType.LOCAL_VARIABLE, ElementType.PARAMETER}) + @interface DA { } +} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out new file mode 100644 index 00000000000..2586e7883d6 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out @@ -0,0 +1,9 @@ +VarVariables.java:18:9: compiler.err.annotation.type.not.applicable +VarVariables.java:21:32: compiler.err.annotation.type.not.applicable +VarVariables.java:24:14: compiler.err.annotation.type.not.applicable +VarVariables.java:27:14: compiler.err.annotation.type.not.applicable +VarVariables.java:32:14: compiler.err.annotation.type.not.applicable +VarVariables.java:36:37: compiler.err.annotation.type.not.applicable +VarVariables.java:32:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.AutoCloseable +VarVariables.java:36:41: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.String +8 errors From 0c09d2e222e6332a69f61524496ae2de03d3855f Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 25 Feb 2026 08:16:14 +0000 Subject: [PATCH 027/636] 8377880: Enable unused function warnings in hotspot with clang too Reviewed-by: azafari, syan, clanger, lucy --- make/hotspot/lib/CompileJvm.gmk | 2 +- test/hotspot/gtest/runtime/test_os.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index 76e0056658c..f41693e05fb 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -105,7 +105,7 @@ DISABLED_WARNINGS_gcc := array-bounds comment delete-non-virtual-dtor \ DISABLED_WARNINGS_clang := delete-non-abstract-non-virtual-dtor \ invalid-offsetof missing-braces \ sometimes-uninitialized unknown-pragmas unused-but-set-variable \ - unused-function unused-local-typedef unused-private-field unused-variable + unused-local-typedef unused-private-field unused-variable ifneq ($(DEBUG_LEVEL), release) # Assert macro gives warning diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index 7f90a21884b..094f16a4262 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -506,6 +506,7 @@ static inline bool can_reserve_executable_memory(void) { #define PRINT_MAPPINGS(s) { tty->print_cr("%s", s); os::print_memory_mappings((char*)p, total_range_len, tty); tty->cr(); } //#define PRINT_MAPPINGS +#ifndef _AIX // Release a range allocated with reserve_multiple carefully, to not trip mapping // asserts on Windows in os::release_memory() static void carefully_release_multiple(address start, int num_stripes, size_t stripe_len) { @@ -515,7 +516,6 @@ static void carefully_release_multiple(address start, int num_stripes, size_t st } } -#ifndef _AIX // JDK-8257041 // Reserve an area consisting of multiple mappings // (from multiple calls to os::reserve_memory) static address reserve_multiple(int num_stripes, size_t stripe_len) { From 119108c0d4b043126948f46248eb9e6594d739cd Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Wed, 25 Feb 2026 09:24:44 +0000 Subject: [PATCH 028/636] 8373595: A new ObjectMonitorTable implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Anton Artemov Co-authored-by: Erik Österlund Co-authored-by: Roman Kennke Reviewed-by: aboldtch, amitkumar, aartemov, rkennke, coleenp, eosterlund --- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 55 +- .../gc/shared/barrierSetAssembler_aarch64.cpp | 7 +- .../gc/shared/barrierSetAssembler_aarch64.hpp | 3 +- .../shenandoahBarrierSetAssembler_aarch64.cpp | 25 + .../shenandoahBarrierSetAssembler_aarch64.hpp | 4 + .../gc/z/zBarrierSetAssembler_aarch64.cpp | 19 +- .../gc/z/zBarrierSetAssembler_aarch64.hpp | 3 +- .../ppc/gc/shared/barrierSetAssembler_ppc.cpp | 7 +- .../ppc/gc/shared/barrierSetAssembler_ppc.hpp | 4 +- .../shenandoahBarrierSetAssembler_ppc.cpp | 28 + .../shenandoahBarrierSetAssembler_ppc.hpp | 4 + .../cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp | 15 +- .../cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp | 4 +- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 58 +- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 55 +- .../gc/shared/barrierSetAssembler_riscv.cpp | 7 +- .../gc/shared/barrierSetAssembler_riscv.hpp | 4 +- .../shenandoahBarrierSetAssembler_riscv.cpp | 25 + .../shenandoahBarrierSetAssembler_riscv.hpp | 5 +- .../riscv/gc/z/zBarrierSetAssembler_riscv.cpp | 23 +- .../riscv/gc/z/zBarrierSetAssembler_riscv.hpp | 6 +- .../gc/shared/barrierSetAssembler_s390.cpp | 7 +- .../gc/shared/barrierSetAssembler_s390.hpp | 4 +- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 57 +- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 66 +- .../x86/gc/shared/barrierSetAssembler_x86.cpp | 7 +- .../x86/gc/shared/barrierSetAssembler_x86.hpp | 4 +- .../shenandoahBarrierSetAssembler_x86.cpp | 22 + .../shenandoahBarrierSetAssembler_x86.hpp | 4 + .../cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp | 15 +- .../cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp | 4 +- src/hotspot/share/runtime/lockStack.cpp | 5 +- src/hotspot/share/runtime/lockStack.hpp | 5 +- src/hotspot/share/runtime/objectMonitor.hpp | 1 + .../share/runtime/objectMonitorTable.cpp | 866 ++++++++++++------ .../share/runtime/objectMonitorTable.hpp | 61 +- src/hotspot/share/runtime/serviceThread.cpp | 10 +- src/hotspot/share/runtime/synchronizer.cpp | 40 +- src/hotspot/share/runtime/synchronizer.hpp | 4 +- 39 files changed, 1082 insertions(+), 461 deletions(-) diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 958855c7685..dc0b9eb9546 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -30,7 +30,9 @@ #include "opto/matcher.hpp" #include "opto/output.hpp" #include "opto/subnode.hpp" +#include "runtime/objectMonitorTable.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" @@ -221,37 +223,52 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, if (!UseObjectMonitorTable) { assert(t1_monitor == t1_mark, "should be the same here"); } else { + const Register t1_hash = t1; Label monitor_found; - // Load cache address - lea(t3_t, Address(rthread, JavaThread::om_cache_oops_offset())); + // Save the mark, we might need it to extract the hash. + mov(t3, t1_mark); - const int num_unrolled = 2; + // Look for the monitor in the om_cache. + + ByteSize cache_offset = JavaThread::om_cache_oops_offset(); + ByteSize monitor_offset = OMCache::oop_to_monitor_difference(); + const int num_unrolled = OMCache::CAPACITY; for (int i = 0; i < num_unrolled; i++) { - ldr(t1, Address(t3_t)); - cmp(obj, t1); + ldr(t1_monitor, Address(rthread, cache_offset + monitor_offset)); + ldr(t2, Address(rthread, cache_offset)); + cmp(obj, t2); br(Assembler::EQ, monitor_found); - increment(t3_t, in_bytes(OMCache::oop_to_oop_difference())); + cache_offset = cache_offset + OMCache::oop_to_oop_difference(); } - Label loop; + // Look for the monitor in the table. - // Search for obj in cache. - bind(loop); + // Get the hash code. + ubfx(t1_hash, t3, markWord::hash_shift, markWord::hash_bits); - // Check for match. - ldr(t1, Address(t3_t)); - cmp(obj, t1); - br(Assembler::EQ, monitor_found); + // Get the table and calculate the bucket's address + lea(t3, ExternalAddress(ObjectMonitorTable::current_table_address())); + ldr(t3, Address(t3)); + ldr(t2, Address(t3, ObjectMonitorTable::table_capacity_mask_offset())); + ands(t1_hash, t1_hash, t2); + ldr(t3, Address(t3, ObjectMonitorTable::table_buckets_offset())); - // Search until null encountered, guaranteed _null_sentinel at end. - increment(t3_t, in_bytes(OMCache::oop_to_oop_difference())); - cbnz(t1, loop); - // Cache Miss, NE set from cmp above, cbnz does not set flags - b(slow_path); + // Read the monitor from the bucket. + ldr(t1_monitor, Address(t3, t1_hash, Address::lsl(LogBytesPerWord))); + + // Check if the monitor in the bucket is special (empty, tombstone or removed). + cmp(t1_monitor, (unsigned char)ObjectMonitorTable::SpecialPointerValues::below_is_special); + br(Assembler::LO, slow_path); + + // Check if object matches. + ldr(t3, Address(t1_monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_resolve_weak_handle_in_c2(this, t3, t2, slow_path); + cmp(t3, obj); + br(Assembler::NE, slow_path); bind(monitor_found); - ldr(t1_monitor, Address(t3_t, OMCache::oop_to_monitor_difference())); } const Register t2_owner_addr = t2; diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index 021af3e5698..2a78d688097 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -441,6 +441,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } +void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ ldr(obj, Address(obj)); +} + #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp index e69be999f00..c2581b2f962 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -135,6 +135,7 @@ public: OptoReg::Name opto_reg); OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg); + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); #endif // COMPILER2 }; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index ad7bac4e067..2f7707227b4 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -442,6 +443,30 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler __ bind(done); } +#ifdef COMPILER2 +void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, + Register tmp, Label& slow_path) { + assert_different_registers(obj, tmp); + + Label done; + + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + + // Check if the reference is null, and if it is, take the fast path. + __ cbz(obj, done); + + Address gc_state(rthread, ShenandoahThreadLocalData::gc_state_offset()); + __ lea(tmp, gc_state); + __ ldrb(tmp, __ legitimize_address(gc_state, 1, tmp)); + + // Check if the heap is under weak-reference/roots processing, in + // which case we need to take the slow path. + __ tbnz(tmp, ShenandoahHeap::WEAK_ROOTS_BITPOS, slow_path); + __ bind(done); +} +#endif + // Special Shenandoah CAS implementation that handles false negatives due // to concurrent evacuation. The service is more complex than a // traditional CAS operation because the CAS operation is intended to diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index 362fcae1ccd..d5d5ce8950e 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -79,6 +80,9 @@ public: Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); +#ifdef COMPILER2 + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); +#endif void cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val, bool acquire, bool release, bool is_cae, Register result); }; diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 07a2d6fbfa0..4f0977a414f 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -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 @@ -1326,6 +1326,23 @@ void ZStoreBarrierStubC2Aarch64::emit_code(MacroAssembler& masm) { register_stub(this); } +#undef __ +#define __ masm-> + +void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + + // Check if the oop is bad, in which case we need to take the slow path. + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadBeforeMov); + __ movzw(tmp, barrier_Relocation::unpatched); + __ tst(obj, tmp); + __ br(Assembler::NE, slow_path); + + // Oop is okay, so we uncolor it. + __ lsr(obj, obj, ZPointerLoadShift); +} + #undef __ #endif // COMPILER2 diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp index 487970ab0c5..fbbc5c1b517 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp @@ -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 @@ -191,6 +191,7 @@ public: ZLoadBarrierStubC2* stub) const; void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const; + void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); #endif // COMPILER2 void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error); diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp index 8712c75711d..82d06f6c685 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -275,6 +275,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } +void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ ld(obj, 0, obj); +} + #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp index 2bf26bd5010..d78071f2ee0 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -81,6 +81,8 @@ public: #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const; + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, + Register tmp, Label& slow_path); #endif // COMPILER2 }; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index c3bb1811031..e1f0416d65d 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -662,6 +663,33 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler __ block_comment("} try_resolve_jobject_in_native (shenandoahgc)"); } +#ifdef COMPILER2 +void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler *masm, Register obj, + Register tmp, Label &slow_path) { + __ block_comment("try_resolve_weak_handle_in_c2 (shenandoahgc) {"); + + assert_different_registers(obj, tmp); + + Label done; + + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + + // Check if the reference is null, and if it is, take the fast path. + __ cmpdi(CR0, obj, 0); + __ beq(CR0, done); + + // Check if the heap is under weak-reference/roots processing, in + // which case we need to take the slow path. + __ lbz(tmp, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread); + __ andi_(tmp, tmp, ShenandoahHeap::WEAK_ROOTS); + __ bne(CR0, slow_path); + __ bind(done); + + __ block_comment("} try_resolve_weak_handle_in_c2 (shenandoahgc)"); +} +#endif + // Special shenandoah CAS implementation that handles false negatives due // to concurrent evacuation. That is, the CAS operation is intended to succeed in // the following scenarios (success criteria): diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 52615a740af..672f8122bcb 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -121,6 +122,9 @@ public: virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, Register obj, Register tmp, Label& slowpath); +#ifdef COMPILER2 + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); +#endif }; #endif // CPU_PPC_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp index 0aa5858c8e6..bfa3c87c179 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -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. * Copyright (c) 2021, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -950,6 +950,19 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ b(*stub->continuation()); } +void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + + // Check if the oop is bad, in which case we need to take the slow path. + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadMask); + __ andi_(R0, obj, barrier_Relocation::unpatched); + __ bne(CR0, slow_path); + + // Oop is okay, so we uncolor it. + __ srdi(obj, obj, ZPointerLoadShift); +} + #undef __ #endif // COMPILER2 diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp index 27203e7b01c..e31817370d9 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -108,6 +108,8 @@ public: void generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const; void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const; + + void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); #endif // COMPILER2 void store_barrier_fast(MacroAssembler* masm, diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 809285afddb..986dd335816 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -42,6 +42,7 @@ #include "runtime/icache.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/objectMonitor.hpp" +#include "runtime/objectMonitorTable.hpp" #include "runtime/os.hpp" #include "runtime/safepoint.hpp" #include "runtime/safepointMechanism.hpp" @@ -2756,39 +2757,54 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register addi(owner_addr, mark, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag); mark = noreg; } else { + const Register tmp3_bucket = tmp3; + const Register tmp2_hash = tmp2; Label monitor_found; - Register cache_addr = tmp2; - // Load cache address - addi(cache_addr, R16_thread, in_bytes(JavaThread::om_cache_oops_offset())); + // Save the mark, we might need it to extract the hash. + mr(tmp2_hash, mark); - const int num_unrolled = 2; + // Look for the monitor in the om_cache. + + ByteSize cache_offset = JavaThread::om_cache_oops_offset(); + ByteSize monitor_offset = OMCache::oop_to_monitor_difference(); + const int num_unrolled = OMCache::CAPACITY; for (int i = 0; i < num_unrolled; i++) { - ld(R0, 0, cache_addr); + ld(R0, in_bytes(cache_offset), R16_thread); + ld(monitor, in_bytes(cache_offset + monitor_offset), R16_thread); cmpd(CR0, R0, obj); beq(CR0, monitor_found); - addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference())); + cache_offset = cache_offset + OMCache::oop_to_oop_difference(); } - Label loop; + // Look for the monitor in the table. - // Search for obj in cache. - bind(loop); + // Get the hash code. + srdi(tmp2_hash, tmp2_hash, markWord::hash_shift); - // Check for match. - ld(R0, 0, cache_addr); - cmpd(CR0, R0, obj); - beq(CR0, monitor_found); + // Get the table and calculate the bucket's address + int simm16_rest = load_const_optimized(tmp3, ObjectMonitorTable::current_table_address(), R0, true); + ld_ptr(tmp3, simm16_rest, tmp3); + ld(tmp1, in_bytes(ObjectMonitorTable::table_capacity_mask_offset()), tmp3); + andr(tmp2_hash, tmp2_hash, tmp1); + ld(tmp3_bucket, in_bytes(ObjectMonitorTable::table_buckets_offset()), tmp3); - // Search until null encountered, guaranteed _null_sentinel at end. - addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference())); - cmpdi(CR1, R0, 0); - bne(CR1, loop); - // Cache Miss, CR0.NE set from cmp above - b(slow_path); + // Read the monitor from the bucket. + sldi(tmp2_hash, tmp2_hash, LogBytesPerWord); + ldx(monitor, tmp3_bucket, tmp2_hash); + + // Check if the monitor in the bucket is special (empty, tombstone or removed). + cmpldi(CR0, monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); + blt(CR0, slow_path); + + // Check if object matches. + ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_resolve_weak_handle_in_c2(this, tmp3, tmp2, slow_path); + cmpd(CR0, tmp3, obj); + bne(CR0, slow_path); bind(monitor_found); - ld(monitor, in_bytes(OMCache::oop_to_monitor_difference()), cache_addr); // Compute owner address. addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset())); diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index b4e0ba69042..72a90ddde1f 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -30,7 +30,9 @@ #include "opto/intrinsicnode.hpp" #include "opto/output.hpp" #include "opto/subnode.hpp" +#include "runtime/objectMonitorTable.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" #include "utilities/globalDefinitions.hpp" #ifdef PRODUCT @@ -123,35 +125,52 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, if (!UseObjectMonitorTable) { assert(tmp1_monitor == tmp1_mark, "should be the same here"); } else { + const Register tmp2_hash = tmp2; + const Register tmp3_bucket = tmp3; Label monitor_found; - // Load cache address - la(tmp3_t, Address(xthread, JavaThread::om_cache_oops_offset())); + // Save the mark, we might need it to extract the hash. + mv(tmp2_hash, tmp1_mark); - const int num_unrolled = 2; + // Look for the monitor in the om_cache. + + ByteSize cache_offset = JavaThread::om_cache_oops_offset(); + ByteSize monitor_offset = OMCache::oop_to_monitor_difference(); + const int num_unrolled = OMCache::CAPACITY; for (int i = 0; i < num_unrolled; i++) { - ld(tmp1, Address(tmp3_t)); - beq(obj, tmp1, monitor_found); - add(tmp3_t, tmp3_t, in_bytes(OMCache::oop_to_oop_difference())); + ld(tmp1_monitor, Address(xthread, cache_offset + monitor_offset)); + ld(tmp4, Address(xthread, cache_offset)); + beq(obj, tmp4, monitor_found); + cache_offset = cache_offset + OMCache::oop_to_oop_difference(); } - Label loop; + // Look for the monitor in the table. - // Search for obj in cache. - bind(loop); + // Get the hash code. + srli(tmp2_hash, tmp2_hash, markWord::hash_shift); - // Check for match. - ld(tmp1, Address(tmp3_t)); - beq(obj, tmp1, monitor_found); + // Get the table and calculate the bucket's address. + la(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address())); + ld(tmp3_t, Address(tmp3_t)); + ld(tmp1, Address(tmp3_t, ObjectMonitorTable::table_capacity_mask_offset())); + andr(tmp2_hash, tmp2_hash, tmp1); + ld(tmp3_t, Address(tmp3_t, ObjectMonitorTable::table_buckets_offset())); - // Search until null encountered, guaranteed _null_sentinel at end. - add(tmp3_t, tmp3_t, in_bytes(OMCache::oop_to_oop_difference())); - bnez(tmp1, loop); - // Cache Miss. Take the slowpath. - j(slow_path); + // Read the monitor from the bucket. + shadd(tmp3_bucket, tmp2_hash, tmp3_t, tmp4, LogBytesPerWord); + ld(tmp1_monitor, Address(tmp3_bucket)); + + // Check if the monitor in the bucket is special (empty, tombstone or removed). + mv(tmp2, ObjectMonitorTable::SpecialPointerValues::below_is_special); + bltu(tmp1_monitor, tmp2, slow_path); + + // Check if object matches. + ld(tmp3, Address(tmp1_monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_resolve_weak_handle_in_c2(this, tmp3, tmp2, slow_path); + bne(tmp3, obj, slow_path); bind(monitor_found); - ld(tmp1_monitor, Address(tmp3_t, OMCache::oop_to_monitor_difference())); } const Register tmp2_owner_addr = tmp2; diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index f5916000890..aeb9df06de6 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -369,6 +369,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } +void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ ld(obj, Address(obj)); +} + #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp index e50fa1dae36..bbb2a5af824 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -110,6 +110,8 @@ public: #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg); + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, + Register tmp, Label& slow_path); #endif // COMPILER2 }; diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 3cbbb783258..8d530d15ee5 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -461,6 +462,30 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler __ bind(done); } +#ifdef COMPILER2 +void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler *masm, Register obj, + Register tmp, Label& slow_path) { + assert_different_registers(obj, tmp); + + Label done; + + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + + // Check if the reference is null, and if it is, take the fast path. + __ beqz(obj, done); + + Address gc_state(xthread, ShenandoahThreadLocalData::gc_state_offset()); + __ lbu(tmp, gc_state); + + // Check if the heap is under weak-reference/roots processing, in + // which case we need to take the slow path. + __ test_bit(tmp, tmp, ShenandoahHeap::WEAK_ROOTS_BITPOS); + __ bnez(tmp, slow_path); + __ bind(done); +} +#endif + // Special Shenandoah CAS implementation that handles false negatives due // to concurrent evacuation. The service is more complex than a // traditional CAS operation because the CAS operation is intended to diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index 5085be26b2e..e35e09c93da 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -84,7 +85,9 @@ public: virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); - +#ifdef COMPILER2 + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path); +#endif void cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val, Assembler::Aqrl acquire, Assembler::Aqrl release, bool is_cae, Register result); }; diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index 09dea62b6d1..163271a2f11 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -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. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -602,6 +602,27 @@ void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, BLOCK_COMMENT("} ZBarrierSetAssembler::try_resolve_jobject_in_native"); } +#ifdef COMPILER2 +void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + BLOCK_COMMENT("ZBarrierSetAssembler::try_resolve_weak_handle_in_c2 {"); + + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path); + + // Check if the oop is bad, in which case we need to take the slow path. + __ relocate(barrier_Relocation::spec(), [&] { + __ li16u(tmp, barrier_Relocation::unpatched); + }, ZBarrierRelocationFormatMarkBadMask); + __ andr(tmp, obj, tmp); + __ bnez(tmp, slow_path); + + // Oop is okay, so we uncolor it. + __ srli(obj, obj, ZPointerLoadShift); + + BLOCK_COMMENT("} ZBarrierSetAssembler::try_resolve_weak_handle_in_c2"); +} +#endif + static uint16_t patch_barrier_relocation_value(int format) { switch (format) { case ZBarrierRelocationFormatLoadBadMask: diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp index 190d81acd0c..648cb3bf63d 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -170,6 +170,10 @@ public: ZLoadBarrierStubC2* stub) const; void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const; + void try_resolve_weak_handle_in_c2(MacroAssembler* masm, + Register obj, + Register tmp, + Label& slow_path); #endif // COMPILER2 void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error); diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp index c6f5a4e119c..7617c7a49e8 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -206,6 +206,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na return opto_reg; } +void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) { + // Load the oop from the weak handle. + __ z_lg(obj, Address(obj)); +} + #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp index 65db915b672..d5682450414 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -65,6 +65,8 @@ public: #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const; + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, + Register tmp, Label& slow_path); #endif // COMPILER2 static const int OFFSET_TO_PATCHABLE_DATA_INSTRUCTION = 6 + 6 + 6; // iihf(6) + iilf(6) + lg(6) diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index f35e18c7398..78779a9098a 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * Copyright 2024 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -44,6 +44,7 @@ #include "runtime/icache.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/objectMonitor.hpp" +#include "runtime/objectMonitorTable.hpp" #include "runtime/os.hpp" #include "runtime/safepoint.hpp" #include "runtime/safepointMechanism.hpp" @@ -6372,45 +6373,55 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis if (!UseObjectMonitorTable) { assert(tmp1_monitor == mark, "should be the same here"); } else { + const Register tmp1_bucket = tmp1; + const Register hash = Z_R0_scratch; NearLabel monitor_found; - // load cache address - z_la(tmp1, Address(Z_thread, JavaThread::om_cache_oops_offset())); + // Save the mark, we might need it to extract the hash. + z_lgr(hash, mark); - const int num_unrolled = 2; + // Look for the monitor in the om_cache. + + ByteSize cache_offset = JavaThread::om_cache_oops_offset(); + ByteSize monitor_offset = OMCache::oop_to_monitor_difference(); + const int num_unrolled = OMCache::CAPACITY; for (int i = 0; i < num_unrolled; i++) { - z_cg(obj, Address(tmp1)); + z_lg(tmp1_monitor, Address(Z_thread, cache_offset + monitor_offset)); + z_cg(obj, Address(Z_thread, cache_offset)); z_bre(monitor_found); - add2reg(tmp1, in_bytes(OMCache::oop_to_oop_difference())); + cache_offset = cache_offset + OMCache::oop_to_oop_difference(); } - NearLabel loop; - // Search for obj in cache + // Get the hash code. + z_srlg(hash, hash, markWord::hash_shift); - bind(loop); + // Get the table and calculate the bucket's address. + load_const_optimized(tmp2, ObjectMonitorTable::current_table_address()); + z_lg(tmp2, Address(tmp2)); + z_ng(hash, Address(tmp2, ObjectMonitorTable::table_capacity_mask_offset())); + z_lg(tmp1_bucket, Address(tmp2, ObjectMonitorTable::table_buckets_offset())); + z_sllg(hash, hash, LogBytesPerWord); + z_agr(tmp1_bucket, hash); - // check for match. - z_cg(obj, Address(tmp1)); - z_bre(monitor_found); + // Read the monitor from the bucket. + z_lg(tmp1_monitor, Address(tmp1_bucket)); - // search until null encountered, guaranteed _null_sentinel at end. - add2reg(tmp1, in_bytes(OMCache::oop_to_oop_difference())); - z_cghsi(0, tmp1, 0); - z_brne(loop); // if not EQ to 0, go for another loop + // Check if the monitor in the bucket is special (empty, tombstone or removed). + z_clgfi(tmp1_monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); + z_brl(slow_path); - // we reached to the end, cache miss - z_ltgr(obj, obj); // set CC to NE - z_bru(slow_path); + // Check if object matches. + z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_resolve_weak_handle_in_c2(this, tmp2, Z_R0_scratch, slow_path); + z_cgr(obj, tmp2); + z_brne(slow_path); - // cache hit bind(monitor_found); - z_lg(tmp1_monitor, Address(tmp1, OMCache::oop_to_monitor_difference())); } NearLabel monitor_locked; // lock the monitor - // mark contains the tagged ObjectMonitor*. - const Register tagged_monitor = mark; const Register zero = tmp2; const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index c65b439604b..a3ccc081b6b 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -34,7 +34,9 @@ #include "opto/subnode.hpp" #include "runtime/globals.hpp" #include "runtime/objectMonitor.hpp" +#include "runtime/objectMonitorTable.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" #include "utilities/checkedCast.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" @@ -217,7 +219,6 @@ inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vle // In the case of failure, the node will branch directly to the // FailureLabel - // obj: object to lock // box: on-stack box address -- KILLED // rax: tmp -- KILLED @@ -286,7 +287,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg, // After successful lock, push object on lock-stack. movptr(Address(thread, top), obj); addl(Address(thread, JavaThread::lock_stack_top_offset()), oopSize); - jmpb(locked); + jmp(locked); } { // Handle inflated monitor. @@ -297,38 +298,49 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg, if (!UseObjectMonitorTable) { assert(mark == monitor, "should be the same here"); } else { - // Uses ObjectMonitorTable. Look for the monitor in the om_cache. - // Fetch ObjectMonitor* from the cache or take the slow-path. + const Register hash = t; Label monitor_found; - // Load cache address - lea(t, Address(thread, JavaThread::om_cache_oops_offset())); + // Look for the monitor in the om_cache. - const int num_unrolled = 2; + ByteSize cache_offset = JavaThread::om_cache_oops_offset(); + ByteSize monitor_offset = OMCache::oop_to_monitor_difference(); + const int num_unrolled = OMCache::CAPACITY; for (int i = 0; i < num_unrolled; i++) { - cmpptr(obj, Address(t)); + movptr(monitor, Address(thread, cache_offset + monitor_offset)); + cmpptr(obj, Address(thread, cache_offset)); jccb(Assembler::equal, monitor_found); - increment(t, in_bytes(OMCache::oop_to_oop_difference())); + cache_offset = cache_offset + OMCache::oop_to_oop_difference(); } - Label loop; + // Look for the monitor in the table. - // Search for obj in cache. - bind(loop); + // Get the hash code. + movptr(hash, Address(obj, oopDesc::mark_offset_in_bytes())); + shrq(hash, markWord::hash_shift); + andq(hash, markWord::hash_mask); - // Check for match. - cmpptr(obj, Address(t)); - jccb(Assembler::equal, monitor_found); + // Get the table and calculate the bucket's address. + lea(rax_reg, ExternalAddress(ObjectMonitorTable::current_table_address())); + movptr(rax_reg, Address(rax_reg)); + andq(hash, Address(rax_reg, ObjectMonitorTable::table_capacity_mask_offset())); + movptr(rax_reg, Address(rax_reg, ObjectMonitorTable::table_buckets_offset())); - // Search until null encountered, guaranteed _null_sentinel at end. - cmpptr(Address(t), 1); - jcc(Assembler::below, slow_path); // 0 check, but with ZF=0 when *t == 0 - increment(t, in_bytes(OMCache::oop_to_oop_difference())); - jmpb(loop); + // Read the monitor from the bucket. + movptr(monitor, Address(rax_reg, hash, Address::times_ptr)); + + // Check if the monitor in the bucket is special (empty, tombstone or removed) + cmpptr(monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); + jcc(Assembler::below, slow_path); + + // Check if object matches. + movptr(rax_reg, Address(monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_resolve_weak_handle_in_c2(this, rax_reg, slow_path); + cmpptr(rax_reg, obj); + jcc(Assembler::notEqual, slow_path); - // Cache hit. bind(monitor_found); - movptr(monitor, Address(t, OMCache::oop_to_monitor_difference())); } const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); const Address recursions_address(monitor, ObjectMonitor::recursions_offset() - monitor_tag); @@ -487,14 +499,14 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, cmpl(top, in_bytes(JavaThread::lock_stack_base_offset())); jcc(Assembler::below, check_done); cmpptr(obj, Address(thread, top)); - jccb(Assembler::notEqual, inflated_check_lock_stack); + jcc(Assembler::notEqual, inflated_check_lock_stack); stop("Fast Unlock lock on stack"); bind(check_done); if (UseObjectMonitorTable) { movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); } testptr(mark, markWord::monitor_value); - jccb(Assembler::notZero, inflated); + jcc(Assembler::notZero, inflated); stop("Fast Unlock not monitor"); #endif @@ -519,7 +531,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, // Check if recursive. cmpptr(recursions_address, 0); - jccb(Assembler::notZero, recursive); + jcc(Assembler::notZero, recursive); // Set owner to null. // Release to satisfy the JMM @@ -530,11 +542,11 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, // Check if the entry_list is empty. cmpptr(entry_list_address, NULL_WORD); - jccb(Assembler::zero, unlocked); // If so we are done. + jcc(Assembler::zero, unlocked); // If so we are done. // Check if there is a successor. cmpptr(succ_address, NULL_WORD); - jccb(Assembler::notZero, unlocked); // If so we are done. + jcc(Assembler::notZero, unlocked); // If so we are done. // Save the monitor pointer in the current thread, so we can try to // reacquire the lock in SharedRuntime::monitor_exit_helper(). diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index 09c5d93dbb3..215dc30f7fd 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -395,6 +395,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na extern void vec_spill_helper(C2_MacroAssembler *masm, bool is_load, int stack_offset, int reg, uint ireg, outputStream* st); +void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath) { + // Load the oop from the weak handle. + __ movptr(obj, Address(obj)); +} + #undef __ #define __ _masm-> diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp index c5bf17c3b4e..6aff29850e3 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -109,6 +109,8 @@ public: #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg); + + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath); #endif // COMPILER2 }; diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 97829a10a3b..67510fac58f 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -618,6 +619,27 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler __ bind(done); } +#ifdef COMPILER2 +void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath) { + Label done; + + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, slowpath); + + // Check if the reference is null, and if it is, take the fast path. + __ testptr(obj, obj); + __ jcc(Assembler::zero, done); + + Address gc_state(r15_thread, ShenandoahThreadLocalData::gc_state_offset()); + + // Check if the heap is under weak-reference/roots processing, in + // which case we need to take the slow path. + __ testb(gc_state, ShenandoahHeap::WEAK_ROOTS); + __ jcc(Assembler::notZero, slowpath); + __ bind(done); +} +#endif // COMPILER2 + // Special Shenandoah CAS implementation that handles false negatives // due to concurrent evacuation. void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm, diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index b5cc5c8d834..79540aa19e1 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -77,6 +78,9 @@ public: Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); +#ifdef COMPILER2 + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath); +#endif // COMPILER2 }; #endif // CPU_X86_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index ae93cca8c19..47a3dad54e7 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -1328,6 +1328,19 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ jmp(slow_continuation); } +void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slow_path) { + // Resolve weak handle using the standard implementation. + BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, slow_path); + + // Check if the oop is bad, in which case we need to take the slow path. + __ testptr(obj, Address(r15_thread, ZThreadLocalData::mark_bad_mask_offset())); + __ jcc(Assembler::notZero, slow_path); + + // Oop is okay, so we uncolor it. + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatLoadGoodBeforeShl); + __ shrq(obj, barrier_Relocation::unpatched); +} + #undef __ #endif // COMPILER2 diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp index 19902500f93..e91e2b9ea20 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -167,6 +167,8 @@ public: ZLoadBarrierStubC2* stub) const; void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const; + + virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slow_path); #endif // COMPILER2 void store_barrier_fast(MacroAssembler* masm, diff --git a/src/hotspot/share/runtime/lockStack.cpp b/src/hotspot/share/runtime/lockStack.cpp index a88a84eb9f8..58b9c58a329 100644 --- a/src/hotspot/share/runtime/lockStack.cpp +++ b/src/hotspot/share/runtime/lockStack.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * 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 @@ -118,7 +118,4 @@ void LockStack::print_on(outputStream* st) { OMCache::OMCache(JavaThread* jt) : _entries() { STATIC_ASSERT(std::is_standard_layout::value); STATIC_ASSERT(std::is_standard_layout::value); - STATIC_ASSERT(offsetof(OMCache, _null_sentinel) == offsetof(OMCache, _entries) + - offsetof(OMCache::OMCacheEntry, _oop) + - OMCache::CAPACITY * in_bytes(oop_to_oop_difference())); } diff --git a/src/hotspot/share/runtime/lockStack.hpp b/src/hotspot/share/runtime/lockStack.hpp index dbc958a71e2..a32364b1774 100644 --- a/src/hotspot/share/runtime/lockStack.hpp +++ b/src/hotspot/share/runtime/lockStack.hpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2024, 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 @@ -132,14 +132,13 @@ class LockStack { class OMCache { friend class VMStructs; public: - static constexpr int CAPACITY = 8; + static constexpr int CAPACITY = 2; private: struct OMCacheEntry { oop _oop = nullptr; ObjectMonitor* _monitor = nullptr; } _entries[CAPACITY]; - const oop _null_sentinel = nullptr; public: static ByteSize entries_offset() { return byte_offset_of(OMCache, _entries); } diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 574a652f230..8d9a481bf37 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -217,6 +217,7 @@ class ObjectMonitor : public CHeapObj { static int Knob_SpinLimit; + static ByteSize object_offset() { return byte_offset_of(ObjectMonitor, _object); } static ByteSize metadata_offset() { return byte_offset_of(ObjectMonitor, _metadata); } static ByteSize owner_offset() { return byte_offset_of(ObjectMonitor, _owner); } static ByteSize recursions_offset() { return byte_offset_of(ObjectMonitor, _recursions); } diff --git a/src/hotspot/share/runtime/objectMonitorTable.cpp b/src/hotspot/share/runtime/objectMonitorTable.cpp index 9b522720a28..767f5de6897 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.cpp +++ b/src/hotspot/share/runtime/objectMonitorTable.cpp @@ -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 @@ -31,278 +31,634 @@ #include "runtime/thread.hpp" #include "runtime/timerTrace.hpp" #include "runtime/trimNativeHeap.hpp" -#include "utilities/concurrentHashTableTasks.inline.hpp" #include "utilities/globalDefinitions.hpp" // ----------------------------------------------------------------------------- -// ConcurrentHashTable storing links from objects to ObjectMonitors +// Theory of operations -- Object Monitor Table: +// +// The OMT (Object Monitor Table) is a concurrent hash table specifically +// designed so that it (in the normal case) can be searched from C2 generated +// code. +// +// In its simplest form it consists of: +// 1. An array of pointers. +// 2. The size (capacity) of the array, which is always a power of two. +// +// When you want to find a monitor associated with an object, you extract the +// hash value of the object. Then calculate an index by taking the hash value +// and bit-wise AND it with the capacity mask (e.g. size-1) of the OMT. Now +// use that index into the OMT's array of pointers. If the pointer is non +// null, check if it's a monitor pointer that is associated with the object. +// If so you're done. If the pointer is non null, but associated with another +// object, you start looking at (index+1), (index+2) and so on, until you +// either find the correct monitor, or a null pointer. Finding a null pointer +// means that the monitor is simply not in the OMT. +// +// If the size of the pointer array is significantly larger than the number of +// pointers in it, the chance of finding the monitor in the hash index +// (without any further linear searching) is quite high. It is also straight +// forward to generate C2 code for this, which for the fast path doesn't +// contain any branching at all. See: C2_MacroAssembler::fast_lock(). +// +// When the number of monitors (pointers in the array) reaches above the +// allowed limit (defined by the GROW_LOAD_FACTOR symbol) we need to grow the +// table. +// +// A simple description of how growing the OMT is done, is to say that we +// allocate a new table (twice as large as the old one), and then copy all the +// old monitor pointers from the old table to the new. +// +// But since the OMT is a concurrent hash table and things needs to work for +// other clients of the OMT while we grow it, it's gets a bit more +// complicated. +// +// Both the new and (potentially several) old table(s) may exist at the same +// time. The newest is always called the "current", and the older ones are +// singly linked using a "prev" pointer. +// +// As soon as we have allocated and linked in the new "current" OMT, all new +// monitor pointers will be added to the new table. Effectively making the +// atomic switch from "old current" to "new current" a linearization point. +// +// After that we start to go through all the indexes in the old table. If the +// index is empty (the pointer is null) we put a "tombstone" into that index, +// which will prevent any future concurrent insert ending up in that index. +// +// If the index contains a monitor pointer, we insert that monitor pointer +// into the OMT which can be considered as one generation newer. If the index +// contains a "removed" pointer, we just ignore it. +// +// We use special pointer values for "tombstone" and "removed". Any pointer +// that is not null, not a tombstone and not removed, is considered to be a +// pointer to a monitor. +// +// When all the monitor pointers from an old OMT has been transferred to the +// new OMT, the old table is unlinked. +// +// This copying from an old OMT to one generation newer OMT, will continue +// until all the monitor pointers from old OMTs has been transferred to the +// newest "current" OMT. +// +// The memory for old, unlinked OMTs will be freed after a thread-local +// handshake with all Java threads. +// +// Searching the OMT for a monitor pointer while there are several generations +// of the OMT, will start from the oldest OMT. +// +// A note about the GROW_LOAD_FACTOR: In order to guarantee that the add and +// search algorithms can't loop forever, we must make sure that there is at +// least one null pointer in the array to stop them from dead looping. +// Furthermore, when we grow the OMT, we must make sure that the new "current" +// can accommodate all the monitors from all older OMTs, while still being so +// sparsely populated that the C2 generated code will likely find what it's +// searching for at the hash index, without needing further linear searching. +// The grow load factor is set to 12.5%, which satisfies the above +// requirements. Don't change it for fun, it might backfire. +// ----------------------------------------------------------------------------- -using ConcurrentTable = ConcurrentHashTable; +ObjectMonitorTable::Table* volatile ObjectMonitorTable::_curr; -static ConcurrentTable* _table = nullptr; -static volatile size_t _items_count = 0; -static size_t _table_size = 0; -static volatile bool _resize = false; +class ObjectMonitorTable::Table : public CHeapObj { + friend class ObjectMonitorTable; -class ObjectMonitorTableConfig : public AllStatic { - public: - using Value = ObjectMonitor*; - static uintx get_hash(Value const& value, bool* is_dead) { - return (uintx)value->hash(); + DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); + const size_t _capacity_mask; // One less than its power-of-two capacity + Table* volatile _prev; // Set while rehashing + Entry volatile* _buckets; // The payload + DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(_capacity_mask) + sizeof(_prev) + sizeof(_buckets)); + volatile size_t _items_count; + DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(_items_count)); + + static Entry as_entry(ObjectMonitor* monitor) { + Entry entry = static_cast((uintptr_t)monitor); + assert(entry >= Entry::below_is_special, "Must be! (entry: " PTR_FORMAT ")", intptr_t(entry)); + return entry; } - static void* allocate_node(void* context, size_t size, Value const& value) { - ObjectMonitorTable::inc_items_count(); - return AllocateHeap(size, mtObjectMonitor); - }; - static void free_node(void* context, void* memory, Value const& value) { - ObjectMonitorTable::dec_items_count(); - FreeHeap(memory); + + static ObjectMonitor* as_monitor(Entry entry) { + assert(entry >= Entry::below_is_special, "Must be! (entry: " PTR_FORMAT ")", intptr_t(entry)); + return reinterpret_cast(entry); + } + + static Entry empty() { + return Entry::empty; + } + + static Entry tombstone() { + return Entry::tombstone; + } + + static Entry removed() { + return Entry::removed; + } + + // Make sure we leave space for previous versions to relocate too. + bool try_inc_items_count() { + for (;;) { + size_t population = AtomicAccess::load(&_items_count); + if (should_grow(population)) { + return false; + } + if (AtomicAccess::cmpxchg(&_items_count, population, population + 1, memory_order_relaxed) == population) { + return true; + } + } + } + + double get_load_factor(size_t count) { + return (double)count / (double)capacity(); + } + + void inc_items_count() { + AtomicAccess::inc(&_items_count, memory_order_relaxed); + } + + void dec_items_count() { + AtomicAccess::dec(&_items_count, memory_order_relaxed); + } + +public: + Table(size_t capacity, Table* prev) + : _capacity_mask(capacity - 1), + _prev(prev), + _buckets(NEW_C_HEAP_ARRAY(Entry, capacity, mtObjectMonitor)), + _items_count(0) + { + for (size_t i = 0; i < capacity; ++i) { + _buckets[i] = empty(); + } + } + + ~Table() { + FREE_C_HEAP_ARRAY(Entry, _buckets); + } + + Table* prev() { + return AtomicAccess::load(&_prev); + } + + size_t capacity() { + return _capacity_mask + 1; + } + + bool should_grow(size_t population) { + return get_load_factor(population) > GROW_LOAD_FACTOR; + } + + bool should_grow() { + return should_grow(AtomicAccess::load(&_items_count)); + } + + size_t total_items() { + size_t current_items = AtomicAccess::load(&_items_count); + Table* prev = AtomicAccess::load(&_prev); + if (prev != nullptr) { + return prev->total_items() + current_items; + } + return current_items; + } + + ObjectMonitor* get(oop obj, intptr_t hash) { + // Acquire tombstones and relocations in case prev transitioned to null + Table* prev = AtomicAccess::load_acquire(&_prev); + if (prev != nullptr) { + ObjectMonitor* result = prev->get(obj, hash); + if (result != nullptr) { + return result; + } + } + + const size_t start_index = size_t(hash) & _capacity_mask; + size_t index = start_index; + + for (;;) { + Entry volatile* bucket = _buckets + index; + Entry entry = AtomicAccess::load_acquire(bucket); + + if (entry == tombstone() || entry == empty()) { + // Not found + break; + } + + if (entry != removed() && as_monitor(entry)->object_peek() == obj) { + // Found matching monitor. + return as_monitor(entry); + } + + index = (index + 1) & _capacity_mask; + assert(index != start_index, "invariant"); + } + + // Rehashing could have started by now, but if a monitor has been inserted in a + // newer table, it was inserted after the get linearization point. + return nullptr; + } + + ObjectMonitor* prepare_insert(oop obj, intptr_t hash) { + // Acquire any tomb stones and relocations if prev transitioned to null. + Table* prev = AtomicAccess::load_acquire(&_prev); + if (prev != nullptr) { + ObjectMonitor* result = prev->prepare_insert(obj, hash); + if (result != nullptr) { + return result; + } + } + + const size_t start_index = size_t(hash) & _capacity_mask; + size_t index = start_index; + + for (;;) { + Entry volatile* bucket = _buckets + index; + Entry entry = AtomicAccess::load_acquire(bucket); + + if (entry == empty()) { + // Found an empty slot to install the new monitor in. + // To avoid concurrent inserts succeeding, place a tomb stone here. + Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_relaxed); + if (result == entry) { + // Success! Nobody will try to insert here again, except reinsert from rehashing. + return nullptr; + } + entry = result; + } + + if (entry == tombstone()) { + // Can't insert into this table. + return nullptr; + } + + if (entry != removed() && as_monitor(entry)->object_peek() == obj) { + // Found matching monitor. + return as_monitor(entry); + } + + index = (index + 1) & _capacity_mask; + assert(index != start_index, "invariant"); + } + } + + ObjectMonitor* get_set(oop obj, Entry new_monitor, intptr_t hash) { + // Acquire any tombstones and relocations if prev transitioned to null. + Table* prev = AtomicAccess::load_acquire(&_prev); + if (prev != nullptr) { + // Sprinkle tombstones in previous tables to force concurrent inserters + // to the latest table. We only really want to try inserting in the + // latest table. + ObjectMonitor* result = prev->prepare_insert(obj, hash); + if (result != nullptr) { + return result; + } + } + + const size_t start_index = size_t(hash) & _capacity_mask; + size_t index = start_index; + + for (;;) { + Entry volatile* bucket = _buckets + index; + Entry entry = AtomicAccess::load_acquire(bucket); + + if (entry == empty()) { + // Empty slot to install the new monitor + if (try_inc_items_count()) { + // Succeeding in claiming an item. + Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + if (result == entry) { + // Success - already incremented. + return as_monitor(new_monitor); + } + + // Something else was installed in place. + dec_items_count(); + entry = result; + } else { + // Out of allowance; leaving place for rehashing to succeed. + // To avoid concurrent inserts succeeding, place a tombstone here. + Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_acq_rel); + if (result == entry) { + // Success; nobody will try to insert here again, except reinsert from rehashing. + return nullptr; + } + entry = result; + } + } + + if (entry == tombstone()) { + // Can't insert into this table. + return nullptr; + } + + if (entry != removed() && as_monitor(entry)->object_peek() == obj) { + // Found matching monitor. + return as_monitor(entry); + } + + index = (index + 1) & _capacity_mask; + assert(index != start_index, "invariant"); + } + } + + void remove(oop obj, Entry old_monitor, intptr_t hash) { + assert(old_monitor >= Entry::below_is_special, + "Must be! (old_monitor: " PTR_FORMAT ")", intptr_t(old_monitor)); + + const size_t start_index = size_t(hash) & _capacity_mask; + size_t index = start_index; + + for (;;) { + Entry volatile* bucket = _buckets + index; + Entry entry = AtomicAccess::load_acquire(bucket); + + if (entry == empty()) { + // The monitor does not exist in this table. + break; + } + + if (entry == tombstone()) { + // Stop searching at tombstones. + break; + } + + if (entry == old_monitor) { + // Found matching entry; remove it + Entry result = AtomicAccess::cmpxchg(bucket, entry, removed(), memory_order_relaxed); + assert(result == entry, "should not fail"); + break; + } + + index = (index + 1) & _capacity_mask; + assert(index != start_index, "invariant"); + } + + // Old versions are removed after newer versions to ensure that observing + // the monitor removed and then doing a subsequent lookup results in there + // still not being a monitor, instead of flickering back to being there. + // Only the deflation thread rebuilds and unlinks tables, so we do not need + // any concurrency safe prev read below. + if (_prev != nullptr) { + _prev->remove(obj, old_monitor, hash); + } + } + + void reinsert(oop obj, Entry new_monitor) { + intptr_t hash = obj->mark().hash(); + + const size_t start_index = size_t(hash) & _capacity_mask; + size_t index = start_index; + + for (;;) { + Entry volatile* bucket = _buckets + index; + Entry entry = AtomicAccess::load_acquire(bucket); + + if (entry == empty()) { + // Empty slot to install the new monitor. + Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + if (result == entry) { + // Success - unconditionally increment. + inc_items_count(); + return; + } + + // Another monitor was installed. + entry = result; + } + + if (entry == tombstone()) { + // A concurrent inserter did not get enough allowance in the table. + // But reinsert always succeeds - we will take the spot. + Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_acq_rel); + if (result == entry) { + // Success - unconditionally increment. + inc_items_count(); + return; + } + + // Another monitor was installed. + entry = result; + } + + if (entry == removed()) { + // A removed entry can be flipped back with reinsertion. + Entry result = AtomicAccess::cmpxchg(bucket, entry, new_monitor, memory_order_release); + if (result == entry) { + // Success - but don't increment; the initial entry did that for us. + return; + } + + // Another monitor was installed. + entry = result; + } + + assert(entry != empty(), "invariant"); + assert(entry != tombstone(), "invariant"); + assert(entry != removed(), "invariant"); + assert(as_monitor(entry)->object_peek() != obj, "invariant"); + index = (index + 1) & _capacity_mask; + assert(index != start_index, "invariant"); + } + } + + void rebuild() { + Table* prev = _prev; + if (prev == nullptr) { + // Base case for recursion - no previous version. + return; + } + + // Finish rebuilding up to prev as target so we can use prev as source. + prev->rebuild(); + + JavaThread* current = JavaThread::current(); + + // Relocate entries from prev. + for (size_t index = 0; index <= prev->_capacity_mask; index++) { + if ((index & 127) == 0) { + // Poll for safepoints to improve time to safepoint + ThreadBlockInVM tbivm(current); + } + + Entry volatile* bucket = prev->_buckets + index; + Entry entry = AtomicAccess::load_acquire(bucket); + + if (entry == empty()) { + // Empty slot; put a tombstone there. + Entry result = AtomicAccess::cmpxchg(bucket, entry, tombstone(), memory_order_acq_rel); + if (result == empty()) { + // Success; move to next entry. + continue; + } + + // Concurrent insert; relocate. + entry = result; + } + + if (entry != tombstone() && entry != removed()) { + // A monitor + ObjectMonitor* monitor = as_monitor(entry); + oop obj = monitor->object_peek(); + if (obj != nullptr) { + // In the current implementation the deflation thread drives + // the rebuilding, and it will already have removed any entry + // it has deflated. The assert is only here to make sure. + assert(!monitor->is_being_async_deflated(), "Should be"); + // Re-insert still live monitor. + reinsert(obj, entry); + } + } + } + + // Unlink this table, releasing the tombstones and relocations. + AtomicAccess::release_store(&_prev, (Table*)nullptr); } }; -class Lookup : public StackObj { - oop _obj; - - public: - explicit Lookup(oop obj) : _obj(obj) {} - - uintx get_hash() const { - uintx hash = _obj->mark().hash(); - assert(hash != 0, "should have a hash"); - return hash; - } - - bool equals(ObjectMonitor** value) { - assert(*value != nullptr, "must be"); - return (*value)->object_refers_to(_obj); - } - - bool is_dead(ObjectMonitor** value) { - assert(*value != nullptr, "must be"); - return false; - } -}; - -class LookupMonitor : public StackObj { - ObjectMonitor* _monitor; - - public: - explicit LookupMonitor(ObjectMonitor* monitor) : _monitor(monitor) {} - - uintx get_hash() const { - return _monitor->hash(); - } - - bool equals(ObjectMonitor** value) { - return (*value) == _monitor; - } - - bool is_dead(ObjectMonitor** value) { - assert(*value != nullptr, "must be"); - return (*value)->object_is_dead(); - } -}; - -void ObjectMonitorTable::inc_items_count() { - AtomicAccess::inc(&_items_count, memory_order_relaxed); -} - -void ObjectMonitorTable::dec_items_count() { - AtomicAccess::dec(&_items_count, memory_order_relaxed); -} - -double ObjectMonitorTable::get_load_factor() { - size_t count = AtomicAccess::load(&_items_count); - return (double)count / (double)_table_size; -} - -size_t ObjectMonitorTable::table_size(Thread* current) { - return ((size_t)1) << _table->get_size_log2(current); -} - -size_t ObjectMonitorTable::max_log_size() { - // TODO[OMTable]: Evaluate the max size. - // TODO[OMTable]: Need to fix init order to use Universe::heap()->max_capacity(); - // Using MaxHeapSize directly this early may be wrong, and there - // are definitely rounding errors (alignment). - const size_t max_capacity = MaxHeapSize; - const size_t min_object_size = CollectedHeap::min_dummy_object_size() * HeapWordSize; - const size_t max_objects = max_capacity / MAX2(MinObjAlignmentInBytes, checked_cast(min_object_size)); - const size_t log_max_objects = log2i_graceful(max_objects); - - return MAX2(MIN2(SIZE_BIG_LOG2, log_max_objects), min_log_size()); -} - -// ~= log(AvgMonitorsPerThreadEstimate default) -size_t ObjectMonitorTable::min_log_size() { - return 10; -} - -template -size_t ObjectMonitorTable::clamp_log_size(V log_size) { - return MAX2(MIN2(log_size, checked_cast(max_log_size())), checked_cast(min_log_size())); -} - -size_t ObjectMonitorTable::initial_log_size() { - const size_t estimate = log2i(MAX2(os::processor_count(), 1)) + log2i(MAX2(AvgMonitorsPerThreadEstimate, size_t(1))); - return clamp_log_size(estimate); -} - -size_t ObjectMonitorTable::grow_hint() { - return ConcurrentTable::DEFAULT_GROW_HINT; -} - void ObjectMonitorTable::create() { - _table = new ConcurrentTable(initial_log_size(), max_log_size(), grow_hint()); - _items_count = 0; - _table_size = table_size(Thread::current()); - _resize = false; -} - -void ObjectMonitorTable::verify_monitor_get_result(oop obj, ObjectMonitor* monitor) { -#ifdef ASSERT - if (SafepointSynchronize::is_at_safepoint()) { - bool has_monitor = obj->mark().has_monitor(); - assert(has_monitor == (monitor != nullptr), - "Inconsistency between markWord and ObjectMonitorTable has_monitor: %s monitor: " PTR_FORMAT, - BOOL_TO_STR(has_monitor), p2i(monitor)); - } -#endif + _curr = new Table(128, nullptr); } ObjectMonitor* ObjectMonitorTable::monitor_get(Thread* current, oop obj) { - ObjectMonitor* result = nullptr; - Lookup lookup_f(obj); - auto found_f = [&](ObjectMonitor** found) { - assert((*found)->object_peek() == obj, "must be"); - result = *found; - }; - _table->get(current, lookup_f, found_f); - verify_monitor_get_result(obj, result); + const intptr_t hash = obj->mark().hash(); + Table* curr = AtomicAccess::load_acquire(&_curr); + ObjectMonitor* monitor = curr->get(obj, hash); + return monitor; +} + +// Returns a new table to try inserting into. +ObjectMonitorTable::Table* ObjectMonitorTable::grow_table(Table* curr) { + Table* result; + Table* new_table = AtomicAccess::load_acquire(&_curr); + if (new_table != curr) { + // Table changed; no need to try further + return new_table; + } + + { + // Use MonitorDeflation_lock to only allow one inflating thread to + // attempt to allocate the new table. + MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag); + + new_table = AtomicAccess::load_acquire(&_curr); + if (new_table != curr) { + // Table changed; no need to try further + return new_table; + } + + new_table = new Table(curr->capacity() << 1, curr); + result = AtomicAccess::cmpxchg(&_curr, curr, new_table, memory_order_acq_rel); + if (result == curr) { + log_info(monitorinflation)("Growing object monitor table (capacity: %zu)", + new_table->capacity()); + // Since we grew the table (we have a new current) we need to + // notify the deflation thread to rebuild the table (to get rid of + // old currents). + ObjectSynchronizer::set_is_async_deflation_requested(true); + ml.notify_all(); + return new_table; + } + } + + // Somebody else started rehashing; restart in new table. + delete new_table; + return result; } -void ObjectMonitorTable::try_notify_grow() { - if (!_table->is_max_size_reached() && !AtomicAccess::load(&_resize)) { - AtomicAccess::store(&_resize, true); - if (Service_lock->try_lock()) { - Service_lock->notify(); - Service_lock->unlock(); - } - } -} - -bool ObjectMonitorTable::should_grow() { - return get_load_factor() > GROW_LOAD_FACTOR && !_table->is_max_size_reached(); -} - -bool ObjectMonitorTable::should_resize() { - return should_grow() || should_shrink() || AtomicAccess::load(&_resize); -} - -template -bool ObjectMonitorTable::run_task(JavaThread* current, Task& task, const char* task_name, Args&... args) { - if (task.prepare(current)) { - log_trace(monitortable)("Started to %s", task_name); - TraceTime timer(task_name, TRACETIME_LOG(Debug, monitortable, perf)); - while (task.do_task(current, args...)) { - task.pause(current); - { - ThreadBlockInVM tbivm(current); - } - task.cont(current); - } - task.done(current); - return true; - } - return false; -} - -bool ObjectMonitorTable::grow(JavaThread* current) { - ConcurrentTable::GrowTask grow_task(_table); - if (run_task(current, grow_task, "Grow")) { - _table_size = table_size(current); - log_info(monitortable)("Grown to size: %zu", _table_size); - return true; - } - return false; -} - -bool ObjectMonitorTable::clean(JavaThread* current) { - ConcurrentTable::BulkDeleteTask clean_task(_table); - auto is_dead = [&](ObjectMonitor** monitor) { - return (*monitor)->object_is_dead(); - }; - auto do_nothing = [&](ObjectMonitor** monitor) {}; - NativeHeapTrimmer::SuspendMark sm("ObjectMonitorTable"); - return run_task(current, clean_task, "Clean", is_dead, do_nothing); -} - -bool ObjectMonitorTable::resize(JavaThread* current) { - LogTarget(Info, monitortable) lt; - bool success = false; - - if (should_grow()) { - lt.print("Start growing with load factor %f", get_load_factor()); - success = grow(current); - } else { - if (!_table->is_max_size_reached() && AtomicAccess::load(&_resize)) { - lt.print("WARNING: Getting resize hints with load factor %f", get_load_factor()); - } - lt.print("Start cleaning with load factor %f", get_load_factor()); - success = clean(current); - } - - AtomicAccess::store(&_resize, false); - - return success; -} - ObjectMonitor* ObjectMonitorTable::monitor_put_get(Thread* current, ObjectMonitor* monitor, oop obj) { - // Enter the monitor into the concurrent hashtable. - ObjectMonitor* result = monitor; - Lookup lookup_f(obj); - auto found_f = [&](ObjectMonitor** found) { - assert((*found)->object_peek() == obj, "must be"); - result = *found; - }; - bool grow; - _table->insert_get(current, lookup_f, monitor, found_f, &grow); - verify_monitor_get_result(obj, result); - if (grow) { - try_notify_grow(); - } - return result; -} + const intptr_t hash = obj->mark().hash(); + Table* curr = AtomicAccess::load_acquire(&_curr); -bool ObjectMonitorTable::remove_monitor_entry(Thread* current, ObjectMonitor* monitor) { - LookupMonitor lookup_f(monitor); - return _table->remove(current, lookup_f); -} - -bool ObjectMonitorTable::contains_monitor(Thread* current, ObjectMonitor* monitor) { - LookupMonitor lookup_f(monitor); - bool result = false; - auto found_f = [&](ObjectMonitor** found) { - result = true; - }; - _table->get(current, lookup_f, found_f); - return result; -} - -void ObjectMonitorTable::print_on(outputStream* st) { - auto printer = [&] (ObjectMonitor** entry) { - ObjectMonitor* om = *entry; - oop obj = om->object_peek(); - st->print("monitor=" PTR_FORMAT ", ", p2i(om)); - st->print("object=" PTR_FORMAT, p2i(obj)); - assert(obj->mark().hash() == om->hash(), "hash must match"); - st->cr(); - return true; - }; - if (SafepointSynchronize::is_at_safepoint()) { - _table->do_safepoint_scan(printer); - } else { - _table->do_scan(Thread::current(), printer); + for (;;) { + // Curr is the latest table and is reasonably loaded. + ObjectMonitor* result = curr->get_set(obj, curr->as_entry(monitor), hash); + if (result != nullptr) { + return result; + } + // The table's limit was reached, we need to grow it. + curr = grow_table(curr); } } + +void ObjectMonitorTable::remove_monitor_entry(Thread* current, ObjectMonitor* monitor) { + oop obj = monitor->object_peek(); + if (obj == nullptr) { + // Defer removal until subsequent rebuilding. + return; + } + const intptr_t hash = obj->mark().hash(); + Table* curr = AtomicAccess::load_acquire(&_curr); + curr->remove(obj, curr->as_entry(monitor), hash); + assert(monitor_get(current, obj) != monitor, "should have been removed"); +} + +// Before handshake; rehash and unlink tables. +void ObjectMonitorTable::rebuild(GrowableArray* delete_list) { + Table* new_table; + { + // Concurrent inserts while in the middle of rebuilding can result + // in the population count increasing past the load factor limit. + // For this to be okay we need to bound how much it may exceed the + // limit. A sequence of tables with doubling capacity may + // eventually, after rebuilding, reach the maximum population of + // max_population(table_1) + max_population(table_1*2) + ... + + // max_population(table_1*2^n). + // I.e. max_population(2*table_1 *2^n) - max_population(table_1). + // With a 12.5% load factor, the implication is that as long as + // rebuilding a table will double its capacity, the maximum load + // after rebuilding is less than 25%. However, we can't always + // double the size each time we rebuild the table. Instead we + // recursively estimate the population count of the chain of + // tables (the current, and all the previous currents). If the sum + // of the population is less than the growing factor, we do not + // need to grow the table. If the new concurrently rebuilding + // table is immediately filled up by concurrent inserts, then the + // worst case load factor after the rebuild may be twice as large, + // which is still guaranteed to be less than a 50% load. If this + // happens, it will cause subsequent rebuilds to increase the + // table capacity, keeping the worst case less than 50%, until the + // load factor eventually becomes less than 12.5% again. So in + // some sense this allows us to be fooled once, but not twice. So, + // given the growing threshold of 12.5%, it is impossible for the + // tables to reach a load factor above 50%. Which is more than + // enough to guarantee the function of this concurrent hash table. + Table* curr = AtomicAccess::load_acquire(&_curr); + size_t need_to_accomodate = curr->total_items(); + size_t new_capacity = curr->should_grow(need_to_accomodate) + ? curr->capacity() << 1 + : curr->capacity(); + new_table = new Table(new_capacity, curr); + Table* result = AtomicAccess::cmpxchg(&_curr, curr, new_table, memory_order_acq_rel); + if (result != curr) { + // Somebody else racingly started rehashing. Delete the + // new_table and treat somebody else's table as the new one. + delete new_table; + new_table = result; + } + log_info(monitorinflation)("Rebuilding object monitor table (capacity: %zu)", + new_table->capacity()); + } + + for (Table* curr = new_table->prev(); curr != nullptr; curr = curr->prev()) { + delete_list->append(curr); + } + + // Rebuild with the new table as target. + new_table->rebuild(); +} + +// After handshake; destroy old tables +void ObjectMonitorTable::destroy(GrowableArray* delete_list) { + for (ObjectMonitorTable::Table* table: *delete_list) { + delete table; + } +} + +address ObjectMonitorTable::current_table_address() { + return (address)(&_curr); +} + +ByteSize ObjectMonitorTable::table_capacity_mask_offset() { + return byte_offset_of(Table, _capacity_mask); +} + +ByteSize ObjectMonitorTable::table_buckets_offset() { + return byte_offset_of(Table, _buckets); +} diff --git a/src/hotspot/share/runtime/objectMonitorTable.hpp b/src/hotspot/share/runtime/objectMonitorTable.hpp index 146468d46b2..e8e7c61c6fa 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.hpp +++ b/src/hotspot/share/runtime/objectMonitorTable.hpp @@ -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 @@ -28,7 +28,9 @@ #include "memory/allStatic.hpp" #include "oops/oopsHierarchy.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/sizes.hpp" +template class GrowableArray; class JavaThread; class ObjectMonitor; class ObjectMonitorTableConfig; @@ -36,42 +38,39 @@ class outputStream; class Thread; class ObjectMonitorTable : AllStatic { - friend class ObjectMonitorTableConfig; + static constexpr double GROW_LOAD_FACTOR = 0.125; - private: - static void inc_items_count(); - static void dec_items_count(); - static double get_load_factor(); - static size_t table_size(Thread* current); - static size_t max_log_size(); - static size_t min_log_size(); +public: + class Table; - template - static size_t clamp_log_size(V log_size); - static size_t initial_log_size(); - static size_t grow_hint(); +private: + static Table* volatile _curr; + static Table* grow_table(Table* curr); + + enum class Entry : uintptr_t { + empty = 0, + tombstone = 1, + removed = 2, + below_is_special = (removed + 1) + }; + +public: + typedef enum { + // Used in the platform code. See: C2_MacroAssembler::fast_lock(). + below_is_special = (int)Entry::below_is_special + } SpecialPointerValues; - public: static void create(); - static void verify_monitor_get_result(oop obj, ObjectMonitor* monitor); static ObjectMonitor* monitor_get(Thread* current, oop obj); - static void try_notify_grow(); - static bool should_shrink() { return false; } // Not implemented - - static constexpr double GROW_LOAD_FACTOR = 0.75; - - static bool should_grow(); - static bool should_resize(); - - template - static bool run_task(JavaThread* current, Task& task, const char* task_name, Args&... args); - static bool grow(JavaThread* current); - static bool clean(JavaThread* current); - static bool resize(JavaThread* current); static ObjectMonitor* monitor_put_get(Thread* current, ObjectMonitor* monitor, oop obj); - static bool remove_monitor_entry(Thread* current, ObjectMonitor* monitor); - static bool contains_monitor(Thread* current, ObjectMonitor* monitor); - static void print_on(outputStream* st); + static void rebuild(GrowableArray* delete_list); + static void destroy(GrowableArray* delete_list); + static void remove_monitor_entry(Thread* current, ObjectMonitor* monitor); + + // Compiler support + static address current_table_address(); + static ByteSize table_capacity_mask_offset(); + static ByteSize table_buckets_offset(); }; #endif // SHARE_RUNTIME_OBJECTMONITORTABLE_HPP diff --git a/src/hotspot/share/runtime/serviceThread.cpp b/src/hotspot/share/runtime/serviceThread.cpp index 27958885a7f..95e672984fa 100644 --- a/src/hotspot/share/runtime/serviceThread.cpp +++ b/src/hotspot/share/runtime/serviceThread.cpp @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,7 +85,6 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { bool cldg_cleanup_work = false; bool jvmti_tagmap_work = false; bool oopmap_cache_work = false; - bool object_monitor_table_work = false; { // Need state transition ThreadBlockInVM so that this thread // will be handled by safepoint correctly when this thread is @@ -112,8 +111,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { (oop_handles_to_release = JavaThread::has_oop_handles_to_release()) | (cldg_cleanup_work = ClassLoaderDataGraph::should_clean_metaspaces_and_reset()) | (jvmti_tagmap_work = JvmtiTagMap::has_object_free_events_and_reset()) | - (oopmap_cache_work = OopMapCache::has_cleanup_work()) | - (object_monitor_table_work = ObjectSynchronizer::needs_resize()) + (oopmap_cache_work = OopMapCache::has_cleanup_work()) ) == 0) { // Wait until notified that there is some work to do or timer expires. // Some cleanup requests don't notify the ServiceThread so work needs to be done at periodic intervals. @@ -171,10 +169,6 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { if (oopmap_cache_work) { OopMapCache::cleanup(); } - - if (object_monitor_table_work) { - ObjectSynchronizer::resize_table(jt); - } } } diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index fde1aa50400..c93e1a65512 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -1195,13 +1195,10 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { GrowableArray delete_list((int)deflated_count); unlinked_count = _in_use_list.unlink_deflated(deflated_count, &delete_list, &safepointer); -#ifdef ASSERT + GrowableArray table_delete_list; if (UseObjectMonitorTable) { - for (ObjectMonitor* monitor : delete_list) { - assert(!ObjectSynchronizer::contains_monitor(current, monitor), "Should have been removed"); - } + ObjectMonitorTable::rebuild(&table_delete_list); } -#endif log.before_handshake(unlinked_count); @@ -1222,6 +1219,9 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { // Delete the unlinked ObjectMonitors. deleted_count = delete_monitors(&delete_list, &safepointer); + if (UseObjectMonitorTable) { + ObjectMonitorTable::destroy(&table_delete_list); + } assert(unlinked_count == deleted_count, "must be"); } @@ -1549,11 +1549,11 @@ ObjectMonitor* ObjectSynchronizer::add_monitor(JavaThread* current, ObjectMonito return ObjectMonitorTable::monitor_put_get(current, monitor, obj); } -bool ObjectSynchronizer::remove_monitor(Thread* current, ObjectMonitor* monitor, oop obj) { +void ObjectSynchronizer::remove_monitor(Thread* current, ObjectMonitor* monitor, oop obj) { assert(UseObjectMonitorTable, "must be"); assert(monitor->object_peek() == obj, "must be, cleared objects are removed by is_dead"); - return ObjectMonitorTable::remove_monitor_entry(current, monitor); + ObjectMonitorTable::remove_monitor_entry(current, monitor); } void ObjectSynchronizer::deflate_mark_word(oop obj) { @@ -1575,20 +1575,6 @@ void ObjectSynchronizer::create_om_table() { ObjectMonitorTable::create(); } -bool ObjectSynchronizer::needs_resize() { - if (!UseObjectMonitorTable) { - return false; - } - return ObjectMonitorTable::should_resize(); -} - -bool ObjectSynchronizer::resize_table(JavaThread* current) { - if (!UseObjectMonitorTable) { - return true; - } - return ObjectMonitorTable::resize(current); -} - class ObjectSynchronizer::LockStackInflateContendedLocks : private OopClosure { private: oop _contended_oops[LockStack::CAPACITY]; @@ -2296,10 +2282,7 @@ ObjectMonitor* ObjectSynchronizer::inflate_and_enter(oop object, BasicLock* lock void ObjectSynchronizer::deflate_monitor(Thread* current, oop obj, ObjectMonitor* monitor) { if (obj != nullptr) { deflate_mark_word(obj); - } - bool removed = remove_monitor(current, monitor, obj); - if (obj != nullptr) { - assert(removed, "Should have removed the entry if obj was alive"); + remove_monitor(current, monitor, obj); } } @@ -2308,11 +2291,6 @@ ObjectMonitor* ObjectSynchronizer::get_monitor_from_table(Thread* current, oop o return ObjectMonitorTable::monitor_get(current, obj); } -bool ObjectSynchronizer::contains_monitor(Thread* current, ObjectMonitor* monitor) { - assert(UseObjectMonitorTable, "must be"); - return ObjectMonitorTable::contains_monitor(current, monitor); -} - ObjectMonitor* ObjectSynchronizer::read_monitor(markWord mark) { return mark.monitor(); } diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index a10e44b3092..97690b9c886 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -213,7 +213,7 @@ public: static ObjectMonitor* get_or_insert_monitor(oop object, JavaThread* current, ObjectSynchronizer::InflateCause cause); static ObjectMonitor* add_monitor(JavaThread* current, ObjectMonitor* monitor, oop obj); - static bool remove_monitor(Thread* current, ObjectMonitor* monitor, oop obj); + static void remove_monitor(Thread* current, ObjectMonitor* monitor, oop obj); static void deflate_mark_word(oop object); From d9e46e3b5e475f17591f458f37f46b4b4eb8b8a8 Mon Sep 17 00:00:00 2001 From: Anton Seoane Ampudia Date: Wed, 25 Feb 2026 09:49:21 +0000 Subject: [PATCH 029/636] 8280283: Dead compiler code found during the JDK-8272058 code review Reviewed-by: bulasevich, dcubed, phh --- .../cpu/aarch64/macroAssembler_aarch64.cpp | 17 ++--------------- .../cpu/aarch64/macroAssembler_aarch64.hpp | 3 +-- src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp | 7 +++---- src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp | 7 +------ 4 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 409343b6b8d..35e90d296c9 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -406,7 +406,6 @@ public: offset <<= shift; uint64_t target_page = ((uint64_t)insn_addr) + offset; target_page &= ((uint64_t)-1) << shift; - uint32_t insn2 = insn_at(insn_addr, 1); target = address(target_page); precond(inner != nullptr); inner(insn_addr, target); @@ -520,13 +519,6 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) { return 2 * NativeInstruction::instruction_size; } -address MacroAssembler::target_addr_for_insn_or_null(address insn_addr) { - if (NativeInstruction::is_ldrw_to_zr(insn_addr)) { - return nullptr; - } - return MacroAssembler::target_addr_for_insn(insn_addr); -} - void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp) { ldr(tmp, Address(rthread, JavaThread::polling_word_offset())); if (at_return) { @@ -1960,9 +1952,7 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, const Register r_array_base = temp1, - r_array_length = temp2, - r_array_index = noreg, // unused - r_bitmap = noreg; // unused + r_array_length = temp2; BLOCK_COMMENT("verify_secondary_supers_table {"); @@ -3611,9 +3601,8 @@ extern "C" void findpc(intptr_t x); void MacroAssembler::debug64(char* msg, int64_t pc, int64_t regs[]) { // In order to get locks to work, we need to fake a in_VM state - if (ShowMessageBoxOnError ) { + if (ShowMessageBoxOnError) { JavaThread* thread = JavaThread::current(); - JavaThreadState saved_state = thread->thread_state(); thread->set_thread_state(_thread_in_vm); #ifndef PRODUCT if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { @@ -5738,7 +5727,6 @@ address MacroAssembler::read_polling_page(Register r, relocInfo::relocType rtype } void MacroAssembler::adrp(Register reg1, const Address &dest, uint64_t &byte_offset) { - relocInfo::relocType rtype = dest.rspec().reloc()->type(); uint64_t low_page = (uint64_t)CodeCache::low_bound() >> 12; uint64_t high_page = (uint64_t)(CodeCache::high_bound()-1) >> 12; uint64_t dest_page = (uint64_t)dest.target() >> 12; @@ -6114,7 +6102,6 @@ void MacroAssembler::string_equals(Register a1, Register a2, Label SAME, DONE, SHORT, NEXT_WORD; Register tmp1 = rscratch1; Register tmp2 = rscratch2; - Register cnt2 = tmp2; // cnt2 only used in array length compare assert_different_registers(a1, a2, result, cnt1, rscratch1, rscratch2); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 7eb6cea0614..7b5af532ca1 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -678,7 +678,6 @@ public: static bool uses_implicit_null_check(void* address); static address target_addr_for_insn(address insn_addr); - static address target_addr_for_insn_or_null(address insn_addr); // Required platform-specific helpers for Label::patch_instructions. // They _shadow_ the declarations in AbstractAssembler, which are undefined. diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index 0cdf36f0bc5..8b76b96d345 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -192,7 +192,6 @@ int NativeMovRegMem::offset() const { void NativeMovRegMem::set_offset(int x) { address pc = instruction_address(); - unsigned insn = *(unsigned*)pc; if (maybe_cpool_ref(pc)) { address addr = MacroAssembler::target_addr_for_insn(pc); *(int64_t*)addr = x; @@ -204,7 +203,7 @@ void NativeMovRegMem::set_offset(int x) { void NativeMovRegMem::verify() { #ifdef ASSERT - address dest = MacroAssembler::target_addr_for_insn_or_null(instruction_address()); + MacroAssembler::target_addr_for_insn(instruction_address()); #endif } @@ -213,7 +212,7 @@ void NativeMovRegMem::verify() { void NativeJump::verify() { ; } address NativeJump::jump_destination() const { - address dest = MacroAssembler::target_addr_for_insn_or_null(instruction_address()); + address dest = MacroAssembler::target_addr_for_insn(instruction_address()); // We use jump to self as the unresolved address which the inline // cache code (and relocs) know about diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index 15b6c9ff215..fc7274714ad 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2025, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -78,7 +78,6 @@ public: inline bool is_nop() const; bool is_jump(); bool is_general_jump(); - inline bool is_jump_or_nop(); inline bool is_cond_jump(); bool is_safepoint_poll(); bool is_movz(); @@ -420,10 +419,6 @@ inline bool NativeInstruction::is_jump() { return false; } -inline bool NativeInstruction::is_jump_or_nop() { - return is_nop() || is_jump(); -} - // Call trampoline stubs. class NativeCallTrampolineStub : public NativeInstruction { public: From 5f098b1284e0b969034cbc159aaedf23d0826ba4 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Wed, 25 Feb 2026 10:49:20 +0000 Subject: [PATCH 030/636] 8376650: os::release_memory_special may not be needed anymore Reviewed-by: coleenp, sjohanss --- src/hotspot/os/aix/os_aix.cpp | 5 ----- src/hotspot/os/bsd/os_bsd.cpp | 5 ----- src/hotspot/os/linux/os_linux.cpp | 6 ------ src/hotspot/os/windows/os_windows.cpp | 5 ----- src/hotspot/share/memory/memoryReserver.cpp | 15 +++------------ src/hotspot/share/runtime/os.cpp | 16 ---------------- src/hotspot/share/runtime/os.hpp | 3 --- test/hotspot/gtest/memory/test_virtualspace.cpp | 8 ++------ test/hotspot/gtest/runtime/test_os_linux.cpp | 10 +++++----- test/hotspot/gtest/runtime/test_os_windows.cpp | 4 ++-- 10 files changed, 12 insertions(+), 65 deletions(-) diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 19cdd6333f8..af743dc7484 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1950,11 +1950,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_ return nullptr; } -bool os::pd_release_memory_special(char* base, size_t bytes) { - fatal("os::release_memory_special should not be called on AIX."); - return false; -} - size_t os::large_page_size() { return _large_page_size; } diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 0ed5335adc3..29ebe65e0db 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -1885,11 +1885,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_ return nullptr; } -bool os::pd_release_memory_special(char* base, size_t bytes) { - fatal("os::release_memory_special should not be called on BSD."); - return false; -} - size_t os::large_page_size() { return _large_page_size; } diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 09c514e3d05..9c2fbab7535 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4208,12 +4208,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_ return addr; } -bool os::pd_release_memory_special(char* base, size_t bytes) { - assert(UseLargePages, "only for large pages"); - // Plain munmap is sufficient - return pd_release_memory(base, bytes); -} - size_t os::large_page_size() { return _large_page_size; } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 2e819e26e37..76f47640e5a 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3517,11 +3517,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_ return reserve_large_pages(bytes, addr, exec); } -bool os::pd_release_memory_special(char* base, size_t bytes) { - assert(base != nullptr, "Sanity check"); - return pd_release_memory(base, bytes); -} - static void warn_fail_commit_memory(char* addr, size_t bytes, bool exec) { int err = os::get_last_error(); char buf[256]; diff --git a/src/hotspot/share/memory/memoryReserver.cpp b/src/hotspot/share/memory/memoryReserver.cpp index 59e8f30707d..1c44f76ed11 100644 --- a/src/hotspot/share/memory/memoryReserver.cpp +++ b/src/hotspot/share/memory/memoryReserver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -231,12 +231,7 @@ ReservedSpace MemoryReserver::reserve(size_t size, void MemoryReserver::release(const ReservedSpace& reserved) { assert(reserved.is_reserved(), "Precondition"); - - if (reserved.special()) { - os::release_memory_special(reserved.base(), reserved.size()); - } else { - os::release_memory(reserved.base(), reserved.size()); - } + os::release_memory(reserved.base(), reserved.size()); } static char* map_memory_to_file(char* requested_address, @@ -372,11 +367,7 @@ ReservedSpace HeapReserver::Instance::reserve_memory(size_t size, void HeapReserver::Instance::release(const ReservedSpace& reserved) { if (reserved.is_reserved()) { if (_fd == -1) { - if (reserved.special()) { - os::release_memory_special(reserved.base(), reserved.size()); - } else{ - os::release_memory(reserved.base(), reserved.size()); - } + os::release_memory(reserved.base(), reserved.size()); } else { os::unmap_memory(reserved.base(), reserved.size()); } diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 58774e59a34..16335f97fdb 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2442,22 +2442,6 @@ char* os::reserve_memory_special(size_t size, size_t alignment, size_t page_size return result; } -void os::release_memory_special(char* addr, size_t bytes) { - bool res; - if (MemTracker::enabled()) { - MemTracker::NmtVirtualMemoryLocker nvml; - res = pd_release_memory_special(addr, bytes); - if (res) { - MemTracker::record_virtual_memory_release(addr, bytes); - } - } else { - res = pd_release_memory_special(addr, bytes); - } - if (!res) { - fatal("Failed to release memory special " RANGEFMT, RANGEFMTARGS(addr, bytes)); - } -} - // Convenience wrapper around naked_short_sleep to allow for longer sleep // times. Only for use by non-JavaThreads. void os::naked_sleep(jlong millis) { diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index e773b40cb14..e185188384f 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -245,9 +245,7 @@ class os: AllStatic { static size_t pd_pretouch_memory(void* first, void* last, size_t page_size); static char* pd_reserve_memory_special(size_t size, size_t alignment, size_t page_size, - char* addr, bool executable); - static bool pd_release_memory_special(char* addr, size_t bytes); static size_t page_size_for_region(size_t region_size, size_t min_pages, bool must_be_aligned); @@ -605,7 +603,6 @@ class os: AllStatic { // reserve, commit and pin the entire memory region static char* reserve_memory_special(size_t size, size_t alignment, size_t page_size, char* addr, bool executable); - static void release_memory_special(char* addr, size_t bytes); static void large_page_init(); static size_t large_page_size(); static bool can_commit_large_page_memory(); diff --git a/test/hotspot/gtest/memory/test_virtualspace.cpp b/test/hotspot/gtest/memory/test_virtualspace.cpp index 70f442c5802..eaabc46edaf 100644 --- a/test/hotspot/gtest/memory/test_virtualspace.cpp +++ b/test/hotspot/gtest/memory/test_virtualspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -354,11 +354,7 @@ class TestReservedSpace : AllStatic { } static void release_memory_for_test(ReservedSpace rs) { - if (rs.special()) { - os::release_memory_special(rs.base(), rs.size()); - } else { - os::release_memory(rs.base(), rs.size()); - } + os::release_memory(rs.base(), rs.size()); } static void test_reserved_space1(size_t size, size_t alignment) { diff --git a/test/hotspot/gtest/runtime/test_os_linux.cpp b/test/hotspot/gtest/runtime/test_os_linux.cpp index 337365592dc..9c624267c18 100644 --- a/test/hotspot/gtest/runtime/test_os_linux.cpp +++ b/test/hotspot/gtest/runtime/test_os_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -59,7 +59,7 @@ namespace { HugeTlbfsMemory(char* const ptr, size_t size) : _ptr(ptr), _size(size) { } ~HugeTlbfsMemory() { if (_ptr != nullptr) { - os::release_memory_special(_ptr, _size); + os::release_memory(_ptr, _size); } } }; @@ -227,7 +227,7 @@ class TestReserveMemorySpecial : AllStatic { char* addr = os::reserve_memory_special(size, alignment, page_size, nullptr, false); if (addr != nullptr) { small_page_write(addr, size); - os::release_memory_special(addr, size); + os::release_memory(addr, size); } } @@ -285,7 +285,7 @@ class TestReserveMemorySpecial : AllStatic { if (p != nullptr) { EXPECT_TRUE(is_aligned(p, alignment)); small_page_write(p, size); - os::release_memory_special(p, size); + os::release_memory(p, size); } } } @@ -300,7 +300,7 @@ class TestReserveMemorySpecial : AllStatic { if (p != nullptr) { EXPECT_EQ(p, req_addr); small_page_write(p, size); - os::release_memory_special(p, size); + os::release_memory(p, size); } } } diff --git a/test/hotspot/gtest/runtime/test_os_windows.cpp b/test/hotspot/gtest/runtime/test_os_windows.cpp index 2d9a7e00b39..13574dcbdb3 100644 --- a/test/hotspot/gtest/runtime/test_os_windows.cpp +++ b/test/hotspot/gtest/runtime/test_os_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -39,7 +39,7 @@ namespace { MemoryReleaser(char* ptr, size_t size) : _ptr(ptr), _size(size) { } ~MemoryReleaser() { if (_ptr != nullptr) { - os::release_memory_special(_ptr, _size); + os::release_memory(_ptr, _size); } } }; From 3a74f16e200c0f543608eac85d6d1d5f51d9c54c Mon Sep 17 00:00:00 2001 From: David Briemann Date: Wed, 25 Feb 2026 11:02:50 +0000 Subject: [PATCH 031/636] 8376113: PPC64: Implement special MachNodes for floating point Min / Max Reviewed-by: mdoerr, rrich --- src/hotspot/cpu/ppc/assembler_ppc.hpp | 10 ++- src/hotspot/cpu/ppc/assembler_ppc.inline.hpp | 7 ++- src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp | 3 +- src/hotspot/cpu/ppc/macroAssembler_ppc.hpp | 4 +- src/hotspot/cpu/ppc/matcher_ppc.hpp | 3 +- src/hotspot/cpu/ppc/ppc.ad | 62 ++++++++++++++++++- .../compiler/igvn/TestMinMaxIdentity.java | 2 +- .../test/ApplicableIRRulesPrinter.java | 5 +- 8 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index 23775a3a52e..da2daffd579 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -599,6 +599,9 @@ class Assembler : public AbstractAssembler { XVMAXSP_OPCODE = (60u << OPCODE_SHIFT | 192u << 3), XVMAXDP_OPCODE = (60u << OPCODE_SHIFT | 224u << 3), + XSMINJDP_OPCODE = (60u << OPCODE_SHIFT | 152u << 3), + XSMAXJDP_OPCODE = (60u << OPCODE_SHIFT | 144u << 3), + // Deliver A Random Number (introduced with POWER9) DARN_OPCODE = (31u << OPCODE_SHIFT | 755u << 1), @@ -2455,6 +2458,9 @@ class Assembler : public AbstractAssembler { inline void xvrdpim( VectorSRegister d, VectorSRegister b); inline void xvrdpip( VectorSRegister d, VectorSRegister b); + inline void xsminjdp( VectorSRegister d, VectorSRegister a, VectorSRegister b); // Requires Power 9 + inline void xsmaxjdp( VectorSRegister d, VectorSRegister a, VectorSRegister b); // Requires Power 9 + // The following functions do not match exactly the Java.math semantics. inline void xvminsp( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xvmindp( VectorSRegister d, VectorSRegister a, VectorSRegister b); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index 4cda782067e..bd6f3300606 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -908,6 +908,9 @@ inline void Assembler::xvrdpic( VectorSRegister d, VectorSRegister b) inline void Assembler::xvrdpim( VectorSRegister d, VectorSRegister b) { emit_int32( XVRDPIM_OPCODE | vsrt(d) | vsrb(b)); } inline void Assembler::xvrdpip( VectorSRegister d, VectorSRegister b) { emit_int32( XVRDPIP_OPCODE | vsrt(d) | vsrb(b)); } +inline void Assembler::xsminjdp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XSMINJDP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } +inline void Assembler::xsmaxjdp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XSMAXJDP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } + inline void Assembler::xvminsp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XVMINSP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xvmindp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XVMINDP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xvmaxsp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XVMAXSP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } diff --git a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp index 2d5071ce86d..8bbffc22c54 100644 --- a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 SAP SE. 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 diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index 875602cae58..58dec702d7a 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index b50de6323de..2ddbec3e48c 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 SAP SE. 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 diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index d926fabd353..4cb9f8820a0 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -1,6 +1,6 @@ // -// Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. -// Copyright (c) 2012, 2025 SAP SE. All rights reserved. +// Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2012, 2026 SAP SE. 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 @@ -2234,6 +2234,12 @@ bool Matcher::match_rule_supported(int opcode) { case Op_FmaVD: return (SuperwordUseVSX && UseFMA); + case Op_MinF: + case Op_MaxF: + case Op_MinD: + case Op_MaxD: + return (PowerArchitecturePPC64 >= 9); + case Op_Digit: return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isDigit); case Op_LowerCase: @@ -12313,6 +12319,58 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC ins_pipe(pipe_class_default); %} +instruct minF(regF dst, regF src1, regF src2) %{ + match(Set dst (MinF src1 src2)); + predicate(PowerArchitecturePPC64 >= 9); + ins_cost(DEFAULT_COST); + + format %{ "MinF $dst, $src1, $src2" %} + size(4); + ins_encode %{ + __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct minD(regD dst, regD src1, regD src2) %{ + match(Set dst (MinD src1 src2)); + predicate(PowerArchitecturePPC64 >= 9); + ins_cost(DEFAULT_COST); + + format %{ "MinD $dst, $src1, $src2" %} + size(4); + ins_encode %{ + __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct maxF(regF dst, regF src1, regF src2) %{ + match(Set dst (MaxF src1 src2)); + predicate(PowerArchitecturePPC64 >= 9); + ins_cost(DEFAULT_COST); + + format %{ "MaxF $dst, $src1, $src2" %} + size(4); + ins_encode %{ + __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct maxD(regD dst, regD src1, regD src2) %{ + match(Set dst (MaxD src1 src2)); + predicate(PowerArchitecturePPC64 >= 9); + ins_cost(DEFAULT_COST); + + format %{ "MaxD $dst, $src1, $src2" %} + size(4); + ins_encode %{ + __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + //---------- Population Count Instructions ------------------------------------ instruct popCountI(iRegIdst dst, iRegIsrc src) %{ diff --git a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java index b4c62fb49a9..f91b153817b 100644 --- a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java +++ b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java @@ -163,7 +163,7 @@ public class TestMinMaxIdentity { """ @IR(counts = {IRNode.#op, "= 1"}, phase = CompilePhase.BEFORE_MACRO_EXPANSION, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "darn", "true"}) @IR(counts = {IRNode.#op, "= 1"}, phase = CompilePhase.BEFORE_MACRO_EXPANSION, applyIfPlatform = {"riscv64", "true"}) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index 4b4ec0f8b26..79f5c7fe18e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java @@ -126,7 +126,10 @@ public class ApplicableIRRulesPrinter { "zfh", "zvbb", "zvfh", - "zvkn" + "zvkn", + // PPC64 + "darn", + "brw" )); /** From 5386a72bc2869f29ef3927768bd9d2273c6dac08 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Wed, 25 Feb 2026 11:22:23 +0000 Subject: [PATCH 032/636] 8378578: Add final to XbmColormap and XbmHints in XbmImageDecoder Reviewed-by: dmarkov, serb, prr, dnguyen --- .../classes/sun/awt/image/XbmImageDecoder.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java index f1ccdcf07f8..1b7cd41daa9 100644 --- a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java @@ -43,22 +43,25 @@ import static java.lang.Math.multiplyExact; /** * Parse files of the form: * + * {@snippet lang=c: * #define foo_width w * #define foo_height h * static char foo_bits[] = { * 0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn, * 0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn, * 0xnn,0xnn,0xnn,0xnn}; + * } * * @author James Gosling */ public class XbmImageDecoder extends ImageDecoder { - private static byte[] XbmColormap = {(byte) 255, (byte) 255, (byte) 255, - 0, 0, 0}; - private static int XbmHints = (ImageConsumer.TOPDOWNLEFTRIGHT | - ImageConsumer.COMPLETESCANLINES | - ImageConsumer.SINGLEPASS | - ImageConsumer.SINGLEFRAME); + private static final byte[] XbmColormap = {(byte) 255, (byte) 255, (byte) 255, + 0, 0, 0}; + private static final int XbmHints = (ImageConsumer.TOPDOWNLEFTRIGHT | + ImageConsumer.COMPLETESCANLINES | + ImageConsumer.SINGLEPASS | + ImageConsumer.SINGLEFRAME); + private static final int MAX_XBM_SIZE = 16384; private static final int HEADER_SCAN_LIMIT = 100; From 3e087d8ebd8c2f860a168b223c5f049dc1c9c068 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Wed, 25 Feb 2026 11:22:43 +0000 Subject: [PATCH 033/636] 8378585: Mark fields in MediaTracker final Reviewed-by: serb, prr --- .../share/classes/java/awt/MediaTracker.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/MediaTracker.java b/src/java.desktop/share/classes/java/awt/MediaTracker.java index f77816dc2e7..02310586c97 100644 --- a/src/java.desktop/share/classes/java/awt/MediaTracker.java +++ b/src/java.desktop/share/classes/java/awt/MediaTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -184,7 +184,7 @@ public class MediaTracker implements java.io.Serializable { * @serial * @see #MediaTracker(Component) */ - Component target; + final Component target; /** * The head of the list of {@code Images} that is being * tracked by the {@code MediaTracker}. @@ -194,7 +194,7 @@ public class MediaTracker implements java.io.Serializable { * @see #removeImage(Image) */ @SuppressWarnings("serial") // Not statically typed as Serializable - MediaEntry head; + private MediaEntry head; /** * Use serialVersionUID from JDK 1.1 for interoperability. @@ -864,8 +864,8 @@ public class MediaTracker implements java.io.Serializable { } abstract class MediaEntry { - MediaTracker tracker; - int ID; + final MediaTracker tracker; + final int ID; MediaEntry next; int status; @@ -897,7 +897,7 @@ abstract class MediaEntry { return head; } - int getID() { + final int getID() { return ID; } @@ -935,12 +935,12 @@ abstract class MediaEntry { * The entry of the list of {@code Images} that is being tracked by the * {@code MediaTracker}. */ -class ImageMediaEntry extends MediaEntry implements ImageObserver, +final class ImageMediaEntry extends MediaEntry implements ImageObserver, java.io.Serializable { @SuppressWarnings("serial") // Not statically typed as Serializable - Image image; - int width; - int height; + final Image image; + final int width; + final int height; /** * Use serialVersionUID from JDK 1.1 for interoperability. @@ -959,10 +959,12 @@ java.io.Serializable { return (image == img && width == w && height == h); } + @Override Object getMedia() { return image; } + @Override synchronized int getStatus(boolean doLoad, boolean doVerify) { if (doVerify) { int flags = tracker.target.checkImage(image, width, height, null); @@ -978,6 +980,7 @@ java.io.Serializable { return super.getStatus(doLoad, doVerify); } + @Override void startLoad() { if (tracker.target.prepareImage(image, width, height, this)) { setStatus(COMPLETE); @@ -995,6 +998,7 @@ java.io.Serializable { return 0; } + @Override public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, int h) { if (cancelled) { From 269c9f3ed53275bcad2b3ce0e1c93cf8eb3ef06d Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Wed, 25 Feb 2026 11:51:51 +0000 Subject: [PATCH 034/636] 8378442: RBTreeCHeap does not deallocate removed nodes when using remove_at_cursor Reviewed-by: jsikstro, jsjolen --- src/hotspot/share/utilities/rbTree.hpp | 2 +- src/hotspot/share/utilities/rbTree.inline.hpp | 13 +++- test/hotspot/gtest/utilities/test_rbtree.cpp | 64 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/utilities/rbTree.hpp b/src/hotspot/share/utilities/rbTree.hpp index 89f9eb256bf..c522d787466 100644 --- a/src/hotspot/share/utilities/rbTree.hpp +++ b/src/hotspot/share/utilities/rbTree.hpp @@ -374,10 +374,10 @@ public: typedef typename BaseType::Cursor Cursor; using BaseType::cursor; using BaseType::insert_at_cursor; - using BaseType::remove_at_cursor; using BaseType::next; using BaseType::prev; + void remove_at_cursor(const Cursor& node_cursor); void replace_at_cursor(RBNode* new_node, const Cursor& node_cursor); RBNode* allocate_node(const K& key); diff --git a/src/hotspot/share/utilities/rbTree.inline.hpp b/src/hotspot/share/utilities/rbTree.inline.hpp index 70d9aee7fbf..381cb916405 100644 --- a/src/hotspot/share/utilities/rbTree.inline.hpp +++ b/src/hotspot/share/utilities/rbTree.inline.hpp @@ -1103,6 +1103,15 @@ bool RBTree::copy_into(RBTree& other) const { return true; } +template +inline void RBTree::remove_at_cursor(const Cursor& node_cursor) { + precond(node_cursor.valid()); + precond(node_cursor.found()); + RBNode* old_node = node_cursor.node(); + BaseType::remove_at_cursor(node_cursor); + free_node(old_node); +} + template inline void RBTree::replace_at_cursor(RBNode* new_node, const Cursor& node_cursor) { precond(new_node != nullptr); @@ -1170,7 +1179,7 @@ inline V* RBTree::find(const K& key) const { template inline void RBTree::remove(RBNode* node) { Cursor node_cursor = cursor(node); - remove_at_cursor(node_cursor); + BaseType::remove_at_cursor(node_cursor); free_node(node); } @@ -1181,7 +1190,7 @@ inline bool RBTree::remove(const K& key) { return false; } RBNode* node = node_cursor.node(); - remove_at_cursor(node_cursor); + BaseType::remove_at_cursor(node_cursor); free_node((RBNode*)node); return true; } diff --git a/test/hotspot/gtest/utilities/test_rbtree.cpp b/test/hotspot/gtest/utilities/test_rbtree.cpp index 17df34fb07a..7eca5f54831 100644 --- a/test/hotspot/gtest/utilities/test_rbtree.cpp +++ b/test/hotspot/gtest/utilities/test_rbtree.cpp @@ -115,6 +115,16 @@ struct ArrayAllocator { using IntrusiveTreeInt = IntrusiveRBTree; using IntrusiveCursor = IntrusiveTreeInt::Cursor; + struct DestructionTracker { + static int destructed_count; + int value; + + DestructionTracker(int value) : value(value) {} + ~DestructionTracker() { destructed_count++; } + + static void reset() { destructed_count = 0; } + }; + public: void inserting_duplicates_results_in_one_value() { constexpr int up_to = 10; @@ -607,6 +617,55 @@ public: } } + void test_remove_destructs() { + using Tree = RBTreeCHeap; + using Node = RBNode; + using Cursor = Tree::Cursor; + + Tree tree; + + // Test the 3 ways of removing a single node + tree.upsert(0, DestructionTracker(0)); + tree.upsert(1, DestructionTracker(1)); + tree.upsert(2, DestructionTracker(2)); + + DestructionTracker::reset(); + + tree.remove(0); + + Node* n = tree.find_node(1); + tree.remove(n); + + Cursor remove_cursor = tree.cursor(2); + tree.remove_at_cursor(remove_cursor); + + EXPECT_EQ(3, DestructionTracker::destructed_count); + + // Test clearing the tree + constexpr int num_nodes = 10; + for (int n = 0; n < num_nodes; n++) { + tree.upsert(n, DestructionTracker(n)); + } + + DestructionTracker::reset(); + + tree.remove_all(); + EXPECT_EQ(num_nodes, DestructionTracker::destructed_count); + + // Test replacing a node + tree.upsert(0, DestructionTracker(0)); + Cursor replace_cursor = tree.cursor(0); + Node* new_node = tree.allocate_node(1, DestructionTracker(1)); + + DestructionTracker::reset(); + + tree.replace_at_cursor(new_node, replace_cursor); + EXPECT_EQ(1, DestructionTracker::destructed_count); + + tree.remove_at_cursor(replace_cursor); + EXPECT_EQ(2, DestructionTracker::destructed_count); + } + void test_cursor() { constexpr int num_nodes = 10; RBTreeInt tree; @@ -971,6 +1030,11 @@ TEST_VM_F(RBTreeTest, NodeHints) { this->test_node_hints(); } +int RBTreeTest::DestructionTracker::destructed_count = 0; +TEST_VM_F(RBTreeTest, RemoveDestructs) { + this->test_remove_destructs(); +} + TEST_VM_F(RBTreeTest, CursorFind) { this->test_cursor(); } From 7b145a51fae9a15e06d169e42d54d08ff250a64a Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Wed, 25 Feb 2026 12:09:25 +0000 Subject: [PATCH 035/636] 8378100: Unused code in rewriter.hpp Reviewed-by: shade, matsaave --- src/hotspot/share/interpreter/rewriter.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hotspot/share/interpreter/rewriter.hpp b/src/hotspot/share/interpreter/rewriter.hpp index b5a4ad4870f..92e8e7db534 100644 --- a/src/hotspot/share/interpreter/rewriter.hpp +++ b/src/hotspot/share/interpreter/rewriter.hpp @@ -78,9 +78,6 @@ class Rewriter: public StackObj { _resolved_reference_limit = _resolved_references_map.length(); } - int cp_entry_to_cp_cache(int i) { assert(has_cp_cache(i), "oob"); return _cp_map.at(i); } - bool has_cp_cache(int i) { return (uint) i < (uint) _cp_map.length() && _cp_map.at(i) >= 0; } - int add_map_entry(int cp_index, GrowableArray* cp_map, GrowableArray* cp_cache_map) { assert(cp_map->at(cp_index) == -1, "not twice on same cp_index"); int cache_index = cp_cache_map->append(cp_index); From 6aed0522ebae2faa6787d6066252a8fcae0c806f Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Wed, 25 Feb 2026 12:49:45 +0000 Subject: [PATCH 036/636] 8373731: C2: Missed optimization opportunity for AddI Reviewed-by: bmaillard, epeter, dlong --- src/hotspot/share/opto/phaseX.cpp | 25 ++-- .../c2/igvn/TestMissingAddSubElimination.java | 117 ++++++++++++++++++ 2 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/igvn/TestMissingAddSubElimination.java diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 0699255a59d..3cbbc114778 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2007,19 +2007,6 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { case Op_ConvI2L: return; - // AddINode::Identity - // Converts (x-y)+y to x - // Could be issue with notification - // - // Turns out AddL does the same. - // - // Found with: - // compiler/c2/Test6792161.java - // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - case Op_AddI: - case Op_AddL: - return; - // AbsINode::Identity // Not investigated yet. case Op_AbsI: @@ -2778,6 +2765,18 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ }; use->visit_uses(push_and_to_worklist, is_boundary); } + + // If changed Sub inputs, check Add for identity. + // e.g., (x - y) + y -> x; x + (y - x) -> y. + if (use_op == Op_SubI || use_op == Op_SubL) { + const int add_op = (use_op == Op_SubI) ? Op_AddI : Op_AddL; + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->Opcode() == add_op) { + worklist.push(u); + } + } + } } /** diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestMissingAddSubElimination.java b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingAddSubElimination.java new file mode 100644 index 00000000000..1679e131505 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingAddSubElimination.java @@ -0,0 +1,117 @@ +/* + * 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.c2.igvn; + +import compiler.lib.ir_framework.*; +import java.util.Random; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +/* + * @test + * @bug 8373731 + * @summary C2 IGVN should eliminate a + (b - c) after c = a * i folds to a, + * i.e. a + (b - a) -> b + * @library /test/lib / + * @run driver ${test.main.class} + */ + +public class TestMissingAddSubElimination { + private static final Random R = Utils.getRandomInstance(); + + public static void main(String[] args) { + TestFramework.runWithFlags("-XX:+IgnoreUnrecognizedVMOptions", + "-XX:VerifyIterativeGVN=1000", + "-XX:CompileCommand=compileonly,compiler.c2.igvn.TestMissingAddSubElimination::*"); + } + + @Run(test = {"testAddI1", "testAddI2", "testAddL1", "testAddL2"}) + public void runTestAdd(){ + int x = R.nextInt(); + int y = R.nextInt(); + Asserts.assertEQ(testAddI1(x, y), y); + Asserts.assertEQ(testAddI2(x, y), x); + + long xl = R.nextLong(); + long yl = R.nextLong(); + Asserts.assertEQ(testAddL1(xl, yl), yl); + Asserts.assertEQ(testAddL2(xl, yl), xl); + } + + // int: x + (y - x) -> y + @Test + @IR(counts = { + IRNode.ADD_I, "0", + IRNode.SUB_I, "0", + IRNode.MUL_I, "0" + }) + int testAddI1(int x, int y) { + int i; + for (i = -10; i < 1; i++) { } + int c = x * i; + return x + (y - c); + } + + // int: (x - y) + y -> x + @Test + @IR(counts = { + IRNode.ADD_I, "0", + IRNode.SUB_I, "0", + IRNode.MUL_I, "0" + }) + int testAddI2(int x, int y) { + int i; + for (i = -10; i < 1; i++) { } + int c = y * i; + return (x - c) + y; + } + + // long: x + (y - x) -> y + @Test + @IR(counts = { + IRNode.ADD_L, "0", + IRNode.SUB_L, "0", + IRNode.MUL_L, "0" + }) + long testAddL1(long x, long y) { + int i; + for (i = -10; i < 1; i++) { } + long c = x * i; + return x + (y - c); + } + + // long: (x - y) + y -> x + @Test + @IR(counts = { + IRNode.ADD_L, "0", + IRNode.SUB_L, "0", + IRNode.MUL_L, "0" + }) + long testAddL2(long x, long y) { + int i; + for (i = -10; i < 1; i++) { } + long c = y * i; + return (x - c) + y; + } +} \ No newline at end of file From ce6ccd385f0eee7dcc52d1dcb93ad8092fd7365d Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 25 Feb 2026 12:53:37 +0000 Subject: [PATCH 037/636] 8378535: Parallel: Replace SpaceCounters with HSpaceCounters Reviewed-by: iwalulya, tschatzl --- src/hotspot/share/gc/parallel/psOldGen.cpp | 13 ++-- src/hotspot/share/gc/parallel/psOldGen.hpp | 5 +- src/hotspot/share/gc/parallel/psYoungGen.cpp | 34 ++++++--- src/hotspot/share/gc/parallel/psYoungGen.hpp | 9 ++- .../share/gc/parallel/spaceCounters.cpp | 76 ------------------- .../share/gc/parallel/spaceCounters.hpp | 71 ----------------- 6 files changed, 38 insertions(+), 170 deletions(-) delete mode 100644 src/hotspot/share/gc/parallel/spaceCounters.cpp delete mode 100644 src/hotspot/share/gc/parallel/spaceCounters.hpp diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp index 974cd6aca59..6a9f8bfeedc 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.cpp +++ b/src/hotspot/share/gc/parallel/psOldGen.cpp @@ -30,6 +30,7 @@ #include "gc/parallel/psOldGen.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/gcLocker.hpp" +#include "gc/shared/hSpaceCounters.hpp" #include "gc/shared/spaceDecorator.hpp" #include "logging/log.hpp" #include "oops/oop.inline.hpp" @@ -113,9 +114,11 @@ void PSOldGen::initialize_performance_counters() { const char* perf_data_name = "old"; _gen_counters = new GenerationCounters(perf_data_name, 1, 1, min_gen_size(), max_gen_size(), virtual_space()->committed_size()); - _space_counters = new SpaceCounters(perf_data_name, 0, - virtual_space()->reserved_size(), - _object_space, _gen_counters); + _space_counters = new HSpaceCounters(_gen_counters->name_space(), + perf_data_name, + 0, + virtual_space()->reserved_size(), + _object_space->capacity_in_bytes()); } HeapWord* PSOldGen::expand_and_allocate(size_t word_size) { @@ -266,7 +269,7 @@ bool PSOldGen::expand_by(size_t bytes) { } post_resize(); if (UsePerfData) { - _space_counters->update_capacity(); + _space_counters->update_capacity(_object_space->capacity_in_bytes()); _gen_counters->update_capacity(_virtual_space->committed_size()); } } @@ -394,7 +397,7 @@ void PSOldGen::print_on(outputStream* st) const { void PSOldGen::update_counters() { if (UsePerfData) { - _space_counters->update_all(); + _space_counters->update_all(_object_space->capacity_in_bytes(), _object_space->used_in_bytes()); _gen_counters->update_capacity(_virtual_space->committed_size()); } } diff --git a/src/hotspot/share/gc/parallel/psOldGen.hpp b/src/hotspot/share/gc/parallel/psOldGen.hpp index 05359b12836..7e3975036d4 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.hpp +++ b/src/hotspot/share/gc/parallel/psOldGen.hpp @@ -28,7 +28,8 @@ #include "gc/parallel/mutableSpace.hpp" #include "gc/parallel/objectStartArray.hpp" #include "gc/parallel/psVirtualspace.hpp" -#include "gc/parallel/spaceCounters.hpp" +#include "gc/shared/generationCounters.hpp" +#include "gc/shared/hSpaceCounters.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" @@ -43,7 +44,7 @@ class PSOldGen : public CHeapObj { // Performance Counters GenerationCounters* _gen_counters; - SpaceCounters* _space_counters; + HSpaceCounters* _space_counters; // Sizing information, in bytes, set in constructor const size_t _min_gen_size; diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp index 593df38e985..870e912f51e 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.cpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp @@ -28,6 +28,7 @@ #include "gc/parallel/psYoungGen.hpp" #include "gc/shared/gcUtil.hpp" #include "gc/shared/genArguments.hpp" +#include "gc/shared/hSpaceCounters.hpp" #include "gc/shared/spaceDecorator.hpp" #include "logging/log.hpp" #include "oops/oop.inline.hpp" @@ -133,12 +134,21 @@ void PSYoungGen::initialize_work() { max_eden_size = size - 2 * max_survivor_size; } - _eden_counters = new SpaceCounters("eden", 0, max_eden_size, _eden_space, - _gen_counters); - _from_counters = new SpaceCounters("s0", 1, max_survivor_size, _from_space, - _gen_counters); - _to_counters = new SpaceCounters("s1", 2, max_survivor_size, _to_space, - _gen_counters); + _eden_counters = new HSpaceCounters(_gen_counters->name_space(), + "eden", + 0, + max_eden_size, + _eden_space->capacity_in_bytes()); + _from_counters = new HSpaceCounters(_gen_counters->name_space(), + "s0", + 1, + max_survivor_size, + _from_space->capacity_in_bytes()); + _to_counters = new HSpaceCounters(_gen_counters->name_space(), + "s1", + 2, + max_survivor_size, + _to_space->capacity_in_bytes()); compute_initial_space_boundaries(); } @@ -161,9 +171,9 @@ void PSYoungGen::compute_initial_space_boundaries() { space_invariants(); if (UsePerfData) { - _eden_counters->update_capacity(); - _from_counters->update_capacity(); - _to_counters->update_capacity(); + _eden_counters->update_capacity(_eden_space->capacity_in_bytes()); + _from_counters->update_capacity(_from_space->capacity_in_bytes()); + _to_counters->update_capacity(_to_space->capacity_in_bytes()); } } @@ -606,9 +616,9 @@ void PSYoungGen::post_resize() { void PSYoungGen::update_counters() { if (UsePerfData) { - _eden_counters->update_all(); - _from_counters->update_all(); - _to_counters->update_all(); + _eden_counters->update_all(_eden_space->capacity_in_bytes(), _eden_space->used_in_bytes()); + _from_counters->update_all(_from_space->capacity_in_bytes(), _from_space->used_in_bytes()); + _to_counters->update_all(_to_space->capacity_in_bytes(), _to_space->used_in_bytes()); _gen_counters->update_capacity(_virtual_space->committed_size()); } } diff --git a/src/hotspot/share/gc/parallel/psYoungGen.hpp b/src/hotspot/share/gc/parallel/psYoungGen.hpp index 98337d53184..10aa037da98 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.hpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.hpp @@ -28,7 +28,8 @@ #include "gc/parallel/mutableSpace.hpp" #include "gc/parallel/objectStartArray.hpp" #include "gc/parallel/psVirtualspace.hpp" -#include "gc/parallel/spaceCounters.hpp" +#include "gc/shared/generationCounters.hpp" +#include "gc/shared/hSpaceCounters.hpp" class ReservedSpace; @@ -51,9 +52,9 @@ class PSYoungGen : public CHeapObj { // Performance counters GenerationCounters* _gen_counters; - SpaceCounters* _eden_counters; - SpaceCounters* _from_counters; - SpaceCounters* _to_counters; + HSpaceCounters* _eden_counters; + HSpaceCounters* _from_counters; + HSpaceCounters* _to_counters; // Initialize the space boundaries void compute_initial_space_boundaries(); diff --git a/src/hotspot/share/gc/parallel/spaceCounters.cpp b/src/hotspot/share/gc/parallel/spaceCounters.cpp deleted file mode 100644 index e41b36c9ed2..00000000000 --- a/src/hotspot/share/gc/parallel/spaceCounters.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2002, 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. - * - */ - -#include "gc/parallel/spaceCounters.hpp" -#include "memory/allocation.inline.hpp" -#include "memory/resourceArea.hpp" -#include "runtime/mutex.hpp" -#include "runtime/mutexLocker.hpp" -#include "utilities/debug.hpp" -#include "utilities/macros.hpp" - -SpaceCounters::SpaceCounters(const char* name, int ordinal, size_t max_size, - MutableSpace* m, GenerationCounters* gc) - : _object_space(m) { - if (UsePerfData) { - EXCEPTION_MARK; - ResourceMark rm; - - const char* cns = PerfDataManager::name_space(gc->name_space(), "space", - ordinal); - - _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1, mtGC); - strcpy(_name_space, cns); - - const char* cname = PerfDataManager::counter_name(_name_space, "name"); - PerfDataManager::create_string_constant(SUN_GC, cname, name, CHECK); - - cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); - PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - (jlong)max_size, CHECK); - - cname = PerfDataManager::counter_name(_name_space, "capacity"); - _capacity = PerfDataManager::create_variable(SUN_GC, cname, - PerfData::U_Bytes, - _object_space->capacity_in_bytes(), - CHECK); - - cname = PerfDataManager::counter_name(_name_space, "used"); - _used = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, - _object_space->used_in_bytes(), - CHECK); - - cname = PerfDataManager::counter_name(_name_space, "initCapacity"); - PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - _object_space->capacity_in_bytes(), CHECK); - } -} - -SpaceCounters::~SpaceCounters() { - FREE_C_HEAP_ARRAY(char, _name_space); -} - -void SpaceCounters::update_used() { - _used->set_value(_object_space->used_in_bytes()); -} diff --git a/src/hotspot/share/gc/parallel/spaceCounters.hpp b/src/hotspot/share/gc/parallel/spaceCounters.hpp deleted file mode 100644 index 86fc8438b23..00000000000 --- a/src/hotspot/share/gc/parallel/spaceCounters.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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. - * - */ - -#ifndef SHARE_GC_PARALLEL_SPACECOUNTERS_HPP -#define SHARE_GC_PARALLEL_SPACECOUNTERS_HPP - -#include "gc/parallel/mutableSpace.hpp" -#include "gc/shared/generationCounters.hpp" -#include "runtime/perfData.hpp" -#include "utilities/macros.hpp" - -// A SpaceCounter is a holder class for performance counters -// that track a space; - -class SpaceCounters: public CHeapObj { - friend class VMStructs; - - private: - PerfVariable* _capacity; - PerfVariable* _used; - - // Constant PerfData types don't need to retain a reference. - // However, it's a good idea to document them here. - // PerfConstant* _size; - - MutableSpace* _object_space; - char* _name_space; - - public: - - SpaceCounters(const char* name, int ordinal, size_t max_size, - MutableSpace* m, GenerationCounters* gc); - - ~SpaceCounters(); - - inline void update_capacity() { - _capacity->set_value(_object_space->capacity_in_bytes()); - } - - void update_used(); - - inline void update_all() { - update_used(); - update_capacity(); - } - - const char* name_space() const { return _name_space; } -}; - -#endif // SHARE_GC_PARALLEL_SPACECOUNTERS_HPP From 93fe49abef85e6c1fcf3a9b853cdcf05c557acba Mon Sep 17 00:00:00 2001 From: Sean Coffey Date: Wed, 25 Feb 2026 12:57:30 +0000 Subject: [PATCH 038/636] 8371333: Optimize static initialization of SSLContextImpl classes and improve logging Reviewed-by: hchao, jnimeh --- .../sun/security/ssl/SSLContextImpl.java | 95 +++++++++++++------ .../classes/sun/security/ssl/Utilities.java | 31 +++++- .../SSLLogger/DebugPropertyValuesTest.java | 29 +++--- 3 files changed, 112 insertions(+), 43 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java index 8df72711dff..a1cc3ee112f 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -31,6 +31,7 @@ import java.security.*; import java.security.cert.*; import java.util.*; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import javax.net.ssl.*; import sun.security.provider.certpath.AlgorithmChecker; import sun.security.ssl.SSLAlgorithmConstraints.SIGNATURE_CONSTRAINTS_MODE; @@ -366,13 +367,24 @@ public abstract class SSLContextImpl extends SSLContextSpi { Collection allowedCipherSuites, List protocols) { LinkedHashSet suites = new LinkedHashSet<>(); + List disabledSuites = null; + List unAvailableSuites = null; + + if (SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.SSLCTX)) { + disabledSuites = new ArrayList<>(); + unAvailableSuites = new ArrayList<>(); + } + if (protocols != null && (!protocols.isEmpty())) { for (CipherSuite suite : allowedCipherSuites) { if (!suite.isAvailable()) { + if (SSLLogger.isOn() && + SSLLogger.isOn(SSLLogger.Opt.SSLCTX)) { + unAvailableSuites.add(suite.name); + } continue; } - boolean isSupported = false; for (ProtocolVersion protocol : protocols) { if (!suite.supports(protocol) || !suite.bulkCipher.isAvailable()) { @@ -383,27 +395,43 @@ public abstract class SSLContextImpl extends SSLContextSpi { EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); - isSupported = true; } else if (SSLLogger.isOn() && - SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { - SSLLogger.fine( - "Ignore disabled cipher suite: " + suite.name); + SSLLogger.isOn(SSLLogger.Opt.SSLCTX)) { + disabledSuites.add(suite.name); } break; } - - if (!isSupported && SSLLogger.isOn() && - SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) { - SSLLogger.finest( - "Ignore unsupported cipher suite: " + suite); - } } } + if(SSLLogger.isOn() && SSLLogger.isOn(SSLLogger.Opt.SSLCTX)) { + logSuites("Ignore disabled cipher suites for protocols: ", + protocols, disabledSuites); + logSuites("Ignore unavailable cipher suites for protocols: ", + protocols, unAvailableSuites); + logSuites("Available cipher suites for protocols: ", + protocols, suites); + + } return new ArrayList<>(suites); } + private static void logSuites(String message, + List protocols, + Collection suites) { + if (suites.isEmpty()) { + return; + } + String protocolStr = protocols.stream() + .map(pv -> pv.name) + .collect(Collectors.joining(", ", "[", "]")); + String suiteStr = String.join(", ", + suites.stream().map(Object::toString).collect(Collectors.toList())); + SSLLogger.finest(message + protocolStr + System.lineSeparator() + + Utilities.wrapText("[" + suiteStr + "]", 140)); + } + /* * Get the customized cipher suites specified by the given system property. */ @@ -459,10 +487,14 @@ public abstract class SSLContextImpl extends SSLContextSpi { } } } - + if (cipherSuites.isEmpty() && SSLLogger.isOn() + && SSLLogger.isOn(SSLLogger.Opt.SSLCTX)) { + SSLLogger.fine( + "No cipher suites satisfy property: " + propertyName + + ". Returning empty list"); + } return cipherSuites; } - return Collections.emptyList(); } @@ -530,9 +562,6 @@ public abstract class SSLContextImpl extends SSLContextSpi { private static final List supportedProtocols; private static final List serverDefaultProtocols; - private static final List supportedCipherSuites; - private static final List serverDefaultCipherSuites; - static { supportedProtocols = Arrays.asList( ProtocolVersion.TLS13, @@ -550,13 +579,15 @@ public abstract class SSLContextImpl extends SSLContextSpi { ProtocolVersion.TLS11, ProtocolVersion.TLS10 }); - - supportedCipherSuites = getApplicableSupportedCipherSuites( - supportedProtocols); - serverDefaultCipherSuites = getApplicableEnabledCipherSuites( - serverDefaultProtocols, false); } + private static final LazyConstant> + supportedCipherSuites = LazyConstant.of(() -> + getApplicableSupportedCipherSuites(supportedProtocols)); + private static final LazyConstant> + serverDefaultCipherSuites = LazyConstant.of(() -> + getApplicableEnabledCipherSuites(serverDefaultProtocols, false)); + @Override List getSupportedProtocolVersions() { return supportedProtocols; @@ -564,7 +595,7 @@ public abstract class SSLContextImpl extends SSLContextSpi { @Override List getSupportedCipherSuites() { - return supportedCipherSuites; + return supportedCipherSuites.get(); } @Override @@ -574,7 +605,7 @@ public abstract class SSLContextImpl extends SSLContextSpi { @Override List getServerDefaultCipherSuites() { - return serverDefaultCipherSuites; + return serverDefaultCipherSuites.get(); } @Override @@ -814,10 +845,18 @@ public abstract class SSLContextImpl extends SSLContextSpi { clientDefaultCipherSuites = getApplicableEnabledCipherSuites( clientDefaultProtocols, true); - serverDefaultCipherSuites = - getApplicableEnabledCipherSuites( - serverDefaultProtocols, false); - + // getApplicableEnabledCipherSuites returns same CS List if + // no customized CS in use and protocols are same. Can avoid + // the getApplicableEnabledCipherSuites call + if (clientCustomizedCipherSuites.isEmpty() && + serverCustomizedCipherSuites.isEmpty() && + clientDefaultProtocols.equals(serverDefaultProtocols)) { + serverDefaultCipherSuites = clientDefaultCipherSuites; + } else { + serverDefaultCipherSuites = + getApplicableEnabledCipherSuites( + serverDefaultProtocols, false); + } } else { // unlikely to be used clientDefaultProtocols = null; diff --git a/src/java.base/share/classes/sun/security/ssl/Utilities.java b/src/java.base/share/classes/sun/security/ssl/Utilities.java index e289a9e1bd6..cfd39aecac2 100644 --- a/src/java.base/share/classes/sun/security/ssl/Utilities.java +++ b/src/java.base/share/classes/sun/security/ssl/Utilities.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -168,6 +168,35 @@ final class Utilities { return builder.toString(); } + static String wrapText(String text, int maxWidth) { + if (text == null || text.isEmpty() || maxWidth <= 0) { + return ""; + } + StringBuilder result = new StringBuilder(); + String[] values = text.split(",\\s*"); + StringBuilder line = new StringBuilder(); + + for (int i = 0; i < values.length; i++) { + String value = values[i]; + // If adding this value would exceed maxWidth + if (line.length() > 0) { + // +1 for the comma + if (line.length() + 1 + value.length() > maxWidth) { + result.append(line).append(LINE_SEP); + line.setLength(0); + } else { + line.append(","); + } + } + line.append(value); + } + // Append any remaining line + if (line.length() > 0) { + result.append(line); + } + return result.toString(); + } + static String byte16HexString(int id) { return "0x" + HEX_FORMATTER.toHexDigits((short)id); } diff --git a/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java index 77f585d6d55..59becf3b664 100644 --- a/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java +++ b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.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 @@ -23,7 +23,7 @@ /** * @test - * @bug 8350582 8340312 8369995 8044609 8372004 + * @bug 8350582 8340312 8369995 8044609 8372004 8371333 * @library /test/lib /javax/net/ssl/templates * @summary Correct the parsing of the ssl value in javax.net.debug * @run junit DebugPropertyValuesTest @@ -60,8 +60,8 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { "supported_versions")); debugMessages.put("handshake-expand", List.of("\"logger\".*: \"javax.net.ssl\",", - "\"specifics\" : \\[", - "\"message\".*: \"Produced ClientHello handshake message")); + "\"specifics\" : \\[", + "\"message\".*: \"Produced ClientHello handshake message")); debugMessages.put("keymanager", List.of("Choosing key:")); debugMessages.put("packet", List.of("Raw write")); debugMessages.put("plaintext", @@ -73,11 +73,13 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { debugMessages.put("session", List.of("Session initialized:")); debugMessages.put("ssl", List.of("jdk.tls.keyLimits:")); debugMessages.put("sslctx", - List.of("trigger seeding of SecureRandom")); + List.of("trigger seeding of SecureRandom", + // Available list should finish with this style + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV]", + "Ignore disabled cipher suites for protocols: " + + "\\[TLSv1.3, TLSv1.2\\]")); debugMessages.put("trustmanager", List.of("adding as trusted certificates")); - debugMessages.put("verbose", - List.of("Ignore unsupported cipher suite:")); debugMessages.put("help", List.of("print this help message and exit", "verbose handshake message printing")); @@ -110,17 +112,16 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { Arguments.of(List.of("-Djavax.net.debug=all"), List.of("handshake", "keymanager", "packet", "plaintext", "record", "session", "ssl", - "sslctx", "trustmanager", "verbose")), + "sslctx", "trustmanager")), // ssl should print most details except verbose details Arguments.of(List.of("-Djavax.net.debug=ssl"), List.of("handshake", "keymanager", "record", "session", "ssl", - "sslctx", "trustmanager", "verbose")), + "sslctx", "trustmanager")), // allow expand option for more verbose output Arguments.of( List.of("-Djavax.net.debug=ssl,handshake,expand"), - List.of("handshake", "handshake-expand", - "ssl", "verbose")), + List.of("handshake", "handshake-expand", "ssl")), // filtering on record option, with expand Arguments.of(List.of("-Djavax.net.debug=ssl:record,expand"), List.of("record", "record-expand", "ssl")), @@ -142,12 +143,12 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { Arguments.of(List.of("-Djavax.net.debug=ssl,typo"), List.of("handshake", "keymanager", "record", "session", "ssl", - "sslctx", "trustmanager", "verbose")), + "sslctx", "trustmanager")), // ssltypo contains "ssl". Treat like "ssl" Arguments.of(List.of("-Djavax.net.debug=ssltypo"), List.of("handshake", "keymanager", "record", "session", "ssl", - "sslctx", "trustmanager", "verbose")), + "sslctx", "trustmanager")), // plaintext is valid for record option Arguments.of(List.of("-Djavax.net.debug=ssl:record:plaintext"), List.of("plaintext", "record", "ssl")), @@ -168,7 +169,7 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { List.of("handshake", "javax.net.debug.logger", "keymanager", "packet", "plaintext", "record", "session", "ssl", - "sslctx", "trustmanager", "verbose")) + "sslctx", "trustmanager")) ); } From 194be8180f89cd247d31e5ecdb23cb5261db2625 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Wed, 25 Feb 2026 13:13:51 +0000 Subject: [PATCH 039/636] 8374807: Crash in MethodData::extra_data_lock()+0x0 when running -XX:+TraceDeoptimization -XX:-ProfileTraps -XX:-TieredCompilation -Xcomp -version Reviewed-by: vlivanov, epeter --- src/hotspot/share/runtime/deoptimization.cpp | 6 +- ...stPrintDiagnosticsWithoutProfileTraps.java | 58 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 2beba9abb06..54a65358693 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -2155,7 +2155,9 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr // Lock to read ProfileData, and ensure lock is not broken by a safepoint // We must do this already now, since we cannot acquire this lock while // holding the tty lock (lock ordering by rank). - MutexLocker ml(trap_mdo->extra_data_lock(), Mutex::_no_safepoint_check_flag); + ConditionalMutexLocker ml((trap_mdo != nullptr) ? trap_mdo->extra_data_lock() : nullptr, + (trap_mdo != nullptr), + Mutex::_no_safepoint_check_flag); ttyLocker ttyl; diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java new file mode 100644 index 00000000000..51b30219aca --- /dev/null +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestPrintDiagnosticsWithoutProfileTraps.java @@ -0,0 +1,58 @@ +/* + * 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 8374807 + * @summary Regression test for -XX:+TraceDeoptimization -XX:-ProfileTraps + * -XX:-TieredCompilation -Xcomp crash + * @modules java.base/jdk.internal.misc + * @requires vm.debug + * @run main/othervm -XX:+TraceDeoptimization -XX:-ProfileTraps + * -XX:-TieredCompilation -Xcomp -Xbatch + * -XX:CompileCommand=compileonly,compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps::test + * compiler.uncommontrap.TestPrintDiagnosticsWithoutProfileTraps + */ + +package compiler.uncommontrap; + +import jdk.internal.misc.Unsafe; + +public class TestPrintDiagnosticsWithoutProfileTraps { + static final Unsafe UNSAFE=Unsafe.getUnsafe(); + static volatile long sink; + + static class TestSubClass { + int f; + } + + static void test(){ + // Trigger a deopt/uncommon-trap path while resolving the field offset. + sink = UNSAFE.objectFieldOffset(TestSubClass.class,"f"); + } + + public static void main(String[] args) { + test(); + System.out.println("passed"); + } +} \ No newline at end of file From a3684a79527ff33ec32c8eeda5c8a536aa6f814b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Wed, 25 Feb 2026 15:14:58 +0000 Subject: [PATCH 040/636] 8377909: Replace SummaryDiff's array implementation with a hashtable Reviewed-by: azafari, phubner --- src/hotspot/share/nmt/memoryFileTracker.cpp | 22 +- .../share/nmt/virtualMemoryTracker.cpp | 18 +- .../share/nmt/virtualMemoryTracker.hpp | 10 +- src/hotspot/share/nmt/vmatree.cpp | 142 ++++++++++-- src/hotspot/share/nmt/vmatree.hpp | 58 ++++- .../gtest/nmt/test_nmt_summarydiff.cpp | 42 ++++ test/hotspot/gtest/nmt/test_regions_tree.cpp | 30 +-- test/hotspot/gtest/nmt/test_vmatree.cpp | 215 +++++++++--------- 8 files changed, 359 insertions(+), 178 deletions(-) create mode 100644 test/hotspot/gtest/nmt/test_nmt_summarydiff.cpp diff --git a/src/hotspot/share/nmt/memoryFileTracker.cpp b/src/hotspot/share/nmt/memoryFileTracker.cpp index fe723d09364..0a458347169 100644 --- a/src/hotspot/share/nmt/memoryFileTracker.cpp +++ b/src/hotspot/share/nmt/memoryFileTracker.cpp @@ -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 @@ -44,21 +44,21 @@ void MemoryFileTracker::allocate_memory(MemoryFile* file, size_t offset, VMATree::RegionData regiondata(sidx, mem_tag); VMATree::SummaryDiff diff; file->_tree.commit_mapping(offset, size, regiondata, diff); - for (int i = 0; i < mt_number_of_tags; i++) { - VirtualMemory* summary = file->_summary.by_tag(NMTUtil::index_to_tag(i)); - summary->reserve_memory(diff.tag[i].commit); - summary->commit_memory(diff.tag[i].commit); - } + diff.visit([&](MemTag mt, const VMATree::SingleDiff& single_diff) { + VirtualMemory* summary = file->_summary.by_tag(mt); + summary->reserve_memory(single_diff.commit); + summary->commit_memory(single_diff.commit); + }); } void MemoryFileTracker::free_memory(MemoryFile* file, size_t offset, size_t size) { VMATree::SummaryDiff diff; file->_tree.release_mapping(offset, size, diff); - for (int i = 0; i < mt_number_of_tags; i++) { - VirtualMemory* summary = file->_summary.by_tag(NMTUtil::index_to_tag(i)); - summary->reserve_memory(diff.tag[i].commit); - summary->commit_memory(diff.tag[i].commit); - } + diff.visit([&](MemTag mt, const VMATree::SingleDiff& single_diff) { + VirtualMemory* summary = file->_summary.by_tag(mt); + summary->reserve_memory(single_diff.commit); + summary->commit_memory(single_diff.commit); + }); } void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* stream, size_t scale) { diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index 4e4138f81a2..dc68664f34f 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -80,16 +80,17 @@ void VirtualMemoryTracker::Instance::set_reserved_region_tag(address addr, size_ } void VirtualMemoryTracker::set_reserved_region_tag(address addr, size_t size, MemTag mem_tag) { - VMATree::SummaryDiff diff = tree()->set_tag((VMATree::position) addr, size, mem_tag); - apply_summary_diff(diff); + VMATree::SummaryDiff diff; + tree()->set_tag((VMATree::position)addr, size, mem_tag, diff); + apply_summary_diff(diff); } -void VirtualMemoryTracker::Instance::apply_summary_diff(VMATree::SummaryDiff diff) { +void VirtualMemoryTracker::Instance::apply_summary_diff(VMATree::SummaryDiff& diff) { assert(_tracker != nullptr, "Sanity check"); _tracker->apply_summary_diff(diff); } -void VirtualMemoryTracker::apply_summary_diff(VMATree::SummaryDiff diff) { +void VirtualMemoryTracker::apply_summary_diff(VMATree::SummaryDiff& diff) { VMATree::SingleDiff::delta reserve_delta, commit_delta; size_t reserved, committed; MemTag tag = mtNone; @@ -104,10 +105,9 @@ void VirtualMemoryTracker::apply_summary_diff(VMATree::SummaryDiff diff) { #endif }; - for (int i = 0; i < mt_number_of_tags; i++) { - reserve_delta = diff.tag[i].reserve; - commit_delta = diff.tag[i].commit; - tag = NMTUtil::index_to_tag(i); + diff.visit([&](MemTag tag, const VMATree::SingleDiff& single_diff) { + reserve_delta = single_diff.reserve; + commit_delta = single_diff.commit; reserved = VirtualMemorySummary::as_snapshot()->by_tag(tag)->reserved(); committed = VirtualMemorySummary::as_snapshot()->by_tag(tag)->committed(); if (reserve_delta != 0) { @@ -138,7 +138,7 @@ void VirtualMemoryTracker::apply_summary_diff(VMATree::SummaryDiff diff) { } } } - } + }); } void VirtualMemoryTracker::Instance::add_committed_region(address addr, size_t size, diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index 3ed8bf93e03..f6cb18983a3 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp @@ -309,9 +309,9 @@ class VirtualMemoryTracker { // Snapshot current thread stacks void snapshot_thread_stacks(); - void apply_summary_diff(VMATree::SummaryDiff diff); - size_t committed_size(const VirtualMemoryRegion* rgn); - address thread_stack_uncommitted_bottom(const VirtualMemoryRegion* rgn); + void apply_summary_diff(VMATree::SummaryDiff& diff); + size_t committed_size(const VirtualMemoryRegion* rmr); + address thread_stack_uncommitted_bottom(const VirtualMemoryRegion* rmr); RegionsTree* tree() { return &_tree; } @@ -334,8 +334,8 @@ class VirtualMemoryTracker { static bool walk_virtual_memory(VirtualMemoryWalker* walker); static bool print_containing_region(const void* p, outputStream* st); static void snapshot_thread_stacks(); - static void apply_summary_diff(VMATree::SummaryDiff diff); - static size_t committed_size(const VirtualMemoryRegion* rgn); + static void apply_summary_diff(VMATree::SummaryDiff& diff); + static size_t committed_size(const VirtualMemoryRegion* rmr); // uncommitted thread stack bottom, above guard pages if there is any. static address thread_stack_uncommitted_bottom(const VirtualMemoryRegion* rgn); diff --git a/src/hotspot/share/nmt/vmatree.cpp b/src/hotspot/share/nmt/vmatree.cpp index df7b1ac867e..6c5ab691f6d 100644 --- a/src/hotspot/share/nmt/vmatree.cpp +++ b/src/hotspot/share/nmt/vmatree.cpp @@ -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. * Copyright (c) 2024, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #include "nmt/vmatree.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" +#include "utilities/powerOfTwo.hpp" // Semantics @@ -192,8 +193,8 @@ void VMATree::compute_summary_diff(const SingleDiff::delta region_size, {0,a, 0,a, -a,a }, // op == Commit {0,0, 0,0, -a,0 } // op == Uncommit }; - SingleDiff& from_rescom = diff.tag[NMTUtil::tag_to_index(current_tag)]; - SingleDiff& to_rescom = diff.tag[NMTUtil::tag_to_index(operation_tag)]; + SingleDiff& from_rescom = diff.tag(current_tag); + SingleDiff& to_rescom = diff.tag(operation_tag); int st = state_to_index(ex); from_rescom.reserve += reserve[op][st * 2 ]; to_rescom.reserve += reserve[op][st * 2 + 1]; @@ -657,7 +658,7 @@ void VMATree::print_on(outputStream* out) { } #endif -VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, const MemTag tag) { +void VMATree::set_tag(const position start, const size size, const MemTag tag, SummaryDiff& diff) { auto pos = [](TNode* n) { return n->key(); }; position from = start; position end = from+size; @@ -689,14 +690,13 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con }; bool success = find_next_range(); - if (!success) return SummaryDiff(); + if (!success) return; assert(range.start != nullptr && range.end != nullptr, "must be"); end = MIN2(from + remsize, pos(range.end)); IntervalState& out = out_state(range.start); StateType type = out.type(); - SummaryDiff diff; // Ignore any released ranges, these must be mtNone and have no stack if (type != StateType::Released) { RegionData new_data = RegionData(out.reserved_stack(), tag); @@ -713,7 +713,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con // Using register_mapping may invalidate the already found range, so we must // use find_next_range repeatedly bool success = find_next_range(); - if (!success) return diff; + if (!success) return; assert(range.start != nullptr && range.end != nullptr, "must be"); end = MIN2(from + remsize, pos(range.end)); @@ -729,25 +729,131 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con remsize = remsize - (end - from); from = end; } - - return diff; } #ifdef ASSERT void VMATree::SummaryDiff::print_on(outputStream* out) { - for (int i = 0; i < mt_number_of_tags; i++) { - if (tag[i].reserve == 0 && tag[i].commit == 0) { - continue; - } - out->print_cr("Tag %s R: " INT64_FORMAT " C: " INT64_FORMAT, NMTUtil::tag_to_enum_name((MemTag)i), tag[i].reserve, - tag[i].commit); - } + visit([&](MemTag mt, const SingleDiff& sd) { + out->print_cr("Tag %s R: " INT64_FORMAT " C: " INT64_FORMAT, + NMTUtil::tag_to_enum_name(mt), sd.reserve, sd.commit); + }); } #endif void VMATree::clear() { _tree.remove_all(); -}; +} + bool VMATree::is_empty() { return _tree.size() == 0; -}; +} + +VMATree::SummaryDiff::KVEntry& +VMATree::SummaryDiff::hash_insert_or_get(const KVEntry& kv, bool* found) { + DEBUG_ONLY(int counter = 0); + // If the length is large (picked as 32) + // then we apply a load-factor check and rehash if it exceeds it. + // When the length is small we're OK with a full linear search for an empty space + // to avoid a grow and rehash. + constexpr float load_factor = 0.5; + constexpr int load_factor_cutoff_length = 32; + if (_length > load_factor_cutoff_length && + (float)_occupied / _length > load_factor) { + grow_and_rehash(); + } + while (true) { + DEBUG_ONLY(counter++); + assert(counter < 8, "Infinite loop?"); + int i = hash_to_bucket(kv.mem_tag); + while (i < _length && _members[i].marker == Marker::Occupied) { + if (_members[i].mem_tag == kv.mem_tag) { + // Found previous + *found = true; + return _members[i]; + } + i++; + } + *found = false; + // We didn't find it but ran out of space, grow and rehash + // Then look at again + if (i >= _length) { + assert(_length < std::numeric_limits>::max(), ""); + grow_and_rehash(); + continue; + } + // We didn't find it, but _members[i] is empty, allocate a new one + assert(_members[i].marker == Marker::Empty, "must be"); + _members[i] = kv; + _occupied++; + return _members[i]; + } +} + +void VMATree::SummaryDiff::grow_and_rehash() { + assert(is_power_of_2(_length), "must be"); + constexpr int length_limit = std::numeric_limits>::max() + 1; + assert(is_power_of_2(length_limit), "must be"); + if (_length == length_limit) { + // If we are at MemTag's maximum size, then just continue with the current size. + return; + } + + int new_len = _length * 2; + // Save old entries (can't use ResourceMark, too early) + GrowableArrayCHeap tmp(_length); + for (int i = 0; i < _length; i++) { + tmp.push(_members[i]); + } + + // Clear previous -- if applicable + if (_members != _small) { + FREE_C_HEAP_ARRAY(KVEntry, _members); + } + + _members = NEW_C_HEAP_ARRAY(KVEntry, new_len, mtNMT); + // Clear new array + memset(_members, 0, sizeof(KVEntry) * new_len); + _length = new_len; + _occupied = 0; + + for (int i = 0; i < tmp.length(); i++) { + bool _found = false; + hash_insert_or_get(tmp.at(i), &_found); + } +} + +VMATree::SingleDiff& VMATree::SummaryDiff::tag(MemTag tag) { + KVEntry kv{Marker::Occupied, tag, {}}; + bool _found = false; + KVEntry& inserted = hash_insert_or_get(kv, &_found); + return inserted.single_diff; +} + +VMATree::SingleDiff& VMATree::SummaryDiff::tag(int mt_index) { + return tag((MemTag)mt_index); +} + +void VMATree::SummaryDiff::add(const SummaryDiff& other) { + other.visit([&](MemTag mt, const SingleDiff& single_diff) { + bool found = false; + KVEntry other_kv = {Marker::Occupied, mt, single_diff}; + KVEntry& this_kv = hash_insert_or_get(other_kv, &found); + if (found) { + this_kv.single_diff.reserve += other_kv.single_diff.reserve; + this_kv.single_diff.commit += other_kv.single_diff.commit; + } + }); +} + +void VMATree::SummaryDiff::clear() { + if (_members != _small) { + FREE_C_HEAP_ARRAY(KVEntry, _members); + } + memset(_small, 0, sizeof(_small)); +} + +uint32_t VMATree::SummaryDiff::hash_to_bucket(MemTag mt) { + uint32_t hash = primitive_hash((uint32_t)mt); + assert(is_power_of_2(_length), "must be"); + return hash & ((uint32_t)_length - 1); +} diff --git a/src/hotspot/share/nmt/vmatree.hpp b/src/hotspot/share/nmt/vmatree.hpp index f7ca8f4c7e0..ae732a4b0d3 100644 --- a/src/hotspot/share/nmt/vmatree.hpp +++ b/src/hotspot/share/nmt/vmatree.hpp @@ -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. * Copyright (c) 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,6 +26,7 @@ #ifndef SHARE_NMT_VMATREE_HPP #define SHARE_NMT_VMATREE_HPP +#include "memory/resourceArea.hpp" #include "nmt/memTag.hpp" #include "nmt/nmtNativeCallStackStorage.hpp" #include "utilities/globalDefinitions.hpp" @@ -238,24 +239,57 @@ public: delta commit; }; - struct SummaryDiff { - SingleDiff tag[mt_number_of_tags]; - SummaryDiff() { + class SummaryDiff { + enum class Marker { Empty, Occupied }; + static_assert((int)Marker::Empty == 0, "We memset the array to 0, so this must be true"); + + struct KVEntry { + Marker marker; + MemTag mem_tag; + SingleDiff single_diff; + }; + + static constexpr const int _init_size = 4; + KVEntry _small[_init_size]; + KVEntry* _members; + int _length; + int _occupied; + KVEntry& hash_insert_or_get(const KVEntry& kv, bool* found); + void grow_and_rehash(); + uint32_t hash_to_bucket(MemTag mt); + + public: + SummaryDiff() : _small(), _members(_small), _length(_init_size), _occupied(0) { clear(); } - void clear() { - for (int i = 0; i < mt_number_of_tags; i++) { - tag[i] = SingleDiff{0, 0}; + ~SummaryDiff() { + if (_members != _small) { + FREE_C_HEAP_ARRAY(KVEntry, _members); } } - void add(SummaryDiff& other) { - for (int i = 0; i < mt_number_of_tags; i++) { - tag[i].reserve += other.tag[i].reserve; - tag[i].commit += other.tag[i].commit; + SingleDiff& tag(MemTag tag); + SingleDiff& tag(int mt_index); + + template + void visit(F f) const { + int hits = 0; + for (int i = 0; i < _length; i++) { + const KVEntry& kv = _members[i]; + if (kv.marker == Marker::Occupied) { + f(kv.mem_tag, kv.single_diff); + hits++; + } + if (hits == _occupied) { + // Early exit + return; + } } } + void add(const SummaryDiff& other); + void clear(); + #ifdef ASSERT void print_on(outputStream* out); #endif @@ -313,7 +347,7 @@ public: // partially contained within that interval and set their tag to the one provided. // This may cause merging and splitting of ranges. // Released regions are ignored. - SummaryDiff set_tag(position from, size size, MemTag tag); + void set_tag(position from, size size, MemTag tag, SummaryDiff& diff); void uncommit_mapping(position from, size size, const RegionData& metadata, SummaryDiff& diff) { register_mapping(from, from + size, StateType::Reserved, metadata, diff, true); diff --git a/test/hotspot/gtest/nmt/test_nmt_summarydiff.cpp b/test/hotspot/gtest/nmt/test_nmt_summarydiff.cpp new file mode 100644 index 00000000000..fcdbe57bfe1 --- /dev/null +++ b/test/hotspot/gtest/nmt/test_nmt_summarydiff.cpp @@ -0,0 +1,42 @@ +/* + * 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. + * + */ + +#include "nmt/memTag.hpp" +#include "nmt/vmatree.hpp" +#include "unittest.hpp" + + +// The SummaryDiff is seldom used with a large number of tags +// so test that separately. +TEST(NMTSummaryDiffTest, WorksForLargeTagCount) { + VMATree::SummaryDiff d; + for (int i = 0; i < std::numeric_limits>::max(); i++) { + VMATree::SingleDiff& sd = d.tag(i); + sd.reserve = i; + } + for (int i = 0; i < std::numeric_limits>::max(); i++) { + VMATree::SingleDiff& sd = d.tag(i); + EXPECT_EQ(i, sd.reserve); + } +} diff --git a/test/hotspot/gtest/nmt/test_regions_tree.cpp b/test/hotspot/gtest/nmt/test_regions_tree.cpp index a17a3fbb945..5d50a797a80 100644 --- a/test/hotspot/gtest/nmt/test_regions_tree.cpp +++ b/test/hotspot/gtest/nmt/test_regions_tree.cpp @@ -44,30 +44,30 @@ TEST_VM_F(NMTRegionsTreeTest, ReserveCommitTwice) { { VMATree::SummaryDiff diff; rt.reserve_mapping(0, 100, rd, diff); - EXPECT_EQ(100, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); + EXPECT_EQ(100, diff.tag(mtTest).reserve); } { VMATree::SummaryDiff diff, not_used; rt.commit_region(nullptr, 50, ncs, not_used); rt.reserve_mapping(0, 100, rd, diff); - EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(-50, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff.tag(mtTest).reserve); + EXPECT_EQ(-50, diff.tag(mtTest).commit); } { VMATree::SummaryDiff diff; rt.reserve_mapping(0, 100, rd2, diff); - EXPECT_EQ(-100, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(100, diff.tag[NMTUtil::tag_to_index(mtGC)].reserve); + EXPECT_EQ(-100, diff.tag(mtTest).reserve); + EXPECT_EQ(100, diff.tag(mtGC).reserve); } { VMATree::SummaryDiff diff1, diff2; rt.commit_region(nullptr, 50, ncs, diff1); - EXPECT_EQ(0, diff1.tag[NMTUtil::tag_to_index(mtGC)].reserve); - EXPECT_EQ(50, diff1.tag[NMTUtil::tag_to_index(mtGC)].commit); + EXPECT_EQ(0, diff1.tag(mtGC).reserve); + EXPECT_EQ(50, diff1.tag(mtGC).commit); rt.commit_region(nullptr, 50, ncs, diff2); - EXPECT_EQ(0, diff2.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(0, diff2.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff2.tag(mtTest).reserve); + EXPECT_EQ(0, diff2.tag(mtTest).commit); } } @@ -79,20 +79,20 @@ TEST_VM_F(NMTRegionsTreeTest, CommitUncommitRegion) { { VMATree::SummaryDiff diff; rt.commit_region(nullptr, 50, ncs, diff); - EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(50, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff.tag(mtTest).reserve); + EXPECT_EQ(50, diff.tag(mtTest).commit); } { VMATree::SummaryDiff diff; rt.commit_region((address)60, 10, ncs, diff); - EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(10, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff.tag(mtTest).reserve); + EXPECT_EQ(10, diff.tag(mtTest).commit); } { VMATree::SummaryDiff diff; rt.uncommit_region(nullptr, 50, diff); - EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(-50, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff.tag(mtTest).reserve); + EXPECT_EQ(-50, diff.tag(mtTest).commit); } } diff --git a/test/hotspot/gtest/nmt/test_vmatree.cpp b/test/hotspot/gtest/nmt/test_vmatree.cpp index eed2e5af0be..67cdc080cd6 100644 --- a/test/hotspot/gtest/nmt/test_vmatree.cpp +++ b/test/hotspot/gtest/nmt/test_vmatree.cpp @@ -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 @@ -221,13 +221,13 @@ public: EXPECT_EQ(n1.val().out.committed_stack(), upd.new_st.committed_stack()) << failed_case; if (from == to) { - EXPECT_EQ(diff.tag[from].reserve, upd.reserve[0] + upd.reserve[1]) << failed_case; - EXPECT_EQ(diff.tag[from].commit, upd.commit[0] + upd.commit[1]) << failed_case; + EXPECT_EQ(diff.tag(from).reserve, upd.reserve[0] + upd.reserve[1]) << failed_case; + EXPECT_EQ(diff.tag(from).commit, upd.commit[0] + upd.commit[1]) << failed_case; } else { - EXPECT_EQ(diff.tag[from].reserve, upd.reserve[0]) << failed_case; - EXPECT_EQ(diff.tag[from].commit, upd.commit[0]) << failed_case; - EXPECT_EQ(diff.tag[to].reserve, upd.reserve[1]) << failed_case; - EXPECT_EQ(diff.tag[to].commit, upd.commit[1]) << failed_case; + EXPECT_EQ(diff.tag(from).reserve, upd.reserve[0]) << failed_case; + EXPECT_EQ(diff.tag(from).commit, upd.commit[0]) << failed_case; + EXPECT_EQ(diff.tag(to).reserve, upd.reserve[1]) << failed_case; + EXPECT_EQ(diff.tag(to).commit, upd.commit[1]) << failed_case; } } @@ -235,6 +235,7 @@ public: void create_tree(Tree& tree, ExpectedTree& et, int line_no) { using SIndex = NativeCallStackStorage::StackIndex; const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack + VMATree::SummaryDiff not_used; VMATree::IntervalChange st; for (int i = 0; i < N; i++) { st.in.set_type(et.states[i]); @@ -537,8 +538,8 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { tree.reserve_mapping(0, 600, rd, not_used); - tree.set_tag(0, 500, mtGC); - tree.set_tag(500, 100, mtClassShared); + tree.set_tag(0, 500, mtGC, not_used); + tree.set_tag(500, 100, mtClassShared, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -569,8 +570,8 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { tree.commit_mapping(550, 10, rd, not_used); tree.commit_mapping(565, 10, rd, not_used); // OK, set tag - tree.set_tag(0, 500, mtGC); - tree.set_tag(500, 100, mtClassShared); + tree.set_tag(0, 500, mtGC, not_used); + tree.set_tag(500, 100, mtClassShared, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -584,7 +585,7 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { Tree::RegionData compiler(si, mtCompiler); tree.reserve_mapping(0, 100, gc, not_used); tree.reserve_mapping(100, 100, compiler, not_used); - tree.set_tag(0, 200, mtGC); + tree.set_tag(0, 200, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -601,7 +602,7 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { Tree::RegionData compiler(si2, mtCompiler); tree.reserve_mapping(0, 100, gc, not_used); tree.reserve_mapping(100, 100, compiler, not_used); - tree.set_tag(0, 200, mtGC); + tree.set_tag(0, 200, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -615,7 +616,7 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { VMATree::SummaryDiff not_used; Tree::RegionData compiler(si, mtCompiler); tree.reserve_mapping(0, 200, compiler, not_used); - tree.set_tag(100, 50, mtGC); + tree.set_tag(100, 50, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -631,7 +632,7 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { Tree::RegionData compiler(si, mtCompiler); tree.reserve_mapping(0, 100, gc, not_used); tree.reserve_mapping(100, 100, compiler, not_used); - tree.set_tag(75, 50, mtClass); + tree.set_tag(75, 50, mtClass, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -647,7 +648,7 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { Tree::RegionData class_shared(si, mtClassShared); tree.reserve_mapping(0, 50, class_shared, not_used); tree.reserve_mapping(75, 25, class_shared, not_used); - tree.set_tag(0, 80, mtGC); + tree.set_tag(0, 80, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -659,7 +660,7 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { VMATree::SummaryDiff not_used; Tree::RegionData class_shared(si, mtClassShared); tree.reserve_mapping(10, 10, class_shared, not_used); - tree.set_tag(0, 100, mtCompiler); + tree.set_tag(0, 100, mtCompiler, not_used); expect_equivalent_form(expected, tree, __LINE__); } @@ -677,7 +678,7 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { tree.reserve_mapping(0, 100, class_shared, not_used); tree.release_mapping(1, 49, not_used); tree.release_mapping(75, 24, not_used); - tree.set_tag(0, 100, mtGC); + tree.set_tag(0, 100, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } } @@ -696,7 +697,7 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // A - Test (reserved) // . - free - VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(100, diff.reserve); tree.reserve_mapping(50, 25, rd_NMT_cs0, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 @@ -707,8 +708,8 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // B - Native Memory Tracking (reserved) // C - Test (reserved) // . - free - diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; - VMATree::SingleDiff diff2 = all_diff.tag[NMTUtil::tag_to_index(mtNMT)]; + diff = all_diff.tag(mtTest); + VMATree::SingleDiff diff2 = all_diff.tag(mtNMT); EXPECT_EQ(-25, diff.reserve); EXPECT_EQ(25, diff2.reserve); } @@ -723,14 +724,14 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // A - Test (reserved) // . - free - VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(100, diff.reserve); tree.release_mapping(0, 100, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 // .............................................................................................................. // Legend: - diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + diff = all_diff.tag(mtTest); EXPECT_EQ(-100, diff.reserve); } { // Convert some of a released mapping to a committed one @@ -744,7 +745,7 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // A - Test (reserved) // . - free - VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(diff.reserve, 100); tree.commit_mapping(0, 100, rd_Test_cs0, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 @@ -753,7 +754,7 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // a - Test (committed) // . - free - diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + diff = all_diff.tag(mtTest); EXPECT_EQ(0, diff.reserve); EXPECT_EQ(100, diff.commit); } @@ -768,7 +769,7 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // A - Test (reserved) // . - free - VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(diff.reserve, 10); tree.reserve_mapping(10, 10, rd_Test_cs0, all_diff); // 1 2 3 @@ -777,7 +778,7 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // A - Test (reserved) // . - free - diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + diff = all_diff.tag(mtTest); EXPECT_EQ(10, diff.reserve); } { // Adjacent reserved mappings with different tags @@ -792,7 +793,7 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // A - Test (reserved) // . - free - VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(diff.reserve, 10); tree.reserve_mapping(10, 10, rd_NMT_cs0, all_diff); // 1 2 3 @@ -802,9 +803,9 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // A - Test (reserved) // B - Native Memory Tracking (reserved) // . - free - diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + diff = all_diff.tag(mtTest); EXPECT_EQ(0, diff.reserve); - diff = all_diff.tag[NMTUtil::tag_to_index(mtNMT)]; + diff = all_diff.tag(mtNMT); EXPECT_EQ(10, diff.reserve); } @@ -834,8 +835,8 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { // Legend: // a - Test (committed) // . - free - EXPECT_EQ(16, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); - EXPECT_EQ(16, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); + EXPECT_EQ(16, diff.tag(mtTest).commit); + EXPECT_EQ(16, diff.tag(mtTest).reserve); } } @@ -845,16 +846,16 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccountingReserveAsUncommit) { VMATree::SummaryDiff diff1, diff2, diff3; tree.reserve_mapping(1200, 100, rd, diff1); tree.commit_mapping(1210, 50, rd, diff2); - EXPECT_EQ(100, diff1.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(50, diff2.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(100, diff1.tag(mtTest).reserve); + EXPECT_EQ(50, diff2.tag(mtTest).commit); tree.reserve_mapping(1220, 20, rd, diff3); - EXPECT_EQ(-20, diff3.tag[NMTUtil::tag_to_index(mtTest)].commit); - EXPECT_EQ(0, diff3.tag[NMTUtil::tag_to_index(mtTest)].reserve); + EXPECT_EQ(-20, diff3.tag(mtTest).commit); + EXPECT_EQ(0, diff3.tag(mtTest).reserve); } // Exceedingly simple tracker for page-granular allocations // Use it for testing consistency with VMATree. - struct SimpleVMATracker : public CHeapObj { +struct SimpleVMATracker : public CHeapObj { const size_t page_size = 4096; enum Kind { Reserved, Committed, Free }; struct Info { @@ -881,10 +882,9 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccountingReserveAsUncommit) { } } - VMATree::SummaryDiff do_it(Kind kind, size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) { + void do_it(Kind kind, size_t start, size_t size, NativeCallStack stack, MemTag mem_tag, VMATree::SummaryDiff& diff) { assert(is_aligned(size, page_size) && is_aligned(start, page_size), "page alignment"); - VMATree::SummaryDiff diff; const size_t page_count = size / page_size; const size_t start_idx = start / page_size; const size_t end_idx = start_idx + page_count; @@ -896,34 +896,33 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccountingReserveAsUncommit) { // Register diff if (old_info.kind == Reserved) { - diff.tag[(int)old_info.mem_tag].reserve -= page_size; + diff.tag(old_info.mem_tag).reserve -= page_size; } else if (old_info.kind == Committed) { - diff.tag[(int)old_info.mem_tag].reserve -= page_size; - diff.tag[(int)old_info.mem_tag].commit -= page_size; + diff.tag(old_info.mem_tag).reserve -= page_size; + diff.tag(old_info.mem_tag).commit -= page_size; } if (kind == Reserved) { - diff.tag[(int)new_info.mem_tag].reserve += page_size; + diff.tag(new_info.mem_tag).reserve += page_size; } else if (kind == Committed) { - diff.tag[(int)new_info.mem_tag].reserve += page_size; - diff.tag[(int)new_info.mem_tag].commit += page_size; + diff.tag(new_info.mem_tag).reserve += page_size; + diff.tag(new_info.mem_tag).commit += page_size; } // Overwrite old one with new pages[i] = new_info; } - return diff; } - VMATree::SummaryDiff reserve(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) { - return do_it(Reserved, start, size, stack, mem_tag); + void reserve(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag, VMATree::SummaryDiff& diff) { + return do_it(Reserved, start, size, stack, mem_tag, diff); } - VMATree::SummaryDiff commit(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) { - return do_it(Committed, start, size, stack, mem_tag); + void commit(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag, VMATree::SummaryDiff& diff) { + return do_it(Committed, start, size, stack, mem_tag, diff); } - VMATree::SummaryDiff release(size_t start, size_t size) { - return do_it(Free, start, size, NativeCallStack(), mtNone); + void release(size_t start, size_t size, VMATree::SummaryDiff& diff) { + return do_it(Free, start, size, NativeCallStack(), mtNone, diff); } }; @@ -979,19 +978,19 @@ TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { VMATree::SummaryDiff tree_diff; VMATree::SummaryDiff simple_diff; if (kind == SimpleVMATracker::Reserved) { - simple_diff = tr->reserve(start, size, stack, mem_tag); + tr->reserve(start, size, stack, mem_tag, simple_diff); tree.reserve_mapping(start, size, data, tree_diff); } else if (kind == SimpleVMATracker::Committed) { - simple_diff = tr->commit(start, size, stack, mem_tag); + tr->commit(start, size, stack, mem_tag, simple_diff); tree.commit_mapping(start, size, data, tree_diff); } else { - simple_diff = tr->release(start, size); + tr->release(start, size, simple_diff); tree.release_mapping(start, size, tree_diff); } for (int j = 0; j < mt_number_of_tags; j++) { - VMATree::SingleDiff td = tree_diff.tag[j]; - VMATree::SingleDiff sd = simple_diff.tag[j]; + VMATree::SingleDiff td = tree_diff.tag(j); + VMATree::SingleDiff sd = simple_diff.tag(j); ASSERT_EQ(td.reserve, sd.reserve); ASSERT_EQ(td.commit, sd.commit); } @@ -1067,22 +1066,22 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccountingWhenUseTagInplace) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCCCCCCCrrrrrrrrrrrrrrrrrrrrrrrrr - EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(25, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff.tag(mtTest).reserve); + EXPECT_EQ(25, diff.tag(mtTest).commit); tree.commit_mapping(30, 5, rd_None_cs1, diff, true); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCCCCCCCrrrrrCCCCCrrrrrrrrrrrrrrr - EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(5, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff.tag(mtTest).reserve); + EXPECT_EQ(5, diff.tag(mtTest).commit); tree.uncommit_mapping(0, 25, rd_None_cs1, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrCCCCCrrrrrrrrrrrrrrr - EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); - EXPECT_EQ(-25, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(0, diff.tag(mtTest).reserve); + EXPECT_EQ(-25, diff.tag(mtTest).commit); } // How the memory regions are visualized: @@ -1328,8 +1327,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows0To3) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.......................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 10); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 25 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, @@ -1356,8 +1355,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows0To3) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCC............................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 5); + EXPECT_EQ(diff.tag(mtTest).commit, 15); + EXPECT_EQ(diff.tag(mtTest).reserve, 5); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, @@ -1402,8 +1401,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows4to7) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr..........CCCCCCCCCCCCCCCCCCCC........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 20); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 20); ExpectedTree<4> et = {{ 0, 10, 20, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , Rl }, @@ -1430,8 +1429,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows4to7) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....rrrrrCCCCCCCCCC............................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 10); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 20 - 15); + EXPECT_EQ(diff.tag(mtTest).commit, 10); + EXPECT_EQ(diff.tag(mtTest).reserve, 20 - 15); ExpectedTree<4> et = {{ 5, 10, 15, 20 }, {mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , C , Rl }, @@ -1458,8 +1457,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows4to7) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCCCCCCCCC........................ - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 10); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<8> et = {{ 0, 5, 7, 10, 12, 14, 16, 27 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl }, @@ -1486,8 +1485,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows4to7) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCC............................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 13); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 3); + EXPECT_EQ(diff.tag(mtTest).commit, 13); + EXPECT_EQ(diff.tag(mtTest).reserve, 3); ExpectedTree<8> et = {{ 0, 5, 7, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl }, @@ -1539,8 +1538,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows8to11) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrCCCCCCCCCCCCCCCCCCCC..................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 20); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 20); ExpectedTree<3> et = {{ 0, 10, 30 }, {mtNone, mtTest, mtTest, mtNone}, {Rl , Rs , C , Rl }, @@ -1567,8 +1566,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows8to11) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCC............................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 10); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<3> et = {{ 0, 10, 20 }, {mtNone, mtTest, mtTest, mtNone}, {Rl , C , C , Rl }, @@ -1595,8 +1594,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows8to11) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.......................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 25 - 20); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 25 - 20); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 25 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, @@ -1623,8 +1622,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows8to11) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCC............................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 0); + EXPECT_EQ(diff.tag(mtTest).commit, 15); + EXPECT_EQ(diff.tag(mtTest).reserve, 0); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, @@ -1670,8 +1669,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows12to15) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 20); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 20); ExpectedTree<4> et = {{ 5, 25, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , C , Rl , Rs , Rl }, @@ -1698,8 +1697,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows12to15) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCCrrrrr..................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 30 - 25); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, 30 - 25); ExpectedTree<4> et = {{ 5, 10, 25, 30 }, {mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , Rs , Rl }, @@ -1726,8 +1725,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows12to15) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, (10 - 5) + ( 25 - 20)); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, (10 - 5) + ( 25 - 20)); ExpectedTree<8> et = {{ 5, 10, 12, 14, 16, 25, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl , Rs , Rl }, @@ -1754,8 +1753,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows12to15) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCC..........rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 10 - 5); + EXPECT_EQ(diff.tag(mtTest).commit, 15); + EXPECT_EQ(diff.tag(mtTest).reserve, 10 - 5); ExpectedTree<8> et = {{ 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl , Rs , Rl }, @@ -1800,8 +1799,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows16to19) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr.....CCCCCCCCCC.....rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 10); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 10); + EXPECT_EQ(diff.tag(mtTest).commit, 10); + EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<6> et = {{ 0, 10, 15, 25, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , Rl , Rs , Rl }, @@ -1828,8 +1827,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows16to19) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr.....CCCCCCCCCCrrrrr..................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 10); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 20 - 15); + EXPECT_EQ(diff.tag(mtTest).commit, 10); + EXPECT_EQ(diff.tag(mtTest).reserve, 20 - 15); ExpectedTree<6> et = {{ 0, 10, 15, 20, 25, 30 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , C , C , Rs , Rl }, @@ -1856,8 +1855,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows16to19) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCCCCCCCCC...rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, (10 - 7) + (27 - 20)); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, (10 - 7) + (27 - 20)); ExpectedTree<10> et = {{ 0, 5, 7, 12, 14, 16, 20, 27, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl , Rs , Rl }, @@ -1884,8 +1883,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows16to19) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCC..........rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 13); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 10 - 7); + EXPECT_EQ(diff.tag(mtTest).commit, 13); + EXPECT_EQ(diff.tag(mtTest).reserve, 10 - 7); ExpectedTree<10> et = {{ 0, 5, 7, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl , Rs , Rl }, @@ -1931,8 +1930,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows20to23) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrCCCCCCCCCCCCCCC.....rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 15); + EXPECT_EQ(diff.tag(mtTest).commit, 15); + EXPECT_EQ(diff.tag(mtTest).reserve, 15); ExpectedTree<5> et = {{ 0, 10, 25, 30, 40 }, {mtNone, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , C , Rl , Rs , Rl }, @@ -1959,8 +1958,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows20to23) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrCCCCCCCCCCCCCCCrrrrr..................... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 20 - 10); + EXPECT_EQ(diff.tag(mtTest).commit, 15); + EXPECT_EQ(diff.tag(mtTest).reserve, 20 - 10); ExpectedTree<5> et = {{ 0, 10, 20, 25, 30 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , C , Rs , Rl }, @@ -1987,8 +1986,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows20to23) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrCCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, (10 - 5) + (25 - 20)); + EXPECT_EQ(diff.tag(mtTest).commit, 20); + EXPECT_EQ(diff.tag(mtTest).reserve, (10 - 5) + (25 - 20)); ExpectedTree<9> et = {{ 0, 5, 12, 14, 16, 20, 25, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , C , C , C , C , C , Rl , Rs , Rl }, @@ -2015,8 +2014,8 @@ TEST_VM_F(NMTVMATreeTest, OverlapTableRows20to23) { // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrCCCCCCCCCCCCCCC..........rrrrrrrrrr........... - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15); - EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].reserve, 10 - 5); + EXPECT_EQ(diff.tag(mtTest).commit, 15); + EXPECT_EQ(diff.tag(mtTest).reserve, 10 - 5); ExpectedTree<9> et = {{ 0, 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , C , C , C , C , C , Rl , Rs , Rl }, @@ -2070,4 +2069,4 @@ TEST_VM_F(NMTVMATreeTest, UpdateRegionTest) { for (auto ci : call_info) { call_update_region(ci); } -} \ No newline at end of file +} From 5a59ed68f973b74a5bc19bf6babd4c20ecc39b04 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 25 Feb 2026 15:46:25 +0000 Subject: [PATCH 041/636] 8376234: Migrate java/lang/constant tests away from TestNG Reviewed-by: rriggs, alanb --- .../jdk/java/lang/constant/ClassDescTest.java | 212 ++++++------------ .../jdk/java/lang/constant/CondyDescTest.java | 49 ++-- .../java/lang/constant/ConstantDescsTest.java | 20 +- .../constant => }/ConstantUtilsTest.java | 44 ++-- .../lang/constant/DescribeResolveTest.java | 15 +- .../constant/DynamicCallSiteDescTest.java | 150 ++++--------- test/jdk/java/lang/constant/IndyDescTest.java | 22 +- .../lang/constant/MethodHandleDescTest.java | 176 ++++++--------- .../lang/constant/MethodTypeDescTest.java | 114 +++++----- .../lang/constant/NameValidationTest.java | 43 ++-- .../java/lang/constant/SymbolicDescTest.java | 6 +- .../lang/constant/TypeDescriptorTest.java | 27 ++- .../lang/constant/boottest/TEST.properties | 4 - .../jdk/internal/constant/ConstantAccess.java | 30 +++ 14 files changed, 367 insertions(+), 545 deletions(-) rename test/jdk/java/lang/constant/{boottest/java.base/jdk/internal/constant => }/ConstantUtilsTest.java (65%) delete mode 100644 test/jdk/java/lang/constant/boottest/TEST.properties create mode 100644 test/jdk/java/lang/constant/java.base/jdk/internal/constant/ConstantAccess.java diff --git a/test/jdk/java/lang/constant/ClassDescTest.java b/test/jdk/java/lang/constant/ClassDescTest.java index ee76d27e8d0..7551edff11a 100644 --- a/test/jdk/java/lang/constant/ClassDescTest.java +++ b/test/jdk/java/lang/constant/ClassDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -31,23 +31,17 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.*; /* * @test * @bug 8215510 8283075 8338544 * @compile ClassDescTest.java - * @run testng ClassDescTest + * @run junit ClassDescTest * @summary unit tests for java.lang.constant.ClassDesc */ -@Test public class ClassDescTest extends SymbolicDescTest { private void testClassDesc(ClassDesc r) throws ReflectiveOperationException { @@ -73,7 +67,7 @@ public class ClassDescTest extends SymbolicDescTest { } if (!r.isClassOrInterface()) { - assertEquals(r.packageName(), ""); + assertEquals("", r.packageName()); } } @@ -97,18 +91,19 @@ public class ClassDescTest extends SymbolicDescTest { private void testClassDesc(ClassDesc r, Class c) throws ReflectiveOperationException { testClassDesc(r); - assertEquals(r.resolveConstantDesc(LOOKUP), c); - assertEquals(c.describeConstable().orElseThrow(), r); - assertEquals(ClassDesc.ofDescriptor(c.descriptorString()), r); + assertEquals(c, r.resolveConstantDesc(LOOKUP)); + assertEquals(r, c.describeConstable().orElseThrow()); + assertEquals(r, ClassDesc.ofDescriptor(c.descriptorString())); if (r.isArray()) { testClassDesc(r.componentType(), c.componentType()); } if (r.isClassOrInterface()) { - assertEquals(r.packageName(), c.getPackageName()); + assertEquals(c.getPackageName(), r.packageName()); } - assertEquals(r.displayName(), classDisplayName(c)); + assertEquals(classDisplayName(c), r.displayName()); } + @Test public void testSymbolicDescsConstants() throws ReflectiveOperationException { int tested = 0; Field[] fields = ConstantDescs.class.getDeclaredFields(); @@ -132,6 +127,7 @@ public class ClassDescTest extends SymbolicDescTest { assertTrue(tested > 0); } + @Test public void testPrimitiveClassDesc() throws ReflectiveOperationException { for (Primitives p : Primitives.values()) { List descs = List.of(ClassDesc.ofDescriptor(p.descriptor), @@ -140,26 +136,27 @@ public class ClassDescTest extends SymbolicDescTest { for (ClassDesc c : descs) { testClassDesc(c, p.clazz); assertTrue(c.isPrimitive()); - assertEquals(p.descriptor, c.descriptorString()); - assertEquals(p.name, c.displayName()); - descs.forEach(cc -> assertEquals(c, cc)); + assertEquals(c.descriptorString(), p.descriptor); + assertEquals(c.displayName(), p.name); + descs.forEach(cc -> assertEquals(cc, c)); if (p != Primitives.VOID) { testClassDesc(c.arrayType(), p.arrayClass); - assertEquals(c, p.arrayClass.describeConstable().orElseThrow().componentType()); - assertEquals(c, p.classDesc.arrayType().componentType()); + assertEquals(p.arrayClass.describeConstable().orElseThrow().componentType(), c); + assertEquals(p.classDesc.arrayType().componentType(), c); } } for (Primitives other : Primitives.values()) { ClassDesc otherDescr = ClassDesc.ofDescriptor(other.descriptor); if (p != other) - descs.forEach(c -> assertNotEquals(c, otherDescr)); + descs.forEach(c -> assertNotEquals(otherDescr, c)); else - descs.forEach(c -> assertEquals(c, otherDescr)); + descs.forEach(c -> assertEquals(otherDescr, c)); } } } + @Test public void testSimpleClassDesc() throws ReflectiveOperationException { List stringClassDescs = Arrays.asList(ClassDesc.ofDescriptor("Ljava/lang/String;"), @@ -175,22 +172,23 @@ public class ClassDescTest extends SymbolicDescTest { assertEquals("String", r.displayName()); testClassDesc(r.arrayType(), String[].class); testClassDesc(r.arrayType(3), String[][][].class); - stringClassDescs.forEach(rr -> assertEquals(r, rr)); + stringClassDescs.forEach(rr -> assertEquals(rr, r)); } testClassDesc(ClassDesc.of("java.lang.String").arrayType(), String[].class); testClassDesc(ClassDesc.of("java.util.Map").nested("Entry"), Map.Entry.class); - assertEquals(ClassDesc.of("java.lang.String"), ClassDesc.ofDescriptor("Ljava/lang/String;")); - assertEquals(ClassDesc.of("java.lang.String"), ClassDesc.ofInternalName("java/lang/String")); + assertEquals(ClassDesc.ofDescriptor("Ljava/lang/String;"), ClassDesc.of("java.lang.String")); + assertEquals(ClassDesc.ofInternalName("java/lang/String"), ClassDesc.of("java.lang.String")); ClassDesc thisClassDesc = ClassDesc.ofDescriptor("LClassDescTest;"); - assertEquals(thisClassDesc, ClassDesc.of("", "ClassDescTest")); - assertEquals(thisClassDesc, ClassDesc.of("ClassDescTest")); - assertEquals(thisClassDesc.displayName(), "ClassDescTest"); + assertEquals(ClassDesc.of("", "ClassDescTest"), thisClassDesc); + assertEquals(ClassDesc.of("ClassDescTest"), thisClassDesc); + assertEquals("ClassDescTest", thisClassDesc.displayName()); testClassDesc(thisClassDesc, ClassDescTest.class); } + @Test public void testPackageName() { assertEquals("com.foo", ClassDesc.of("com.foo.Bar").packageName()); assertEquals("com.foo", ClassDesc.of("com.foo.Bar").nested("Baz").packageName()); @@ -205,33 +203,19 @@ public class ClassDescTest extends SymbolicDescTest { } private void testBadArrayRank(ClassDesc cr) { - try { - cr.arrayType(-1); - fail(""); - } catch (IllegalArgumentException e) { - // good - } - try { - cr.arrayType(0); - fail(""); - } catch (IllegalArgumentException e) { - // good - } + assertThrows(IllegalArgumentException.class, () -> cr.arrayType(-1)); + assertThrows(IllegalArgumentException.class, () -> cr.arrayType(0)); } private void testArrayRankOverflow() { ClassDesc TwoDArrayDesc = String.class.describeConstable().get().arrayType().arrayType(); - try { - TwoDArrayDesc.arrayType(Integer.MAX_VALUE); - fail(""); - } catch (IllegalArgumentException iae) { - // Expected - } + assertThrows(IllegalArgumentException.class, () -> TwoDArrayDesc.arrayType(Integer.MAX_VALUE)); } + @Test public void testArrayClassDesc() throws ReflectiveOperationException { for (String d : basicDescs) { ClassDesc a0 = ClassDesc.ofDescriptor(d); @@ -246,36 +230,32 @@ public class ClassDescTest extends SymbolicDescTest { assertTrue(a2.isArray()); assertFalse(a1.isPrimitive()); assertFalse(a2.isPrimitive()); - assertEquals(a0.descriptorString(), d); - assertEquals(a1.descriptorString(), "[" + a0.descriptorString()); - assertEquals(a2.descriptorString(), "[[" + a0.descriptorString()); + assertEquals(d, a0.descriptorString()); + assertEquals("[" + a0.descriptorString(), a1.descriptorString()); + assertEquals("[[" + a0.descriptorString(), a2.descriptorString()); assertNull(a0.componentType()); - assertEquals(a0, a1.componentType()); - assertEquals(a1, a2.componentType()); + assertEquals(a1.componentType(), a0); + assertEquals(a2.componentType(), a1); - assertNotEquals(a0, a1); - assertNotEquals(a1, a2); + assertNotEquals(a1, a0); + assertNotEquals(a2, a1); - assertEquals(a1, ClassDesc.ofDescriptor("[" + d)); - assertEquals(a2, ClassDesc.ofDescriptor("[[" + d)); - assertEquals(classToDescriptor(a0.resolveConstantDesc(LOOKUP)), a0.descriptorString()); - assertEquals(classToDescriptor(a1.resolveConstantDesc(LOOKUP)), a1.descriptorString()); - assertEquals(classToDescriptor(a2.resolveConstantDesc(LOOKUP)), a2.descriptorString()); + assertEquals(ClassDesc.ofDescriptor("[" + d), a1); + assertEquals(ClassDesc.ofDescriptor("[[" + d), a2); + assertEquals(a0.descriptorString(), classToDescriptor(a0.resolveConstantDesc(LOOKUP))); + assertEquals(a1.descriptorString(), classToDescriptor(a1.resolveConstantDesc(LOOKUP))); + assertEquals(a2.descriptorString(), classToDescriptor(a2.resolveConstantDesc(LOOKUP))); testBadArrayRank(ConstantDescs.CD_int); testBadArrayRank(ConstantDescs.CD_String); testBadArrayRank(ClassDesc.of("Bar")); testArrayRankOverflow(); } - try { - ConstantDescs.CD_void.arrayType(); - fail("Should throw IAE"); - } catch (IllegalArgumentException iae) { - // Expected - } + assertThrows(IllegalArgumentException.class, () -> ConstantDescs.CD_void.arrayType()); } + @Test public void testBadClassDescs() { List badDescriptors = List.of("II", "I;", "Q", "L", "", "java.lang.String", "[]", "Ljava/lang/String", @@ -283,35 +263,19 @@ public class ClassDescTest extends SymbolicDescTest { "La//b;", "L/a;", "La/;"); for (String d : badDescriptors) { - try { - ClassDesc constant = ClassDesc.ofDescriptor(d); - fail(d); - } - catch (IllegalArgumentException e) { - // good - } + assertThrows(IllegalArgumentException.class, () -> ClassDesc.ofDescriptor(d), d); } List badBinaryNames = List.of("I;", "[]", "Ljava/lang/String", "Ljava.lang.String;", "java/lang/String", ""); for (String d : badBinaryNames) { - try { - ClassDesc constant = ClassDesc.of(d); - fail(d); - } catch (IllegalArgumentException e) { - // good - } + assertThrows(IllegalArgumentException.class, () -> ClassDesc.of(d), d); } List badInternalNames = List.of("I;", "[]", "[Ljava/lang/String;", "Ljava.lang.String;", "java.lang.String", ""); for (String d : badInternalNames) { - try { - ClassDesc constant = ClassDesc.ofInternalName(d); - fail(d); - } catch (IllegalArgumentException e) { - // good - } + assertThrows(IllegalArgumentException.class, () -> ClassDesc.ofInternalName(d), d); } for (Primitives p : Primitives.values()) { @@ -321,80 +285,48 @@ public class ClassDescTest extends SymbolicDescTest { ClassDesc stringDesc = ClassDesc.ofDescriptor("Ljava/lang/String;"); ClassDesc stringArrDesc = stringDesc.arrayType(255); - try { - ClassDesc arrGreaterThan255 = stringArrDesc.arrayType(); - fail("can't create an array type descriptor with more than 255 dimensions"); - } catch (IllegalStateException e) { - // good - } - String descWith255ArrayDims = new String(new char[255]).replace('\0', '['); - try { - ClassDesc arrGreaterThan255 = ClassDesc.ofDescriptor(descWith255ArrayDims + "[Ljava/lang/String;"); - fail("can't create an array type descriptor with more than 255 dimensions"); - } catch (IllegalArgumentException e) { - // good - } - try { - ClassDesc arrWith255Dims = ClassDesc.ofDescriptor(descWith255ArrayDims + "Ljava/lang/String;"); - arrWith255Dims.arrayType(1); - fail("can't create an array type descriptor with more than 255 dimensions"); - } catch (IllegalArgumentException e) { - // good - } + assertThrows(IllegalStateException.class, () -> stringArrDesc.arrayType(), + "can't create an array type descriptor with more than 255 dimensions"); + String descWith255ArrayDims = "[".repeat(255); + assertThrows(IllegalArgumentException.class, () -> ClassDesc.ofDescriptor(descWith255ArrayDims + "[Ljava/lang/String;"), + "can't create an array type descriptor with more than 255 dimensions"); + ClassDesc arrWith255Dims = ClassDesc.ofDescriptor(descWith255ArrayDims + "Ljava/lang/String;"); + assertThrows(IllegalArgumentException.class, () -> arrWith255Dims.arrayType(1), + "can't create an array type descriptor with more than 255 dimensions"); } private void testBadNestedClasses(ClassDesc cr, String firstNestedName, String... moreNestedNames) { - try { - cr.nested(firstNestedName, moreNestedNames); - fail(""); - } catch (IllegalStateException e) { - // good - } + assertThrows(IllegalStateException.class, () -> cr.nested(firstNestedName, moreNestedNames)); } + @Test public void testLangClasses() { Double d = 1.0; - assertEquals(d.resolveConstantDesc(LOOKUP), d); - assertEquals(d.describeConstable().get(), d); + assertEquals(d, d.resolveConstantDesc(LOOKUP)); + assertEquals(d, d.describeConstable().get()); Integer i = 1; - assertEquals(i.resolveConstantDesc(LOOKUP), i); - assertEquals(i.describeConstable().get(), i); + assertEquals(i, i.resolveConstantDesc(LOOKUP)); + assertEquals(i, i.describeConstable().get()); Float f = 1.0f; - assertEquals(f.resolveConstantDesc(LOOKUP), f); - assertEquals(f.describeConstable().get(), f); + assertEquals(f, f.resolveConstantDesc(LOOKUP)); + assertEquals(f, f.describeConstable().get()); Long l = 1L; - assertEquals(l.resolveConstantDesc(LOOKUP), l); - assertEquals(l.describeConstable().get(), l); + assertEquals(l, l.resolveConstantDesc(LOOKUP)); + assertEquals(l, l.describeConstable().get()); String s = ""; - assertEquals(s.resolveConstantDesc(LOOKUP), s); - assertEquals(s.describeConstable().get(), s); + assertEquals(s, s.resolveConstantDesc(LOOKUP)); + assertEquals(s, s.describeConstable().get()); } + @Test public void testNullNestedClasses() { ClassDesc cd = ClassDesc.of("Bar"); - try { - cd.nested(null); - fail(""); - } catch (NullPointerException e) { - // good - } - - try { - cd.nested("good", null); - fail(""); - } catch (NullPointerException e) { - // good - } - - try { - cd.nested("good", "goodToo", null); - fail(""); - } catch (NullPointerException e) { - // good - } + assertThrows(NullPointerException.class, () -> cd.nested(null)); + assertThrows(NullPointerException.class, () -> cd.nested("good", null)); + assertThrows(NullPointerException.class, () -> cd.nested("good", "goodToo", null)); } } diff --git a/test/jdk/java/lang/constant/CondyDescTest.java b/test/jdk/java/lang/constant/CondyDescTest.java index 08350f7748c..194c12527a2 100644 --- a/test/jdk/java/lang/constant/CondyDescTest.java +++ b/test/jdk/java/lang/constant/CondyDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -33,52 +33,50 @@ import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; -import org.testng.annotations.Test; - import static java.lang.constant.ConstantDescs.CD_MethodHandle; import static java.lang.constant.ConstantDescs.CD_Object; import static java.lang.constant.ConstantDescs.CD_String; import static java.lang.constant.ConstantDescs.CD_VarHandle; import static java.lang.constant.ConstantDescs.CD_int; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; /* * @test * @compile CondyDescTest.java - * @run testng CondyDescTest + * @run junit CondyDescTest * @summary unit tests for java.lang.constant.CondyDescTest */ -@Test public class CondyDescTest extends SymbolicDescTest { private final static ConstantDesc[] EMPTY_ARGS = new ConstantDesc[0]; private final static ClassDesc CD_ConstantBootstraps = ClassDesc.of("java.lang.invoke.ConstantBootstraps"); private static void testDCR(DynamicConstantDesc r, T c) throws ReflectiveOperationException { - assertEquals(r, DynamicConstantDesc.ofNamed(r.bootstrapMethod(), r.constantName(), r.constantType(), r.bootstrapArgs())); - assertEquals(r.resolveConstantDesc(LOOKUP), c); + assertEquals(DynamicConstantDesc.ofNamed(r.bootstrapMethod(), r.constantName(), r.constantType(), r.bootstrapArgs()), r); + assertEquals(c, r.resolveConstantDesc(LOOKUP)); } private void testVarHandleDesc(DynamicConstantDesc r, VarHandle vh) throws ReflectiveOperationException { testSymbolicDesc(r); - assertEquals(vh.describeConstable().orElseThrow(), r); + assertEquals(r, vh.describeConstable().orElseThrow()); } private static> void testEnumDesc(EnumDesc r, E e) throws ReflectiveOperationException { testSymbolicDesc(r); - assertEquals(r, EnumDesc.of(r.constantType(), r.constantName())); - assertEquals(r.resolveConstantDesc(LOOKUP), e); + assertEquals(EnumDesc.of(r.constantType(), r.constantName()), r); + assertEquals(e, r.resolveConstantDesc(LOOKUP)); } + @Test public void testNullConstant() throws ReflectiveOperationException { DynamicConstantDesc r = (DynamicConstantDesc) ConstantDescs.NULL; - assertEquals(r, DynamicConstantDesc.ofNamed(r.bootstrapMethod(), r.constantName(), r.constantType(), r.bootstrapArgs())); + assertEquals(DynamicConstantDesc.ofNamed(r.bootstrapMethod(), r.constantName(), r.constantType(), r.bootstrapArgs()), r); assertNull(r.resolveConstantDesc(LOOKUP)); } @@ -86,6 +84,7 @@ public class CondyDescTest extends SymbolicDescTest { return a + b; } + @Test public void testDynamicConstant() throws ReflectiveOperationException { DirectMethodHandleDesc bsmDesc = ConstantDescs.ofConstantBootstrap(ClassDesc.of("CondyDescTest"), "concatBSM", CD_String, CD_String, CD_String); @@ -93,6 +92,7 @@ public class CondyDescTest extends SymbolicDescTest { testDCR(r, "foobar"); } + @Test public void testNested() throws Throwable { DirectMethodHandleDesc invoker = ConstantDescs.ofConstantBootstrap(CD_ConstantBootstraps, "invoke", CD_Object, CD_MethodHandle, CD_Object.arrayType()); DirectMethodHandleDesc format = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, CD_String, "format", @@ -101,7 +101,7 @@ public class CondyDescTest extends SymbolicDescTest { String s = (String) invoker.resolveConstantDesc(LOOKUP) .invoke(LOOKUP, "", String.class, format.resolveConstantDesc(LOOKUP), "%s%s", "moo", "cow"); - assertEquals(s, "moocow"); + assertEquals("moocow", s); DynamicConstantDesc desc = DynamicConstantDesc.of(invoker, format, "%s%s", "moo", "cow"); testDCR(desc, "moocow"); @@ -112,6 +112,7 @@ public class CondyDescTest extends SymbolicDescTest { enum MyEnum { A, B, C } + @Test public void testEnumDesc() throws ReflectiveOperationException { ClassDesc enumClass = ClassDesc.of("CondyDescTest").nested("MyEnum"); @@ -131,6 +132,7 @@ public class CondyDescTest extends SymbolicDescTest { int f; } + @Test public void testVarHandles() throws ReflectiveOperationException { ClassDesc testClass = ClassDesc.of("CondyDescTest").nested("MyClass"); MyClass instance = new MyClass(); @@ -140,20 +142,20 @@ public class CondyDescTest extends SymbolicDescTest { VarHandle varHandle = LOOKUP.findStaticVarHandle(MyClass.class, "sf", int.class); testVarHandleDesc(vhc, varHandle); - assertEquals(varHandle.varType(), int.class); + assertEquals(int.class, varHandle.varType()); varHandle.set(8); assertEquals(8, (int) varHandle.get()); - assertEquals(MyClass.sf, 8); + assertEquals(8, MyClass.sf); // static varHandle vhc = VarHandleDesc.ofField(testClass, "f", CD_int); varHandle = LOOKUP.findVarHandle(MyClass.class, "f", int.class); testVarHandleDesc(vhc, varHandle); - assertEquals(varHandle.varType(), int.class); + assertEquals(int.class, varHandle.varType()); varHandle.set(instance, 9); assertEquals(9, (int) varHandle.get(instance)); - assertEquals(instance.f, 9); + assertEquals(9, instance.f); vhc = VarHandleDesc.ofArray(CD_int.arrayType()); varHandle = MethodHandles.arrayElementVarHandle(int[].class); @@ -215,6 +217,7 @@ public class CondyDescTest extends SymbolicDescTest { } } + @Test public void testLifting() { DynamicConstantDesc unliftedNull = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_NULL_CONSTANT, "_", CD_Object, EMPTY_ARGS); assertEquals(ConstantDescs.NULL, unliftedNull); diff --git a/test/jdk/java/lang/constant/ConstantDescsTest.java b/test/jdk/java/lang/constant/ConstantDescsTest.java index 9b0e73868b9..0d7a85f5425 100644 --- a/test/jdk/java/lang/constant/ConstantDescsTest.java +++ b/test/jdk/java/lang/constant/ConstantDescsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -21,9 +21,6 @@ * questions. */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDescs; @@ -47,18 +44,20 @@ import java.util.Set; import java.util.stream.Stream; import static java.lang.constant.ConstantDescs.*; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test * @compile ConstantDescsTest.java - * @run testng ConstantDescsTest + * @run junit ConstantDescsTest * @summary unit tests for java.lang.constant.ConstantDescs */ public class ConstantDescsTest { - @DataProvider(name = "validateFields") - public Object[][] knownFieldsData() { + public static Object[][] knownFieldsData() { return new Object[][]{ {CD_Object, Object.class}, {CD_String, String.class}, @@ -117,10 +116,11 @@ public class ConstantDescsTest { * constants. * @throws ReflectiveOperationException if the test fails */ - @Test(dataProvider = "validateFields") + @ParameterizedTest + @MethodSource("knownFieldsData") public void validateFields(ConstantDesc desc, Object value) throws ReflectiveOperationException { // Use a minimally-trusted lookup - assertEquals(desc.resolveConstantDesc(MethodHandles.publicLookup()), value); + assertEquals(value, desc.resolveConstantDesc(MethodHandles.publicLookup())); } /** diff --git a/test/jdk/java/lang/constant/boottest/java.base/jdk/internal/constant/ConstantUtilsTest.java b/test/jdk/java/lang/constant/ConstantUtilsTest.java similarity index 65% rename from test/jdk/java/lang/constant/boottest/java.base/jdk/internal/constant/ConstantUtilsTest.java rename to test/jdk/java/lang/constant/ConstantUtilsTest.java index 3afcc64bc4a..c614132c7e5 100644 --- a/test/jdk/java/lang/constant/boottest/java.base/jdk/internal/constant/ConstantUtilsTest.java +++ b/test/jdk/java/lang/constant/ConstantUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -21,55 +21,41 @@ * questions. */ -package jdk.internal.constant; - import java.lang.constant.*; import java.util.*; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; -import static org.testng.Assert.*; +import jdk.internal.constant.ConstantAccess; +import jdk.internal.constant.ConstantUtils; +import org.junit.jupiter.api.Test; -/** +/* * @test * @bug 8303930 + * @build java.base/jdk.internal.constant.* * @compile ConstantUtilsTest.java * @modules java.base/jdk.internal.constant - * @run testng ConstantUtilsTest + * @run junit ConstantUtilsTest * @summary unit tests for methods of java.lang.constant.ConstantUtils that are not covered by other unit tests */ -@Test public class ConstantUtilsTest { private static ClassDesc thisClass = ClassDesc.of("MethodHandleDescTest"); + @Test public void testValidateMemberName() { - try { - ConstantUtils.validateMemberName(null, false); - fail(""); - } catch (NullPointerException e) { - // good - } - - try { - ConstantUtils.validateMemberName("", false); - fail(""); - } catch (IllegalArgumentException e) { - // good - } + assertThrows(NullPointerException.class, () -> ConstantUtils.validateMemberName(null, false)); + assertThrows(IllegalArgumentException.class, () -> ConstantUtils.validateMemberName("", false)); List badNames = List.of(".", ";", "[", "/", "<", ">"); for (String n : badNames) { - try { - ConstantUtils.validateMemberName(n, true); - fail(n); - } catch (IllegalArgumentException e) { - // good - } + assertThrows(IllegalArgumentException.class, () -> ConstantUtils.validateMemberName(n, true), n); } } + @Test public void testSkipOverFieldSignatureVoid() { - int ret = ConstantUtils.skipOverFieldSignature("(V)V", 1, 4); - assertEquals(ret, 0, "Descriptor of (V)V starting at index 1, void disallowed"); + int ret = ConstantAccess.skipOverFieldSignature("(V)V", 1, 4); + assertEquals(0, ret, "Descriptor of (V)V starting at index 1, void disallowed"); } } diff --git a/test/jdk/java/lang/constant/DescribeResolveTest.java b/test/jdk/java/lang/constant/DescribeResolveTest.java index cc2a36dd3e2..7a9a11e64d0 100644 --- a/test/jdk/java/lang/constant/DescribeResolveTest.java +++ b/test/jdk/java/lang/constant/DescribeResolveTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -23,21 +23,19 @@ /* * @test - * @run testng DescribeResolveTest + * @run junit DescribeResolveTest */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.lang.constant.Constable; import java.lang.constant.ConstantDesc; import java.lang.invoke.MethodHandles; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class DescribeResolveTest { - @DataProvider public static Object[][] constables() { return new Object[][]{ { true }, @@ -48,7 +46,8 @@ public class DescribeResolveTest { }; } - @Test(dataProvider = "constables") + @ParameterizedTest + @MethodSource("constables") public void testDescribeResolve(Constable constable) throws ReflectiveOperationException { ConstantDesc desc = constable.describeConstable().orElseThrow(); Object resolved = desc.resolveConstantDesc(MethodHandles.lookup()); diff --git a/test/jdk/java/lang/constant/DynamicCallSiteDescTest.java b/test/jdk/java/lang/constant/DynamicCallSiteDescTest.java index 6f5e04efeb0..14d5fadbf01 100644 --- a/test/jdk/java/lang/constant/DynamicCallSiteDescTest.java +++ b/test/jdk/java/lang/constant/DynamicCallSiteDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 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 @@ -21,109 +21,71 @@ * questions. */ -import java.lang.invoke.MethodType; import java.lang.constant.*; -import java.util.Arrays; -import java.util.List; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -import static java.lang.constant.ConstantDescs.CD_int; -import static java.lang.constant.ConstantDescs.CD_void; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.*; -/** +/* * @test * @compile DynamicCallSiteDescTest.java - * @run testng DynamicCallSiteDescTest + * @run junit DynamicCallSiteDescTest * @summary unit tests for java.lang.constant.DynamicCallSiteDesc */ -@Test public class DynamicCallSiteDescTest extends SymbolicDescTest { /* note there is no unit test for method resolveCallSiteDesc as it is being tested in another test in this * suite, IndyDescTest */ + @Test public void testOf() throws ReflectiveOperationException { DirectMethodHandleDesc dmh = ConstantDescs.ofCallsiteBootstrap( ClassDesc.of("BootstrapAndTarget"), "bootstrap", ClassDesc.of("java.lang.invoke.CallSite") ); - try { - DynamicCallSiteDesc.of( - dmh, - "", - MethodTypeDesc.ofDescriptor("()I") - ); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException iae) { - // good - } + assertThrows(IllegalArgumentException.class, () -> DynamicCallSiteDesc.of( + dmh, + "", + MethodTypeDesc.ofDescriptor("()I") + )); - try { - DynamicCallSiteDesc.of( - null, - "getTarget", - MethodTypeDesc.ofDescriptor("()I") - ); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } + assertThrows(NullPointerException.class, () -> DynamicCallSiteDesc.of( + null, + "getTarget", + MethodTypeDesc.ofDescriptor("()I") + )); - try { - DynamicCallSiteDesc.of( - dmh, - null, - MethodTypeDesc.ofDescriptor("()I") - ); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } + assertThrows(NullPointerException.class, () -> DynamicCallSiteDesc.of( + dmh, + null, + MethodTypeDesc.ofDescriptor("()I") + )); - try { - DynamicCallSiteDesc.of( - dmh, - "getTarget", - null - ); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } + assertThrows(NullPointerException.class, () -> DynamicCallSiteDesc.of( + dmh, + "getTarget", + null + )); - try { - DynamicCallSiteDesc.of( - dmh, - "getTarget", - MethodTypeDesc.ofDescriptor("()I"), - null - ); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } - try { - DynamicCallSiteDesc.of( - dmh, - "getTarget", - MethodTypeDesc.ofDescriptor("()I"), - new ConstantDesc[]{ null } - ); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } + assertThrows(NullPointerException.class, () -> DynamicCallSiteDesc.of( + dmh, + "getTarget", + MethodTypeDesc.ofDescriptor("()I"), + null + )); + + assertThrows(NullPointerException.class, () -> DynamicCallSiteDesc.of( + dmh, + "getTarget", + MethodTypeDesc.ofDescriptor("()I"), + new ConstantDesc[]{ null } + )); } + @Test public void testWithArgs() throws ReflectiveOperationException { DynamicCallSiteDesc desc = DynamicCallSiteDesc.of(ConstantDescs.ofCallsiteBootstrap( ClassDesc.of("BootstrapAndTarget"), @@ -134,21 +96,11 @@ public class DynamicCallSiteDescTest extends SymbolicDescTest { MethodTypeDesc.ofDescriptor("()I") ); - try { - desc.withArgs(null); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } - - try { - desc.withArgs(new ConstantDesc[]{ null }); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } + assertThrows(NullPointerException.class, () -> desc.withArgs(null)); + assertThrows(NullPointerException.class, () -> desc.withArgs(new ConstantDesc[]{ null })); } + @Test public void testWithNameAndType() throws ReflectiveOperationException { DynamicCallSiteDesc desc = DynamicCallSiteDesc.of(ConstantDescs.ofCallsiteBootstrap( ClassDesc.of("BootstrapAndTarget"), @@ -159,21 +111,11 @@ public class DynamicCallSiteDescTest extends SymbolicDescTest { MethodTypeDesc.ofDescriptor("()I") ); - try { - desc.withNameAndType(null, MethodTypeDesc.ofDescriptor("()I")); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } - - try { - desc.withNameAndType("bootstrap", null); - fail("NullPointerException expected"); - } catch (NullPointerException npe) { - // good - } + assertThrows(NullPointerException.class, () -> desc.withNameAndType(null, MethodTypeDesc.ofDescriptor("()I"))); + assertThrows(NullPointerException.class, () -> desc.withNameAndType("bootstrap", null)); } + @Test public void testAccessorsAndFactories() throws ReflectiveOperationException { DynamicCallSiteDesc desc = DynamicCallSiteDesc.of(ConstantDescs.ofCallsiteBootstrap( ClassDesc.of("BootstrapAndTarget"), diff --git a/test/jdk/java/lang/constant/IndyDescTest.java b/test/jdk/java/lang/constant/IndyDescTest.java index 13862fecd0b..9d635c0eb08 100644 --- a/test/jdk/java/lang/constant/IndyDescTest.java +++ b/test/jdk/java/lang/constant/IndyDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,19 +32,19 @@ import java.lang.constant.DynamicCallSiteDesc; import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodTypeDesc; -import org.testng.annotations.Test; import static java.lang.constant.ConstantDescs.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import org.junit.jupiter.api.Test; -/** +/* * @test * @compile IndyDescTest.java - * @run testng IndyDescTest + * @run junit IndyDescTest * @summary unit tests for java.lang.constant.IndyDescTest */ -@Test public class IndyDescTest { public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, Object... args) { @@ -54,6 +54,7 @@ public class IndyDescTest { return new ConstantCallSite(MethodHandles.constant(String.class, (String) args[0])); } + @Test public void testIndyDesc() throws Throwable { ClassDesc c = ClassDesc.of("IndyDescTest"); MethodTypeDesc mt = MethodTypeDesc.of(CD_CallSite, CD_MethodHandles_Lookup, CD_String, CD_MethodType, CD_Object.arrayType()); @@ -96,6 +97,7 @@ public class IndyDescTest { assertEquals("foo", csd5.invocationName()); } + @Test public void testEqualsHashToString() throws Throwable { ClassDesc c = ClassDesc.of("IndyDescTest"); MethodTypeDesc mt = MethodTypeDesc.of(CD_CallSite, CD_MethodHandles_Lookup, CD_String, CD_MethodType, CD_Object.arrayType()); @@ -109,14 +111,14 @@ public class IndyDescTest { assertNotEquals(csd1, csd3); assertNotEquals(csd1.hashCode(), csd3.hashCode()); - assertEquals(csd1.toString(), "DynamicCallSiteDesc[IndyDescTest::bootstrap(wooga/):()String]"); + assertEquals("DynamicCallSiteDesc[IndyDescTest::bootstrap(wooga/):()String]", csd1.toString()); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testEmptyInvocationName() throws Throwable { ClassDesc c = ClassDesc.of("IndyDescTest"); MethodTypeDesc mt = MethodTypeDesc.of(CD_CallSite, CD_MethodHandles_Lookup, CD_String, CD_MethodType, CD_Object.arrayType()); DirectMethodHandleDesc mh = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, c, "bootstrap", mt); - DynamicCallSiteDesc csd1 = DynamicCallSiteDesc.of(mh, "", MethodTypeDesc.of(CD_String)); + Assertions.assertThrows(IllegalArgumentException.class, () -> DynamicCallSiteDesc.of(mh, "", MethodTypeDesc.of(CD_String))); } } diff --git a/test/jdk/java/lang/constant/MethodHandleDescTest.java b/test/jdk/java/lang/constant/MethodHandleDescTest.java index e8b66daa6b7..68d9dad045b 100644 --- a/test/jdk/java/lang/constant/MethodHandleDescTest.java +++ b/test/jdk/java/lang/constant/MethodHandleDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -37,7 +37,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; -import org.testng.annotations.Test; import static java.lang.constant.ConstantDescs.CD_Void; import static java.lang.constant.ConstantDescs.CD_boolean; @@ -54,20 +53,16 @@ import static java.lang.constant.ConstantDescs.CD_String; import static java.lang.constant.ConstantDescs.CD_int; import static java.lang.constant.ConstantDescs.CD_void; import static java.lang.invoke.MethodHandleInfo.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertNotSame; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; -/** +import static org.junit.jupiter.api.Assertions.*; + +/* * @test * @compile MethodHandleDescTest.java - * @run testng MethodHandleDescTest + * @run junit MethodHandleDescTest * @summary unit tests for java.lang.constant.MethodHandleDesc */ -@Test public class MethodHandleDescTest extends SymbolicDescTest { private static ClassDesc helperHolderClass = ClassDesc.of("TestHelpers"); private static ClassDesc testClass = helperHolderClass.nested("TestClass"); @@ -75,13 +70,13 @@ public class MethodHandleDescTest extends SymbolicDescTest { private static ClassDesc testSuperclass = helperHolderClass.nested("TestSuperclass"); - private static void assertMHEquals(MethodHandle a, MethodHandle b) { - MethodHandleInfo ia = LOOKUP.revealDirect(a); - MethodHandleInfo ib = LOOKUP.revealDirect(b); - assertEquals(ia.getDeclaringClass(), ib.getDeclaringClass()); - assertEquals(ia.getName(), ib.getName()); - assertEquals(ia.getMethodType(), ib.getMethodType()); - assertEquals(ia.getReferenceKind(), ib.getReferenceKind()); + private static void assertMHEquals(MethodHandle expected, MethodHandle actual) { + MethodHandleInfo expectedInfo = LOOKUP.revealDirect(expected); + MethodHandleInfo actualInfo = LOOKUP.revealDirect(actual); + assertEquals(expectedInfo.getDeclaringClass(), actualInfo.getDeclaringClass()); + assertEquals(expectedInfo.getName(), actualInfo.getName()); + assertEquals(expectedInfo.getMethodType(), actualInfo.getMethodType()); + assertEquals(expectedInfo.getReferenceKind(), actualInfo.getReferenceKind()); } private void testMethodHandleDesc(MethodHandleDesc r) throws ReflectiveOperationException { @@ -89,8 +84,8 @@ public class MethodHandleDescTest extends SymbolicDescTest { testSymbolicDesc(r); DirectMethodHandleDesc rr = (DirectMethodHandleDesc) r; - assertEquals(r, MethodHandleDesc.of(rr.kind(), rr.owner(), rr.methodName(), rr.lookupDescriptor())); - assertEquals(r.invocationType().resolveConstantDesc(LOOKUP), r.resolveConstantDesc(LOOKUP).type()); + assertEquals(MethodHandleDesc.of(rr.kind(), rr.owner(), rr.methodName(), rr.lookupDescriptor()), r); + assertEquals(r.resolveConstantDesc(LOOKUP).type(), r.invocationType().resolveConstantDesc(LOOKUP)); } else { testSymbolicDescForwardOnly(r); @@ -114,19 +109,20 @@ public class MethodHandleDescTest extends SymbolicDescTest { private void testMethodHandleDesc(MethodHandleDesc r, MethodHandle mh) throws ReflectiveOperationException { testMethodHandleDesc(r); - assertMHEquals(r.resolveConstantDesc(LOOKUP), mh); - assertEquals(mh.describeConstable().orElseThrow(), r); + assertMHEquals(mh, r.resolveConstantDesc(LOOKUP)); + assertEquals(r, mh.describeConstable().orElseThrow()); // compare extractable properties: refKind, owner, name, type MethodHandleInfo mhi = LOOKUP.revealDirect(mh); DirectMethodHandleDesc rr = (DirectMethodHandleDesc) r; - assertEquals(mhi.getDeclaringClass().descriptorString(), rr.owner().descriptorString()); - assertEquals(mhi.getName(), rr.methodName()); - assertEquals(mhi.getReferenceKind(), rr.kind().refKind); + assertEquals(rr.owner().descriptorString(), mhi.getDeclaringClass().descriptorString()); + assertEquals(rr.methodName(), mhi.getName()); + assertEquals(rr.kind().refKind, mhi.getReferenceKind()); MethodType type = mhi.getMethodType(); - assertEquals(type.toMethodDescriptorString(), lookupDescriptor(rr)); + assertEquals(lookupDescriptor(rr), type.toMethodDescriptorString()); } + @Test public void testSimpleMHs() throws ReflectiveOperationException { MethodHandle MH_String_isEmpty = LOOKUP.findVirtual(String.class, "isEmpty", MethodType.fromMethodDescriptorString("()Z", null)); testMethodHandleDesc(MethodHandleDesc.of(Kind.VIRTUAL, CD_String, "isEmpty", "()Z"), MH_String_isEmpty); @@ -147,33 +143,16 @@ public class MethodHandleDescTest extends SymbolicDescTest { MH_ArrayList_new); testMethodHandleDesc(MethodHandleDesc.ofConstructor(ClassDesc.of("java.util.ArrayList")), MH_ArrayList_new); - // bad constructor non void return type - try { - MethodHandleDesc.of(Kind.CONSTRUCTOR, ClassDesc.of("java.util.ArrayList"), "", "()I"); - fail("should have failed: non void return type for constructor"); - } catch (IllegalArgumentException ex) { - // good - } + assertThrows(IllegalArgumentException.class, () -> MethodHandleDesc.of(Kind.CONSTRUCTOR, ClassDesc.of("java.util.ArrayList"), "", "()I"), + "bad constructor non void return type"); + assertThrows(NullPointerException.class, () -> MethodHandleDesc.ofConstructor(ClassDesc.of("java.util.ArrayList", null)), + "null list of parameters"); - // null list of parameters - try { - MethodHandleDesc.ofConstructor(ClassDesc.of("java.util.ArrayList", null)); - fail("should have failed: null list of parameters"); - } catch (NullPointerException ex) { - // good - } - - // null elements in list of parameters - try { - ClassDesc[] paramList = new ClassDesc[1]; - paramList[0] = null; - MethodHandleDesc.ofConstructor(ClassDesc.of("java.util.ArrayList"), paramList); - fail("should have failed: null content in list of parameters"); - } catch (NullPointerException ex) { - // good - } + assertThrows(NullPointerException.class, () -> MethodHandleDesc.ofConstructor(ClassDesc.of("java.util.ArrayList"), new ClassDesc[] { null }), + "null elements in list of parameters"); } + @Test public void testAsType() throws Throwable { MethodHandleDesc mhr = MethodHandleDesc.ofMethod(Kind.STATIC, ClassDesc.of("java.lang.Integer"), "valueOf", MethodTypeDesc.of(CD_Integer, CD_int)); @@ -181,41 +160,33 @@ public class MethodHandleDescTest extends SymbolicDescTest { testMethodHandleDesc(takesInteger); MethodHandle mh1 = takesInteger.resolveConstantDesc(LOOKUP); assertEquals((Integer) 3, (Integer) mh1.invokeExact((Integer) 3)); - assertEquals(takesInteger.toString(), "MethodHandleDesc[STATIC/Integer::valueOf(int)Integer].asType(Integer)Integer"); + assertEquals("MethodHandleDesc[STATIC/Integer::valueOf(int)Integer].asType(Integer)Integer", takesInteger.toString()); - try { - Integer i = (Integer) mh1.invokeExact(3); - fail("Expected WMTE"); - } - catch (WrongMethodTypeException ignored) { } + assertThrows(WrongMethodTypeException.class, () -> { + Integer _ = (Integer) mh1.invokeExact(3); + }); MethodHandleDesc takesInt = takesInteger.asType(MethodTypeDesc.of(CD_Integer, CD_int)); testMethodHandleDesc(takesInt); MethodHandle mh2 = takesInt.resolveConstantDesc(LOOKUP); assertEquals((Integer) 3, (Integer) mh2.invokeExact(3)); - try { - Integer i = (Integer) mh2.invokeExact((Integer) 3); - fail("Expected WMTE"); - } - catch (WrongMethodTypeException ignored) { } + assertThrows(WrongMethodTypeException.class, () -> { + Integer _ = (Integer) mh2.invokeExact((Integer) 3); + }); // Short circuit optimization MethodHandleDesc same = mhr.asType(mhr.invocationType()); assertSame(mhr, same); - try { - mhr.asType(null); - fail("Expected NPE"); - } catch (NullPointerException ex) { - // good - } + assertThrows(NullPointerException.class, () -> mhr.asType(null)); // @@@ Test varargs adaptation // @@@ Test bad adaptations and assert runtime error on resolution // @@@ Test intrinsification of adapted MH } + @Test public void testMethodHandleDesc() throws Throwable { MethodHandleDesc ctorDesc = MethodHandleDesc.of(Kind.CONSTRUCTOR, testClass, "", "()V"); MethodHandleDesc staticMethodDesc = MethodHandleDesc.of(Kind.STATIC, testClass, "sm", "(I)I"); @@ -229,14 +200,14 @@ public class MethodHandleDescTest extends SymbolicDescTest { MethodHandleDesc privateStaticMethodDesc = MethodHandleDesc.of(Kind.STATIC, testClass, "psm", "(I)I"); MethodHandleDesc privateStaticIMethodDesc = MethodHandleDesc.of(Kind.INTERFACE_STATIC, testInterface, "psm", "(I)I"); - assertEquals(ctorDesc.invocationType(), MethodTypeDesc.of(testClass)); - assertEquals(((DirectMethodHandleDesc) ctorDesc).lookupDescriptor(), "()V"); + assertEquals(MethodTypeDesc.of(testClass), ctorDesc.invocationType()); + assertEquals("()V", ((DirectMethodHandleDesc) ctorDesc).lookupDescriptor()); - assertEquals(staticMethodDesc.invocationType().descriptorString(), "(I)I"); - assertEquals(((DirectMethodHandleDesc) staticMethodDesc).lookupDescriptor(), "(I)I"); + assertEquals("(I)I", staticMethodDesc.invocationType().descriptorString()); + assertEquals("(I)I", ((DirectMethodHandleDesc) staticMethodDesc).lookupDescriptor()); - assertEquals(instanceMethodDesc.invocationType().descriptorString(), "(" + testClass.descriptorString() + "I)I"); - assertEquals(((DirectMethodHandleDesc) instanceMethodDesc).lookupDescriptor(), "(I)I"); + assertEquals("(" + testClass.descriptorString() + "I)I", instanceMethodDesc.invocationType().descriptorString()); + assertEquals("(I)I", ((DirectMethodHandleDesc) instanceMethodDesc).lookupDescriptor()); for (MethodHandleDesc r : List.of(ctorDesc, staticMethodDesc, staticIMethodDesc, instanceMethodDesc, instanceIMethodDesc)) testMethodHandleDesc(r); @@ -257,29 +228,23 @@ public class MethodHandleDescTest extends SymbolicDescTest { assertEquals(5, (int) instanceIMethodDesc.resolveConstantDesc(LOOKUP).invokeExact(instanceI, 5)); assertEquals(5, (int) instanceIMethodDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instanceI, 5)); - try { superMethodDesc.resolveConstantDesc(LOOKUP); fail(); } - catch (IllegalAccessException e) { /* expected */ } + assertThrows(IllegalAccessException.class, () -> superMethodDesc.resolveConstantDesc(LOOKUP)); assertEquals(-1, (int) superMethodDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instance, 5)); - try { superIMethodDesc.resolveConstantDesc(LOOKUP); fail(); } - catch (IllegalAccessException e) { /* expected */ } + assertThrows(IllegalAccessException.class, () -> superIMethodDesc.resolveConstantDesc(LOOKUP)); assertEquals(0, (int) superIMethodDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instance, 5)); - try { privateMethodDesc.resolveConstantDesc(LOOKUP); fail(); } - catch (IllegalAccessException e) { /* expected */ } + assertThrows(IllegalAccessException.class, () -> privateMethodDesc.resolveConstantDesc(LOOKUP)); assertEquals(5, (int) privateMethodDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instance, 5)); - try { privateIMethodDesc.resolveConstantDesc(LOOKUP); fail(); } - catch (IllegalAccessException e) { /* expected */ } + assertThrows(IllegalAccessException.class, () -> privateIMethodDesc.resolveConstantDesc(LOOKUP)); assertEquals(0, (int) privateIMethodDesc.resolveConstantDesc(TestHelpers.TestInterface.LOOKUP).invokeExact(instanceI, 5)); assertEquals(0, (int) privateIMethodDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invoke(instanceI, 5)); - try { privateStaticMethodDesc.resolveConstantDesc(LOOKUP); fail(); } - catch (IllegalAccessException e) { /* expected */ } + assertThrows(IllegalAccessException.class, () -> privateStaticMethodDesc.resolveConstantDesc(LOOKUP)); assertEquals(5, (int) privateStaticMethodDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(5)); - try { privateStaticIMethodDesc.resolveConstantDesc(LOOKUP); fail(); } - catch (IllegalAccessException e) { /* expected */ } + assertThrows(IllegalAccessException.class, () -> privateStaticIMethodDesc.resolveConstantDesc(LOOKUP)); assertEquals(0, (int) privateStaticIMethodDesc.resolveConstantDesc(TestHelpers.TestInterface.LOOKUP).invokeExact(5)); assertEquals(0, (int) privateStaticIMethodDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(5)); @@ -292,34 +257,29 @@ public class MethodHandleDescTest extends SymbolicDescTest { for (MethodHandleDesc r : List.of(staticSetterDesc, staticGetterDesc, staticGetterIDesc, setterDesc, getterDesc)) testMethodHandleDesc(r); - staticSetterDesc.resolveConstantDesc(LOOKUP).invokeExact(6); assertEquals(TestHelpers.TestClass.sf, 6); + staticSetterDesc.resolveConstantDesc(LOOKUP).invokeExact(6); assertEquals(6, TestHelpers.TestClass.sf); assertEquals(6, (int) staticGetterDesc.resolveConstantDesc(LOOKUP).invokeExact()); assertEquals(6, (int) staticGetterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact()); - staticSetterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(7); assertEquals(TestHelpers.TestClass.sf, 7); + staticSetterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(7); assertEquals(7, TestHelpers.TestClass.sf); assertEquals(7, (int) staticGetterDesc.resolveConstantDesc(LOOKUP).invokeExact()); assertEquals(7, (int) staticGetterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact()); assertEquals(3, (int) staticGetterIDesc.resolveConstantDesc(LOOKUP).invokeExact()); assertEquals(3, (int) staticGetterIDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact()); - setterDesc.resolveConstantDesc(LOOKUP).invokeExact(instance, 6); assertEquals(instance.f, 6); + setterDesc.resolveConstantDesc(LOOKUP).invokeExact(instance, 6); assertEquals(6, instance.f); assertEquals(6, (int) getterDesc.resolveConstantDesc(LOOKUP).invokeExact(instance)); assertEquals(6, (int) getterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instance)); - setterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instance, 7); assertEquals(instance.f, 7); + setterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instance, 7); assertEquals(7, instance.f); assertEquals(7, (int) getterDesc.resolveConstantDesc(LOOKUP).invokeExact(instance)); assertEquals(7, (int) getterDesc.resolveConstantDesc(TestHelpers.TestClass.LOOKUP).invokeExact(instance)); } private void assertBadArgs(Supplier supplier, String s) { - try { - MethodHandleDesc r = supplier.get(); - fail("Expected failure for " + s); - } - catch (IllegalArgumentException e) { - // succeed - } + assertThrows(IllegalArgumentException.class, supplier::get, s); } + @Test public void testBadFieldMHs() { List badGetterDescs = List.of("()V", "(Ljava/lang/Object;)V", "(I)I", "(Ljava/lang/Object;I)I"); List badStaticGetterDescs = List.of("()V", "(Ljava/lang/Object;)I", "(I)I", "(Ljava/lang/Object;I)I"); @@ -332,11 +292,12 @@ public class MethodHandleDescTest extends SymbolicDescTest { badStaticSetterDescs.forEach(s -> assertBadArgs(() -> MethodHandleDesc.of(STATIC_SETTER, helperHolderClass, "x", s), s)); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testBadOwners() { - MethodHandleDesc.ofMethod(VIRTUAL, ClassDesc.ofDescriptor("I"), "x", MethodTypeDesc.ofDescriptor("()I")); + assertThrows(IllegalArgumentException.class, () -> MethodHandleDesc.ofMethod(VIRTUAL, ClassDesc.ofDescriptor("I"), "x", MethodTypeDesc.ofDescriptor("()I"))); } + @Test public void testSymbolicDescsConstants() throws ReflectiveOperationException { int tested = 0; Field[] fields = ConstantDescs.class.getDeclaredFields(); @@ -359,10 +320,11 @@ public class MethodHandleDescTest extends SymbolicDescTest { assertTrue(tested > 0); } + @Test public void testKind() { for (Kind k : Kind.values()) { - assertEquals(Kind.valueOf(k.refKind), Kind.valueOf(k.refKind, k.refKind == MethodHandleInfo.REF_invokeInterface)); - assertEquals(Kind.valueOf(k.refKind, k.isInterface), k); + assertEquals(Kind.valueOf(k.refKind, k.refKind == MethodHandleInfo.REF_invokeInterface), Kind.valueOf(k.refKind)); + assertEquals(k, Kind.valueOf(k.refKind, k.isInterface)); } // let's now verify those cases for which the value of the isInterface parameter is ignored int[] isInterfaceIgnored = new int[] { @@ -374,15 +336,15 @@ public class MethodHandleDescTest extends SymbolicDescTest { REF_invokeInterface }; for (int refKind : isInterfaceIgnored) { - assertEquals(Kind.valueOf(refKind, false), Kind.valueOf(refKind, true)); + assertEquals(Kind.valueOf(refKind, true), Kind.valueOf(refKind, false)); } // some explicit tests for REF_invokeStatic and REF_invokeSpecial - assertNotEquals(Kind.valueOf(REF_invokeStatic, false), Kind.valueOf(REF_invokeStatic, true)); - assertNotEquals(Kind.valueOf(REF_invokeSpecial, false), Kind.valueOf(REF_invokeSpecial, true)); - assertEquals(Kind.valueOf(REF_invokeStatic, false), Kind.STATIC); - assertEquals(Kind.valueOf(REF_invokeStatic, true), Kind.INTERFACE_STATIC); - assertEquals(Kind.valueOf(REF_invokeSpecial, false), Kind.SPECIAL); - assertEquals(Kind.valueOf(REF_invokeSpecial, true), Kind.INTERFACE_SPECIAL); + assertNotEquals(Kind.valueOf(REF_invokeStatic, true), Kind.valueOf(REF_invokeStatic, false)); + assertNotEquals(Kind.valueOf(REF_invokeSpecial, true), Kind.valueOf(REF_invokeSpecial, false)); + assertEquals(Kind.STATIC, Kind.valueOf(REF_invokeStatic, false)); + assertEquals(Kind.INTERFACE_STATIC, Kind.valueOf(REF_invokeStatic, true)); + assertEquals(Kind.SPECIAL, Kind.valueOf(REF_invokeSpecial, false)); + assertEquals(Kind.INTERFACE_SPECIAL, Kind.valueOf(REF_invokeSpecial, true)); } } diff --git a/test/jdk/java/lang/constant/MethodTypeDescTest.java b/test/jdk/java/lang/constant/MethodTypeDescTest.java index 5f7a2fc691f..06a6d5055b8 100644 --- a/test/jdk/java/lang/constant/MethodTypeDescTest.java +++ b/test/jdk/java/lang/constant/MethodTypeDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,21 +32,20 @@ import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; -import org.testng.annotations.Test; import static java.lang.constant.ConstantDescs.*; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; -/** +/* * @test * @bug 8304932 * @compile MethodTypeDescTest.java - * @run testng MethodTypeDescTest + * @run junit MethodTypeDescTest * @summary unit tests for java.lang.constant.MethodTypeDesc */ -@Test public class MethodTypeDescTest extends SymbolicDescTest { private void testMethodTypeDesc(MethodTypeDesc r) throws ReflectiveOperationException { @@ -75,16 +74,16 @@ public class MethodTypeDescTest extends SymbolicDescTest { private void testMethodTypeDesc(MethodTypeDesc r, MethodType mt) throws ReflectiveOperationException { testMethodTypeDesc(r); - assertEquals(r.resolveConstantDesc(LOOKUP), mt); - assertEquals(mt.describeConstable().get(), r); + assertEquals(mt, r.resolveConstantDesc(LOOKUP)); + assertEquals(r, mt.describeConstable().get()); - assertEquals(r.descriptorString(), mt.toMethodDescriptorString()); - assertEquals(r.parameterCount(), mt.parameterCount()); - assertEquals(r.parameterList(), mt.parameterList().stream().map(SymbolicDescTest::classToDesc).collect(toList())); - assertEquals(r.parameterArray(), Stream.of(mt.parameterArray()).map(SymbolicDescTest::classToDesc).toArray(ClassDesc[]::new)); + assertEquals(mt.toMethodDescriptorString(), r.descriptorString()); + assertEquals(mt.parameterCount(), r.parameterCount()); + assertEquals(mt.parameterList().stream().map(SymbolicDescTest::classToDesc).collect(toList()), r.parameterList()); + assertArrayEquals(Stream.of(mt.parameterArray()).map(SymbolicDescTest::classToDesc).toArray(ClassDesc[]::new), r.parameterArray()); for (int i=0; i { - MethodTypeDesc newDesc = mtDesc.changeReturnType(null); - }); + assertThrows(NullPointerException.class, () -> mtDesc.changeReturnType(null)); // changeParamType for (int i=0; i paramTypes[j]) .toArray(ClassDesc[]::new); MethodTypeDesc newDesc = mtDesc.dropParameterTypes(i, i + 1); - assertEquals(newDesc, MethodTypeDesc.of(returnType, ps)); + assertEquals(MethodTypeDesc.of(returnType, ps), newDesc); testMethodTypeDesc(newDesc, mt.dropParameterTypes(i, i+1)); // drop multiple params @@ -147,7 +144,7 @@ public class MethodTypeDescTest extends SymbolicDescTest { var t = new ArrayList<>(Arrays.asList(paramTypes)); t.subList(i, j).clear(); MethodTypeDesc multiDrop = mtDesc.dropParameterTypes(i, j); - assertEquals(multiDrop, MethodTypeDesc.of(returnType, t.toArray(ClassDesc[]::new))); + assertEquals(MethodTypeDesc.of(returnType, t.toArray(ClassDesc[]::new)), multiDrop); testMethodTypeDesc(multiDrop, mt.dropParameterTypes(i, j)); } } @@ -162,7 +159,7 @@ public class MethodTypeDescTest extends SymbolicDescTest { .mapToObj(j -> (j < k) ? paramTypes[j] : (j == k) ? p : paramTypes[j-1]) .toArray(ClassDesc[]::new); MethodTypeDesc newDesc = mtDesc.insertParameterTypes(i, p); - assertEquals(newDesc, MethodTypeDesc.of(returnType, ps)); + assertEquals(MethodTypeDesc.of(returnType, ps), newDesc); testMethodTypeDesc(newDesc, mt.insertParameterTypes(i, p.resolveConstantDesc(LOOKUP))); } @@ -172,7 +169,7 @@ public class MethodTypeDescTest extends SymbolicDescTest { a.addAll(i, Arrays.asList(addition)); MethodTypeDesc newDesc = mtDesc.insertParameterTypes(i, addition); - assertEquals(newDesc, MethodTypeDesc.of(returnType, a.toArray(ClassDesc[]::new))); + assertEquals(MethodTypeDesc.of(returnType, a.toArray(ClassDesc[]::new)), newDesc); testMethodTypeDesc(newDesc, mt.insertParameterTypes(i, Arrays.stream(addition).map(d -> { try { return (Class) d.resolveConstantDesc(LOOKUP); @@ -190,32 +187,28 @@ public class MethodTypeDescTest extends SymbolicDescTest { IntStream.rangeClosed(0, paramDescTypes.length - 1) .mapToObj(i -> ClassDesc.ofDescriptor(paramDescTypes[i])).toArray(ClassDesc[]::new); MethodTypeDesc mtDesc = MethodTypeDesc.of(returnType, paramTypes); - expectThrows(IndexOutOfBoundsException.class, () -> { - MethodTypeDesc newDesc = mtDesc.insertParameterTypes(-1, paramTypes); - }); + assertThrows(IndexOutOfBoundsException.class, () -> mtDesc.insertParameterTypes(-1, paramTypes)); - expectThrows(IndexOutOfBoundsException.class, () -> { - MethodTypeDesc newDesc = mtDesc.insertParameterTypes(paramTypes.length + 1, paramTypes); - }); + assertThrows(IndexOutOfBoundsException.class, () -> mtDesc.insertParameterTypes(paramTypes.length + 1, paramTypes)); - expectThrows(IllegalArgumentException.class, () -> { + { ClassDesc[] newParamTypes = new ClassDesc[1]; newParamTypes[0] = CD_void; MethodTypeDesc newDesc = MethodTypeDesc.of(returnType, CD_int); - newDesc = newDesc.insertParameterTypes(0, newParamTypes); - }); + assertThrows(IllegalArgumentException.class, () -> newDesc.insertParameterTypes(0, newParamTypes)); + } - expectThrows(NullPointerException.class, () -> { + { MethodTypeDesc newDesc = MethodTypeDesc.of(returnType, CD_int); - newDesc = newDesc.insertParameterTypes(0, null); - }); + assertThrows(NullPointerException.class, () -> newDesc.insertParameterTypes(0, null)); + } - expectThrows(NullPointerException.class, () -> { + { ClassDesc[] newParamTypes = new ClassDesc[1]; newParamTypes[0] = null; MethodTypeDesc newDesc = MethodTypeDesc.of(returnType, CD_int); - newDesc = newDesc.insertParameterTypes(0, newParamTypes); - }); + assertThrows(NullPointerException.class, () -> newDesc.insertParameterTypes(0, newParamTypes)); + } } private void badDropParametersTypes(ClassDesc returnType, String... paramDescTypes) { @@ -224,27 +217,18 @@ public class MethodTypeDescTest extends SymbolicDescTest { .mapToObj(i -> ClassDesc.ofDescriptor(paramDescTypes[i])).toArray(ClassDesc[]::new); MethodTypeDesc mtDesc = MethodTypeDesc.of(returnType, paramTypes); - expectThrows(IndexOutOfBoundsException.class, () -> { - MethodTypeDesc newDesc = mtDesc.dropParameterTypes(-1, 0); - }); + assertThrows(IndexOutOfBoundsException.class, () -> mtDesc.dropParameterTypes(-1, 0)); - expectThrows(IndexOutOfBoundsException.class, () -> { - MethodTypeDesc newDesc = mtDesc.dropParameterTypes(paramTypes.length, 0); - }); + assertThrows(IndexOutOfBoundsException.class, () -> mtDesc.dropParameterTypes(paramTypes.length, 0)); - expectThrows(IndexOutOfBoundsException.class, () -> { - MethodTypeDesc newDesc = mtDesc.dropParameterTypes(paramTypes.length + 1, 0); - }); + assertThrows(IndexOutOfBoundsException.class, () -> mtDesc.dropParameterTypes(paramTypes.length + 1, 0)); - expectThrows(IndexOutOfBoundsException.class, () -> { - MethodTypeDesc newDesc = mtDesc.dropParameterTypes(0, paramTypes.length + 1); - }); + assertThrows(IndexOutOfBoundsException.class, () -> mtDesc.dropParameterTypes(0, paramTypes.length + 1)); - expectThrows(IndexOutOfBoundsException.class, () -> { - MethodTypeDesc newDesc = mtDesc.dropParameterTypes(1, 0); - }); + assertThrows(IndexOutOfBoundsException.class, () -> mtDesc.dropParameterTypes(1, 0)); } + @Test public void testMethodTypeDesc() throws ReflectiveOperationException { for (String r : returnDescs) { assertMethodType(ClassDesc.ofDescriptor(r)); @@ -257,6 +241,7 @@ public class MethodTypeDescTest extends SymbolicDescTest { } } + @Test public void testBadMethodTypeRefs() { // ofDescriptor List badDescriptors = List.of("()II", "()I;", "(I;)", "(I)", "()L", "(V)V", @@ -283,29 +268,32 @@ public class MethodTypeDescTest extends SymbolicDescTest { assertThrows(IllegalArgumentException.class, () -> MethodTypeDesc.of(CD_int, List.of(CD_void))); } + @Test public void testOfArrayImmutability() { ClassDesc[] args = {CD_Object, CD_int}; var mtd = MethodTypeDesc.of(CD_void, args); args[1] = CD_void; - assertEquals(mtd, MethodTypeDesc.of(CD_void, CD_Object, CD_int)); + assertEquals(MethodTypeDesc.of(CD_void, CD_Object, CD_int), mtd); mtd.parameterArray()[1] = CD_void; - assertEquals(mtd, MethodTypeDesc.of(CD_void, CD_Object, CD_int)); + assertEquals(MethodTypeDesc.of(CD_void, CD_Object, CD_int), mtd); } + @Test public void testOfListImmutability() { List args = Arrays.asList(CD_Object, CD_int); var mtd = MethodTypeDesc.of(CD_void, args); args.set(1, CD_void); - assertEquals(mtd, MethodTypeDesc.of(CD_void, CD_Object, CD_int)); + assertEquals(MethodTypeDesc.of(CD_void, CD_Object, CD_int), mtd); assertThrows(UnsupportedOperationException.class, () -> mtd.parameterList().set(1, CD_void)); - assertEquals(mtd, MethodTypeDesc.of(CD_void, CD_Object, CD_int)); + assertEquals(MethodTypeDesc.of(CD_void, CD_Object, CD_int), mtd); } + @Test public void testMissingClass() { var mtd = MTD_void.insertParameterTypes(0, ClassDesc.of("does.not.exist.DoesNotExist")); assertThrows(ReflectiveOperationException.class, () -> mtd.resolveConstantDesc(MethodHandles.publicLookup())); diff --git a/test/jdk/java/lang/constant/NameValidationTest.java b/test/jdk/java/lang/constant/NameValidationTest.java index be266129370..96296ce290e 100644 --- a/test/jdk/java/lang/constant/NameValidationTest.java +++ b/test/jdk/java/lang/constant/NameValidationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -21,26 +21,22 @@ * questions. */ -/** +/* * @test * @bug 8215510 * @compile NameValidationTest.java - * @run testng NameValidationTest + * @run junit NameValidationTest * @summary unit tests for verifying member names */ import java.lang.constant.*; -import java.lang.invoke.*; - -import org.testng.annotations.Test; import static java.lang.constant.DirectMethodHandleDesc.*; import static java.lang.constant.ConstantDescs.*; -import static java.lang.constant.DirectMethodHandleDesc.Kind.VIRTUAL; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; -@Test public class NameValidationTest { private static final String[] badMemberNames = new String[] {"xx.xx", "zz;zz", "[l", "aa/aa", ""}; @@ -49,26 +45,17 @@ public class NameValidationTest { private static final String[] badClassNames = new String[] {"zz;zz", "[l", "aa/aa", ".", "a..b"}; private static final String[] goodClassNames = new String[] {"3", "~", "$", "qq", "a.a"}; + @Test public void testMemberNames() { DirectMethodHandleDesc mh = MethodHandleDesc.of(Kind.VIRTUAL, CD_String, "isEmpty", "()Z"); for (String badName : badMemberNames) { - try { - memberNamesHelper(badName, mh, CD_int, null); - fail("Expected failure for name " + badName); - } catch (IllegalArgumentException iae) { - // expected - } - try { - memberNamesHelper(badName, mh, CD_int, new ConstantDesc[0]); - fail("Expected failure for name " + badName); - } catch (IllegalArgumentException iae) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> memberNamesHelper(badName, mh, CD_int, null), badName); + assertThrows(IllegalArgumentException.class, () -> memberNamesHelper(badName, mh, CD_int, new ConstantDesc[0]), badName); } - for (String badName : goodMemberNames) { - memberNamesHelper(badName, mh, CD_int, null); - memberNamesHelper(badName, mh, CD_int, new ConstantDesc[0]); + for (String goodName : goodMemberNames) { + memberNamesHelper(goodName, mh, CD_int, null); + memberNamesHelper(goodName, mh, CD_int, new ConstantDesc[0]); } } @@ -83,14 +70,10 @@ public class NameValidationTest { } } + @Test public void testClassNames() { for (String badName : badClassNames) { - try { - ClassDesc.of(badName); - fail("Expected failure for name " + badName); - } catch (IllegalArgumentException iae) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> ClassDesc.of(badName), badName); } for (String goodName : goodClassNames) { diff --git a/test/jdk/java/lang/constant/SymbolicDescTest.java b/test/jdk/java/lang/constant/SymbolicDescTest.java index 5eb0ca10da6..8d4fcdf6a2c 100644 --- a/test/jdk/java/lang/constant/SymbolicDescTest.java +++ b/test/jdk/java/lang/constant/SymbolicDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,7 +32,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Base class for XxxDesc tests @@ -106,7 +106,7 @@ public abstract class SymbolicDescTest { if (desc instanceof Constable) { Optional opt = (Optional) ((Constable) desc).describeConstable(); ConstantDesc sr = (ConstantDesc) opt.orElseThrow().resolveConstantDesc(LOOKUP); - assertEquals(sr, desc); + assertEquals(desc, sr); } } } diff --git a/test/jdk/java/lang/constant/TypeDescriptorTest.java b/test/jdk/java/lang/constant/TypeDescriptorTest.java index 9ffbd886f41..9253462e223 100644 --- a/test/jdk/java/lang/constant/TypeDescriptorTest.java +++ b/test/jdk/java/lang/constant/TypeDescriptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -22,37 +22,35 @@ */ import java.lang.invoke.TypeDescriptor; -import java.lang.constant.ClassDesc; - -import org.testng.annotations.Test; import static java.lang.constant.ConstantDescs.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; -/** +/* * @test * @compile TypeDescriptorTest.java - * @run testng TypeDescriptorTest + * @run junit TypeDescriptorTest * @summary unit tests for implementations of java.lang.invoke.TypeDescriptor */ -@Test public class TypeDescriptorTest { private> void testArray(F f, boolean isArray, F component, F array) { if (isArray) { assertTrue(f.isArray()); - assertEquals(f.arrayType(), array); - assertEquals(f.componentType(), component); + assertEquals(array, f.arrayType()); + assertEquals(component, f.componentType()); } else { assertFalse(f.isArray()); - assertEquals(f.arrayType(), array); + assertEquals(array, f.arrayType()); assertNull(f.componentType()); } } + @Test public void testClass() { testArray(int.class, false, null, int[].class); testArray(int[].class, true, int.class, int[][].class); @@ -67,6 +65,7 @@ public class TypeDescriptorTest { assertFalse(String[].class.isPrimitive()); } + @Test public void testClassDesc() { testArray(CD_int, false, null, CD_int.arrayType()); diff --git a/test/jdk/java/lang/constant/boottest/TEST.properties b/test/jdk/java/lang/constant/boottest/TEST.properties deleted file mode 100644 index 565242e959b..00000000000 --- a/test/jdk/java/lang/constant/boottest/TEST.properties +++ /dev/null @@ -1,4 +0,0 @@ -# This file identifies root(s) of the test-ng hierarchy. - -TestNG.dirs = . -lib.dirs = /lib/testlibrary/bootlib diff --git a/test/jdk/java/lang/constant/java.base/jdk/internal/constant/ConstantAccess.java b/test/jdk/java/lang/constant/java.base/jdk/internal/constant/ConstantAccess.java new file mode 100644 index 00000000000..a8d7c2f3f99 --- /dev/null +++ b/test/jdk/java/lang/constant/java.base/jdk/internal/constant/ConstantAccess.java @@ -0,0 +1,30 @@ +/* + * 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 jdk.internal.constant; + +// Exposes package-private methods for testing. +public final class ConstantAccess { + public static int skipOverFieldSignature(String descriptor, int start, int end) { + return ConstantUtils.skipOverFieldSignature(descriptor, start, end); + } +} From 8af95879f3a74e1b6e419305e1db85e9c14972db Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 25 Feb 2026 15:58:49 +0000 Subject: [PATCH 042/636] 8377438: [VectorAPI] Add new carrier type to LaneType class Reviewed-by: psandoz, liach --- src/hotspot/share/classfile/vmSymbols.hpp | 1 + src/hotspot/share/opto/vectorIntrinsics.cpp | 8 ++++---- src/hotspot/share/prims/vectorSupport.cpp | 4 ++-- .../jdk/incubator/vector/AbstractSpecies.java | 13 ++++++++++--- .../jdk/incubator/vector/ByteVector128.java | 11 +++++++++-- .../jdk/incubator/vector/ByteVector256.java | 11 +++++++++-- .../jdk/incubator/vector/ByteVector512.java | 11 +++++++++-- .../jdk/incubator/vector/ByteVector64.java | 11 +++++++++-- .../jdk/incubator/vector/ByteVectorMax.java | 11 +++++++++-- .../jdk/incubator/vector/DoubleVector128.java | 11 +++++++++-- .../jdk/incubator/vector/DoubleVector256.java | 11 +++++++++-- .../jdk/incubator/vector/DoubleVector512.java | 11 +++++++++-- .../jdk/incubator/vector/DoubleVector64.java | 11 +++++++++-- .../jdk/incubator/vector/DoubleVectorMax.java | 11 +++++++++-- .../jdk/incubator/vector/FloatVector128.java | 11 +++++++++-- .../jdk/incubator/vector/FloatVector256.java | 11 +++++++++-- .../jdk/incubator/vector/FloatVector512.java | 11 +++++++++-- .../jdk/incubator/vector/FloatVector64.java | 11 +++++++++-- .../jdk/incubator/vector/FloatVectorMax.java | 11 +++++++++-- .../jdk/incubator/vector/IntVector128.java | 11 +++++++++-- .../jdk/incubator/vector/IntVector256.java | 11 +++++++++-- .../jdk/incubator/vector/IntVector512.java | 11 +++++++++-- .../jdk/incubator/vector/IntVector64.java | 11 +++++++++-- .../jdk/incubator/vector/IntVectorMax.java | 11 +++++++++-- .../jdk/incubator/vector/LaneType.java | 19 ++++++++++++------- .../jdk/incubator/vector/LongVector128.java | 11 +++++++++-- .../jdk/incubator/vector/LongVector256.java | 11 +++++++++-- .../jdk/incubator/vector/LongVector512.java | 11 +++++++++-- .../jdk/incubator/vector/LongVector64.java | 11 +++++++++-- .../jdk/incubator/vector/LongVectorMax.java | 11 +++++++++-- .../jdk/incubator/vector/ShortVector128.java | 11 +++++++++-- .../jdk/incubator/vector/ShortVector256.java | 11 +++++++++-- .../jdk/incubator/vector/ShortVector512.java | 11 +++++++++-- .../jdk/incubator/vector/ShortVector64.java | 11 +++++++++-- .../jdk/incubator/vector/ShortVectorMax.java | 11 +++++++++-- .../jdk/incubator/vector/VectorShape.java | 14 +++++++------- .../vector/X-VectorBits.java.template | 11 +++++++++-- .../classes/jdk/incubator/vector/gen-src.sh | 4 ++++ 38 files changed, 319 insertions(+), 85 deletions(-) diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 0054b7ba3f2..2ae42bebcfd 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -98,6 +98,7 @@ class SerializeClosure; template(jdk_internal_vm_vector_VectorMask, "jdk/internal/vm/vector/VectorSupport$VectorMask") \ template(jdk_internal_vm_vector_VectorShuffle, "jdk/internal/vm/vector/VectorSupport$VectorShuffle") \ template(payload_name, "payload") \ + template(CTYPE_name, "CTYPE") \ template(ETYPE_name, "ETYPE") \ template(VLENGTH_name, "VLENGTH") \ \ diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 9df10fefdd1..7fc06db4b78 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -42,7 +42,7 @@ static bool check_vbox(const TypeInstPtr* vbox_type) { ciInstanceKlass* ik = vbox_type->instance_klass(); assert(is_vector(ik), "not a vector"); - ciField* fd1 = ik->get_field_by_name(ciSymbols::ETYPE_name(), ciSymbols::class_signature(), /* is_static */ true); + ciField* fd1 = ik->get_field_by_name(ciSymbols::CTYPE_name(), ciSymbols::class_signature(), /* is_static */ true); assert(fd1 != nullptr, "element type info is missing"); ciConstant val1 = fd1->constant_value(); @@ -301,9 +301,9 @@ static bool is_primitive_lane_type(VectorSupport::LaneType laneType) { return laneType >= VectorSupport::LT_FLOAT && laneType <= VectorSupport::LT_LONG; } -static BasicType get_vector_primitive_lane_type(VectorSupport::LaneType lane_type) { - assert(is_primitive_lane_type(lane_type), ""); - switch (lane_type) { +static BasicType get_vector_primitive_lane_type(VectorSupport::LaneType lanetype) { + assert(is_primitive_lane_type(lanetype), ""); + switch (lanetype) { case VectorSupport::LaneType::LT_FLOAT: return T_FLOAT; case VectorSupport::LaneType::LT_DOUBLE: return T_DOUBLE; case VectorSupport::LaneType::LT_LONG: return T_LONG; diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index 9c0ec113a02..58f22d38d33 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -54,8 +54,8 @@ bool VectorSupport::is_vector_mask(Klass* klass) { BasicType VectorSupport::klass2bt(InstanceKlass* ik) { assert(ik->is_subclass_of(vmClasses::vector_VectorPayload_klass()), "%s not a VectorPayload", ik->name()->as_C_string()); fieldDescriptor fd; // find_field initializes fd if found - // static final Class ETYPE; - Klass* holder = ik->find_field(vmSymbols::ETYPE_name(), vmSymbols::class_signature(), &fd); + // static final Class CTYPE; + Klass* holder = ik->find_field(vmSymbols::CTYPE_name(), vmSymbols::class_signature(), &fd); assert(holder != nullptr, "sanity"); assert(fd.is_static(), ""); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java index 0f9edbe450c..6c834077387 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java @@ -150,6 +150,14 @@ abstract class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.V int laneTypeOrdinal() { return laneType.ordinal(); } + + @ForceInline + @SuppressWarnings("unchecked") + //NOT FINAL: SPECIALIZED + Class carrierType() { + return (Class) laneType.carrierType; + } + // FIXME: appeal to general method (see https://bugs.openjdk.org/browse/JDK-6176992) // replace usages of this method and remove @ForceInline @@ -326,7 +334,7 @@ abstract class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.V return makeDummyVector(); } private AbstractVector makeDummyVector() { - Object za = Array.newInstance(elementType(), laneCount); + Object za = Array.newInstance(carrierType(), laneCount); return dummyVector = vectorFactory.apply(za); // This is the only use of vectorFactory. // All other factory requests are routed @@ -421,8 +429,7 @@ abstract class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.V Object iotaArray() { // Create an iota array. It's OK if this is really slow, // because it happens only once per species. - Object ia = Array.newInstance(laneType.elementType, - laneCount); + Object ia = Array.newInstance(carrierType(), laneCount); assert(ia.getClass() == laneType.arrayType); checkValue(laneCount-1); // worst case for (int i = 0; i < laneCount; i++) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java index 396165cef5e..c38e8d0f8a0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java @@ -54,6 +54,8 @@ final class ByteVector128 extends ByteVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = byte.class; // carrier type used by the JVM + static final Class ETYPE = byte.class; // used by the JVM ByteVector128(byte[] v) { @@ -92,6 +94,9 @@ final class ByteVector128 extends ByteVector { @Override public final Class elementType() { return byte.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Byte.SIZE; } @@ -596,7 +601,8 @@ final class ByteVector128 extends ByteVector { static final class ByteMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteMask128(boolean[] bits) { this(bits, 0); @@ -828,7 +834,8 @@ final class ByteVector128 extends ByteVector { static final class ByteShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteShuffle128(byte[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java index 5da5dba4a2a..0eec0c56e37 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java @@ -54,6 +54,8 @@ final class ByteVector256 extends ByteVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = byte.class; // carrier type used by the JVM + static final Class ETYPE = byte.class; // used by the JVM ByteVector256(byte[] v) { @@ -92,6 +94,9 @@ final class ByteVector256 extends ByteVector { @Override public final Class elementType() { return byte.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Byte.SIZE; } @@ -628,7 +633,8 @@ final class ByteVector256 extends ByteVector { static final class ByteMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteMask256(boolean[] bits) { this(bits, 0); @@ -860,7 +866,8 @@ final class ByteVector256 extends ByteVector { static final class ByteShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteShuffle256(byte[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java index 8d1b65b4837..138319b60d4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java @@ -54,6 +54,8 @@ final class ByteVector512 extends ByteVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = byte.class; // carrier type used by the JVM + static final Class ETYPE = byte.class; // used by the JVM ByteVector512(byte[] v) { @@ -92,6 +94,9 @@ final class ByteVector512 extends ByteVector { @Override public final Class elementType() { return byte.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Byte.SIZE; } @@ -692,7 +697,8 @@ final class ByteVector512 extends ByteVector { static final class ByteMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteMask512(boolean[] bits) { this(bits, 0); @@ -924,7 +930,8 @@ final class ByteVector512 extends ByteVector { static final class ByteShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteShuffle512(byte[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java index 8b76a54adca..d7c7c78534b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java @@ -54,6 +54,8 @@ final class ByteVector64 extends ByteVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = byte.class; // carrier type used by the JVM + static final Class ETYPE = byte.class; // used by the JVM ByteVector64(byte[] v) { @@ -92,6 +94,9 @@ final class ByteVector64 extends ByteVector { @Override public final Class elementType() { return byte.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Byte.SIZE; } @@ -580,7 +585,8 @@ final class ByteVector64 extends ByteVector { static final class ByteMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteMask64(boolean[] bits) { this(bits, 0); @@ -812,7 +818,8 @@ final class ByteVector64 extends ByteVector { static final class ByteShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteShuffle64(byte[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java index aaf9da19290..636aa83893a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java @@ -54,6 +54,8 @@ final class ByteVectorMax extends ByteVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = byte.class; // carrier type used by the JVM + static final Class ETYPE = byte.class; // used by the JVM ByteVectorMax(byte[] v) { @@ -92,6 +94,9 @@ final class ByteVectorMax extends ByteVector { @Override public final Class elementType() { return byte.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Byte.SIZE; } @@ -566,7 +571,8 @@ final class ByteVectorMax extends ByteVector { static final class ByteMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteMaskMax(boolean[] bits) { this(bits, 0); @@ -798,7 +804,8 @@ final class ByteVectorMax extends ByteVector { static final class ByteShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = byte.class; // used by the JVM + + static final Class CTYPE = byte.class; // used by the JVM ByteShuffleMax(byte[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java index 1cd47ec6f84..1140d377e9b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java @@ -54,6 +54,8 @@ final class DoubleVector128 extends DoubleVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = double.class; // carrier type used by the JVM + static final Class ETYPE = double.class; // used by the JVM DoubleVector128(double[] v) { @@ -92,6 +94,9 @@ final class DoubleVector128 extends DoubleVector { @Override public final Class elementType() { return double.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Double.SIZE; } @@ -557,7 +562,8 @@ final class DoubleVector128 extends DoubleVector { static final class DoubleMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = double.class; // used by the JVM + + static final Class CTYPE = double.class; // used by the JVM DoubleMask128(boolean[] bits) { this(bits, 0); @@ -789,7 +795,8 @@ final class DoubleVector128 extends DoubleVector { static final class DoubleShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM DoubleShuffle128(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java index ab9c6ee0557..59b7913cfcb 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java @@ -54,6 +54,8 @@ final class DoubleVector256 extends DoubleVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = double.class; // carrier type used by the JVM + static final Class ETYPE = double.class; // used by the JVM DoubleVector256(double[] v) { @@ -92,6 +94,9 @@ final class DoubleVector256 extends DoubleVector { @Override public final Class elementType() { return double.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Double.SIZE; } @@ -561,7 +566,8 @@ final class DoubleVector256 extends DoubleVector { static final class DoubleMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = double.class; // used by the JVM + + static final Class CTYPE = double.class; // used by the JVM DoubleMask256(boolean[] bits) { this(bits, 0); @@ -793,7 +799,8 @@ final class DoubleVector256 extends DoubleVector { static final class DoubleShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM DoubleShuffle256(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java index 93477cd7470..8ed21953394 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java @@ -54,6 +54,8 @@ final class DoubleVector512 extends DoubleVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = double.class; // carrier type used by the JVM + static final Class ETYPE = double.class; // used by the JVM DoubleVector512(double[] v) { @@ -92,6 +94,9 @@ final class DoubleVector512 extends DoubleVector { @Override public final Class elementType() { return double.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Double.SIZE; } @@ -569,7 +574,8 @@ final class DoubleVector512 extends DoubleVector { static final class DoubleMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = double.class; // used by the JVM + + static final Class CTYPE = double.class; // used by the JVM DoubleMask512(boolean[] bits) { this(bits, 0); @@ -801,7 +807,8 @@ final class DoubleVector512 extends DoubleVector { static final class DoubleShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM DoubleShuffle512(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java index 2b55198a050..7e1a8cf768d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java @@ -54,6 +54,8 @@ final class DoubleVector64 extends DoubleVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = double.class; // carrier type used by the JVM + static final Class ETYPE = double.class; // used by the JVM DoubleVector64(double[] v) { @@ -92,6 +94,9 @@ final class DoubleVector64 extends DoubleVector { @Override public final Class elementType() { return double.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Double.SIZE; } @@ -555,7 +560,8 @@ final class DoubleVector64 extends DoubleVector { static final class DoubleMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = double.class; // used by the JVM + + static final Class CTYPE = double.class; // used by the JVM DoubleMask64(boolean[] bits) { this(bits, 0); @@ -787,7 +793,8 @@ final class DoubleVector64 extends DoubleVector { static final class DoubleShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM DoubleShuffle64(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java index 15166c6e5e5..46c090e066e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java @@ -54,6 +54,8 @@ final class DoubleVectorMax extends DoubleVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = double.class; // carrier type used by the JVM + static final Class ETYPE = double.class; // used by the JVM DoubleVectorMax(double[] v) { @@ -92,6 +94,9 @@ final class DoubleVectorMax extends DoubleVector { @Override public final Class elementType() { return double.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Double.SIZE; } @@ -554,7 +559,8 @@ final class DoubleVectorMax extends DoubleVector { static final class DoubleMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = double.class; // used by the JVM + + static final Class CTYPE = double.class; // used by the JVM DoubleMaskMax(boolean[] bits) { this(bits, 0); @@ -786,7 +792,8 @@ final class DoubleVectorMax extends DoubleVector { static final class DoubleShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM DoubleShuffleMax(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java index 809e947c6d8..1e3867e84fc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java @@ -54,6 +54,8 @@ final class FloatVector128 extends FloatVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = float.class; // carrier type used by the JVM + static final Class ETYPE = float.class; // used by the JVM FloatVector128(float[] v) { @@ -92,6 +94,9 @@ final class FloatVector128 extends FloatVector { @Override public final Class elementType() { return float.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Float.SIZE; } @@ -561,7 +566,8 @@ final class FloatVector128 extends FloatVector { static final class FloatMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = float.class; // used by the JVM + + static final Class CTYPE = float.class; // used by the JVM FloatMask128(boolean[] bits) { this(bits, 0); @@ -793,7 +799,8 @@ final class FloatVector128 extends FloatVector { static final class FloatShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM FloatShuffle128(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java index 06dcc061a27..f267025972d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java @@ -54,6 +54,8 @@ final class FloatVector256 extends FloatVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = float.class; // carrier type used by the JVM + static final Class ETYPE = float.class; // used by the JVM FloatVector256(float[] v) { @@ -92,6 +94,9 @@ final class FloatVector256 extends FloatVector { @Override public final Class elementType() { return float.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Float.SIZE; } @@ -569,7 +574,8 @@ final class FloatVector256 extends FloatVector { static final class FloatMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = float.class; // used by the JVM + + static final Class CTYPE = float.class; // used by the JVM FloatMask256(boolean[] bits) { this(bits, 0); @@ -801,7 +807,8 @@ final class FloatVector256 extends FloatVector { static final class FloatShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM FloatShuffle256(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java index a83a97e1de5..439e26f0d89 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java @@ -54,6 +54,8 @@ final class FloatVector512 extends FloatVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = float.class; // carrier type used by the JVM + static final Class ETYPE = float.class; // used by the JVM FloatVector512(float[] v) { @@ -92,6 +94,9 @@ final class FloatVector512 extends FloatVector { @Override public final Class elementType() { return float.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Float.SIZE; } @@ -585,7 +590,8 @@ final class FloatVector512 extends FloatVector { static final class FloatMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = float.class; // used by the JVM + + static final Class CTYPE = float.class; // used by the JVM FloatMask512(boolean[] bits) { this(bits, 0); @@ -817,7 +823,8 @@ final class FloatVector512 extends FloatVector { static final class FloatShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM FloatShuffle512(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java index 2d7c436f88d..9e81a52d27b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java @@ -54,6 +54,8 @@ final class FloatVector64 extends FloatVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = float.class; // carrier type used by the JVM + static final Class ETYPE = float.class; // used by the JVM FloatVector64(float[] v) { @@ -92,6 +94,9 @@ final class FloatVector64 extends FloatVector { @Override public final Class elementType() { return float.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Float.SIZE; } @@ -557,7 +562,8 @@ final class FloatVector64 extends FloatVector { static final class FloatMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = float.class; // used by the JVM + + static final Class CTYPE = float.class; // used by the JVM FloatMask64(boolean[] bits) { this(bits, 0); @@ -789,7 +795,8 @@ final class FloatVector64 extends FloatVector { static final class FloatShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM FloatShuffle64(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java index e8a7eefb8e3..4813f153544 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java @@ -54,6 +54,8 @@ final class FloatVectorMax extends FloatVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = float.class; // carrier type used by the JVM + static final Class ETYPE = float.class; // used by the JVM FloatVectorMax(float[] v) { @@ -92,6 +94,9 @@ final class FloatVectorMax extends FloatVector { @Override public final Class elementType() { return float.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Float.SIZE; } @@ -554,7 +559,8 @@ final class FloatVectorMax extends FloatVector { static final class FloatMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = float.class; // used by the JVM + + static final Class CTYPE = float.class; // used by the JVM FloatMaskMax(boolean[] bits) { this(bits, 0); @@ -786,7 +792,8 @@ final class FloatVectorMax extends FloatVector { static final class FloatShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM FloatShuffleMax(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java index 09fb0c29127..cc8f31a4bc2 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java @@ -54,6 +54,8 @@ final class IntVector128 extends IntVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = int.class; // carrier type used by the JVM + static final Class ETYPE = int.class; // used by the JVM IntVector128(int[] v) { @@ -92,6 +94,9 @@ final class IntVector128 extends IntVector { @Override public final Class elementType() { return int.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Integer.SIZE; } @@ -572,7 +577,8 @@ final class IntVector128 extends IntVector { static final class IntMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntMask128(boolean[] bits) { this(bits, 0); @@ -804,7 +810,8 @@ final class IntVector128 extends IntVector { static final class IntShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntShuffle128(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java index 33ba1062c1b..0630cd958f2 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java @@ -54,6 +54,8 @@ final class IntVector256 extends IntVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = int.class; // carrier type used by the JVM + static final Class ETYPE = int.class; // used by the JVM IntVector256(int[] v) { @@ -92,6 +94,9 @@ final class IntVector256 extends IntVector { @Override public final Class elementType() { return int.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Integer.SIZE; } @@ -580,7 +585,8 @@ final class IntVector256 extends IntVector { static final class IntMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntMask256(boolean[] bits) { this(bits, 0); @@ -812,7 +818,8 @@ final class IntVector256 extends IntVector { static final class IntShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntShuffle256(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java index 89128b1ff57..92eb5a0f2d2 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java @@ -54,6 +54,8 @@ final class IntVector512 extends IntVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = int.class; // carrier type used by the JVM + static final Class ETYPE = int.class; // used by the JVM IntVector512(int[] v) { @@ -92,6 +94,9 @@ final class IntVector512 extends IntVector { @Override public final Class elementType() { return int.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Integer.SIZE; } @@ -596,7 +601,8 @@ final class IntVector512 extends IntVector { static final class IntMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntMask512(boolean[] bits) { this(bits, 0); @@ -828,7 +834,8 @@ final class IntVector512 extends IntVector { static final class IntShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntShuffle512(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java index 68195064879..c3f92285034 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java @@ -54,6 +54,8 @@ final class IntVector64 extends IntVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = int.class; // carrier type used by the JVM + static final Class ETYPE = int.class; // used by the JVM IntVector64(int[] v) { @@ -92,6 +94,9 @@ final class IntVector64 extends IntVector { @Override public final Class elementType() { return int.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Integer.SIZE; } @@ -568,7 +573,8 @@ final class IntVector64 extends IntVector { static final class IntMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntMask64(boolean[] bits) { this(bits, 0); @@ -800,7 +806,8 @@ final class IntVector64 extends IntVector { static final class IntShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntShuffle64(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java index e3d94a0d06d..8d3c251536c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java @@ -54,6 +54,8 @@ final class IntVectorMax extends IntVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = int.class; // carrier type used by the JVM + static final Class ETYPE = int.class; // used by the JVM IntVectorMax(int[] v) { @@ -92,6 +94,9 @@ final class IntVectorMax extends IntVector { @Override public final Class elementType() { return int.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Integer.SIZE; } @@ -566,7 +571,8 @@ final class IntVectorMax extends IntVector { static final class IntMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntMaskMax(boolean[] bits) { this(bits, 0); @@ -809,7 +815,8 @@ final class IntVectorMax extends IntVector { static final class IntShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = int.class; // used by the JVM + + static final Class CTYPE = int.class; // used by the JVM IntShuffleMax(int[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java index 59844eabb57..c18bbce1f34 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java @@ -35,19 +35,20 @@ import static jdk.incubator.vector.VectorIntrinsics.*; * It caches all sorts of goodies that we can't put on java.lang.Class. */ enum LaneType { - FLOAT(float.class, Float.class, float[].class, 'F', 24, Float.SIZE), - DOUBLE(double.class, Double.class, double[].class, 'F', 53, Double.SIZE), - BYTE(byte.class, Byte.class, byte[].class, 'I', -1, Byte.SIZE), - SHORT(short.class, Short.class, short[].class, 'I', -1, Short.SIZE), - INT(int.class, Integer.class, int[].class, 'I', -1, Integer.SIZE), - LONG(long.class, Long.class, long[].class, 'I', -1, Long.SIZE); + FLOAT(float.class, Float.class, float[].class, 'F', 24, Float.SIZE, float.class), + DOUBLE(double.class, Double.class, double[].class, 'F', 53, Double.SIZE, double.class), + BYTE(byte.class, Byte.class, byte[].class, 'I', -1, Byte.SIZE, byte.class), + SHORT(short.class, Short.class, short[].class, 'I', -1, Short.SIZE, short.class), + INT(int.class, Integer.class, int[].class, 'I', -1, Integer.SIZE, int.class), + LONG(long.class, Long.class, long[].class, 'I', -1, Long.SIZE, long.class); LaneType(Class elementType, Class genericElementType, Class arrayType, char elementKind, int elementPrecision, - int elementSize) { + int elementSize, + Class carrierType) { if (elementPrecision <= 0) elementPrecision += elementSize; this.elementType = elementType; @@ -66,6 +67,9 @@ enum LaneType { // report that condition also. this.typeChar = genericElementType.getSimpleName().charAt(0); assert("FDBSIL".indexOf(typeChar) == ordinal()) : this; + this.carrierType = carrierType; + assert(carrierType.isPrimitive()); + } final Class elementType; @@ -78,6 +82,7 @@ enum LaneType { final int switchKey; // 1+ordinal(), which is non-zero final String printName; final char typeChar; // one of "BSILFD" + final Class carrierType; private @Stable LaneType asIntegral; private @Stable LaneType asFloating; diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java index b629c66df97..f8dad12ff89 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java @@ -54,6 +54,8 @@ final class LongVector128 extends LongVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = long.class; // carrier type used by the JVM + static final Class ETYPE = long.class; // used by the JVM LongVector128(long[] v) { @@ -92,6 +94,9 @@ final class LongVector128 extends LongVector { @Override public final Class elementType() { return long.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Long.SIZE; } @@ -558,7 +563,8 @@ final class LongVector128 extends LongVector { static final class LongMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongMask128(boolean[] bits) { this(bits, 0); @@ -790,7 +796,8 @@ final class LongVector128 extends LongVector { static final class LongShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongShuffle128(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java index a1cfe199ed2..144e2c1c64d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java @@ -54,6 +54,8 @@ final class LongVector256 extends LongVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = long.class; // carrier type used by the JVM + static final Class ETYPE = long.class; // used by the JVM LongVector256(long[] v) { @@ -92,6 +94,9 @@ final class LongVector256 extends LongVector { @Override public final Class elementType() { return long.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Long.SIZE; } @@ -562,7 +567,8 @@ final class LongVector256 extends LongVector { static final class LongMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongMask256(boolean[] bits) { this(bits, 0); @@ -794,7 +800,8 @@ final class LongVector256 extends LongVector { static final class LongShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongShuffle256(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java index 3036ed8c06e..b49d0c7c147 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java @@ -54,6 +54,8 @@ final class LongVector512 extends LongVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = long.class; // carrier type used by the JVM + static final Class ETYPE = long.class; // used by the JVM LongVector512(long[] v) { @@ -92,6 +94,9 @@ final class LongVector512 extends LongVector { @Override public final Class elementType() { return long.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Long.SIZE; } @@ -570,7 +575,8 @@ final class LongVector512 extends LongVector { static final class LongMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongMask512(boolean[] bits) { this(bits, 0); @@ -802,7 +808,8 @@ final class LongVector512 extends LongVector { static final class LongShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongShuffle512(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java index 201261a83f3..5e8451695bc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java @@ -54,6 +54,8 @@ final class LongVector64 extends LongVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = long.class; // carrier type used by the JVM + static final Class ETYPE = long.class; // used by the JVM LongVector64(long[] v) { @@ -92,6 +94,9 @@ final class LongVector64 extends LongVector { @Override public final Class elementType() { return long.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Long.SIZE; } @@ -556,7 +561,8 @@ final class LongVector64 extends LongVector { static final class LongMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongMask64(boolean[] bits) { this(bits, 0); @@ -788,7 +794,8 @@ final class LongVector64 extends LongVector { static final class LongShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongShuffle64(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java index 2f747299573..3469da8f2f4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java @@ -54,6 +54,8 @@ final class LongVectorMax extends LongVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = long.class; // carrier type used by the JVM + static final Class ETYPE = long.class; // used by the JVM LongVectorMax(long[] v) { @@ -92,6 +94,9 @@ final class LongVectorMax extends LongVector { @Override public final Class elementType() { return long.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Long.SIZE; } @@ -556,7 +561,8 @@ final class LongVectorMax extends LongVector { static final class LongMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongMaskMax(boolean[] bits) { this(bits, 0); @@ -788,7 +794,8 @@ final class LongVectorMax extends LongVector { static final class LongShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = long.class; // used by the JVM + + static final Class CTYPE = long.class; // used by the JVM LongShuffleMax(long[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java index 97df871c9c0..e989cdbdbea 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java @@ -54,6 +54,8 @@ final class ShortVector128 extends ShortVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = short.class; // carrier type used by the JVM + static final Class ETYPE = short.class; // used by the JVM ShortVector128(short[] v) { @@ -92,6 +94,9 @@ final class ShortVector128 extends ShortVector { @Override public final Class elementType() { return short.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Short.SIZE; } @@ -580,7 +585,8 @@ final class ShortVector128 extends ShortVector { static final class ShortMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortMask128(boolean[] bits) { this(bits, 0); @@ -812,7 +818,8 @@ final class ShortVector128 extends ShortVector { static final class ShortShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortShuffle128(short[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java index d3f526a636d..c74188e22f5 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java @@ -54,6 +54,8 @@ final class ShortVector256 extends ShortVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = short.class; // carrier type used by the JVM + static final Class ETYPE = short.class; // used by the JVM ShortVector256(short[] v) { @@ -92,6 +94,9 @@ final class ShortVector256 extends ShortVector { @Override public final Class elementType() { return short.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Short.SIZE; } @@ -596,7 +601,8 @@ final class ShortVector256 extends ShortVector { static final class ShortMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortMask256(boolean[] bits) { this(bits, 0); @@ -828,7 +834,8 @@ final class ShortVector256 extends ShortVector { static final class ShortShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortShuffle256(short[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java index 2d11eedc28c..46b5d652200 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java @@ -54,6 +54,8 @@ final class ShortVector512 extends ShortVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = short.class; // carrier type used by the JVM + static final Class ETYPE = short.class; // used by the JVM ShortVector512(short[] v) { @@ -92,6 +94,9 @@ final class ShortVector512 extends ShortVector { @Override public final Class elementType() { return short.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Short.SIZE; } @@ -628,7 +633,8 @@ final class ShortVector512 extends ShortVector { static final class ShortMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortMask512(boolean[] bits) { this(bits, 0); @@ -860,7 +866,8 @@ final class ShortVector512 extends ShortVector { static final class ShortShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortShuffle512(short[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java index 0ae35404225..66ff3efe522 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java @@ -54,6 +54,8 @@ final class ShortVector64 extends ShortVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = short.class; // carrier type used by the JVM + static final Class ETYPE = short.class; // used by the JVM ShortVector64(short[] v) { @@ -92,6 +94,9 @@ final class ShortVector64 extends ShortVector { @Override public final Class elementType() { return short.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Short.SIZE; } @@ -572,7 +577,8 @@ final class ShortVector64 extends ShortVector { static final class ShortMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortMask64(boolean[] bits) { this(bits, 0); @@ -804,7 +810,8 @@ final class ShortVector64 extends ShortVector { static final class ShortShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortShuffle64(short[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java index 0dfe94c6d61..b9a9b85126b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java @@ -54,6 +54,8 @@ final class ShortVectorMax extends ShortVector { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class CTYPE = short.class; // carrier type used by the JVM + static final Class ETYPE = short.class; // used by the JVM ShortVectorMax(short[] v) { @@ -92,6 +94,9 @@ final class ShortVectorMax extends ShortVector { @Override public final Class elementType() { return short.class; } + @ForceInline + final Class carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return Short.SIZE; } @@ -566,7 +571,8 @@ final class ShortVectorMax extends ShortVector { static final class ShortMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortMaskMax(boolean[] bits) { this(bits, 0); @@ -798,7 +804,8 @@ final class ShortVectorMax extends ShortVector { static final class ShortShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class ETYPE = short.class; // used by the JVM + + static final Class CTYPE = short.class; // used by the JVM ShortShuffleMax(short[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShape.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShape.java index f6e36450ce0..84b58ef789a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShape.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -250,8 +250,8 @@ public enum VectorShape { private static VectorShape computePreferredShape() { int prefBitSize = Integer.MAX_VALUE; for (LaneType type : LaneType.values()) { - Class etype = type.elementType; - prefBitSize = Math.min(prefBitSize, getMaxVectorBitSize(etype)); + Class ctype = type.carrierType; + prefBitSize = Math.min(prefBitSize, getMaxVectorBitSize(ctype)); } // If these assertions fail, we must reconsider our API portability assumptions. assert(prefBitSize >= Double.SIZE && prefBitSize < Integer.MAX_VALUE / Long.SIZE); @@ -264,16 +264,16 @@ public enum VectorShape { /** * Returns the maximum vector bit size for a given element type. * - * @param etype the element type. + * @param ctype the carrier type. * @return the maximum vector bit. */ /*package-private*/ - static int getMaxVectorBitSize(Class etype) { + static int getMaxVectorBitSize(Class ctype) { // VectorSupport.getMaxLaneCount may return -1 if C2 is not enabled, // or a value smaller than the S_64_BIT.vectorBitSize / elementSizeInBits if MaxVectorSize < 16 // If so default to S_64_BIT - int maxLaneCount = VectorSupport.getMaxLaneCount(etype); - int elementSizeInBits = LaneType.of(etype).elementSize; + int maxLaneCount = VectorSupport.getMaxLaneCount(ctype); + int elementSizeInBits = LaneType.of(ctype).elementSize; return Math.max(maxLaneCount * elementSizeInBits, S_64_BIT.vectorBitSize); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template index 7076ed183ea..bbf02f9c6cd 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template @@ -54,6 +54,8 @@ final class $vectortype$ extends $abstractvectortype$ { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM + static final Class<$Carriertype$> CTYPE = $carriertype$.class; // carrier type used by the JVM + static final Class<$Boxtype$> ETYPE = $type$.class; // used by the JVM $vectortype$($type$[] v) { @@ -92,6 +94,9 @@ final class $vectortype$ extends $abstractvectortype$ { @Override public final Class<$Boxtype$> elementType() { return $type$.class; } + @ForceInline + final Class<$Carriertype$> carrierType() { return CTYPE; } + @ForceInline @Override public final int elementSize() { return $Boxtype$.SIZE; } @@ -853,7 +858,8 @@ final class $vectortype$ extends $abstractvectortype$ { static final class $masktype$ extends AbstractMask<$Boxtype$> { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class<$Boxtype$> ETYPE = $type$.class; // used by the JVM + + static final Class<$Carriertype$> CTYPE = $carriertype$.class; // used by the JVM $masktype$(boolean[] bits) { this(bits, 0); @@ -1100,7 +1106,8 @@ final class $vectortype$ extends $abstractvectortype$ { static final class $shuffletype$ extends AbstractShuffle<$Boxtype$> { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM - static final Class<$Boxbitstype$> ETYPE = $bitstype$.class; // used by the JVM + + static final Class<$Boxbitstype$> CTYPE = $bitstype$.class; // used by the JVM $shuffletype$($bitstype$[] indices) { super(indices); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh index 052a7ff2ddb..a8a7ea83625 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh @@ -72,6 +72,8 @@ do fptype=$type Fptype=$Type Boxfptype=$Boxtype + carriertype=$type + Carriertype=$Type case $type in byte) @@ -90,6 +92,7 @@ do ;; int) Boxtype=Integer + Carriertype=Integer Wideboxtype=Integer Boxbitstype=Integer fptype=float @@ -135,6 +138,7 @@ do args="$args -Dbitstype=$bitstype -DBitstype=$Bitstype -DBoxbitstype=$Boxbitstype" args="$args -Dfptype=$fptype -DFptype=$Fptype -DBoxfptype=$Boxfptype" args="$args -DsizeInBytes=$sizeInBytes" + args="$args -Dcarriertype=$carriertype -DCarriertype=$Carriertype" abstractvectortype=${typeprefix}${Type}Vector abstractbitsvectortype=${typeprefix}Vector${Bitstype} From fb097492898d423bca3c723f55f980121f75b8d2 Mon Sep 17 00:00:00 2001 From: Leonid Mesnik Date: Wed, 25 Feb 2026 16:08:30 +0000 Subject: [PATCH 043/636] =?UTF-8?q?8378641:=20Test=20serviceability/jvmti/?= =?UTF-8?q?RedefineClasses/RedefineVerifyError.java=E2=80=8E=20missing=20U?= =?UTF-8?q?nlockDiagnosticVMOptions=20after=208376295?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: ayang, dholmes, syan --- .../jvmti/RedefineClasses/RedefineVerifyError.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.java index eeedd9b583c..340207071e0 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineVerifyError.java @@ -42,6 +42,7 @@ * @run main/othervm/timeout=180 * -javaagent:redefineagent.jar * -Xlog:class+init,exceptions + * -XX:+UnlockDiagnosticVMOptions * -XX:-BytecodeVerificationRemote * RedefineVerifyError */ From 0ab8a85e87cb607c48a45900550998f0d36cf761 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 25 Feb 2026 17:37:09 +0000 Subject: [PATCH 044/636] 8376152: Test javax/sound/sampled/Clip/bug5070081.java timed out then completed Reviewed-by: syan, aivanov, azvegint --- test/jdk/javax/sound/sampled/Clip/bug5070081.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/jdk/javax/sound/sampled/Clip/bug5070081.java b/test/jdk/javax/sound/sampled/Clip/bug5070081.java index e7bf7e30de2..89e9d591d28 100644 --- a/test/jdk/javax/sound/sampled/Clip/bug5070081.java +++ b/test/jdk/javax/sound/sampled/Clip/bug5070081.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, 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 @@ -31,7 +31,8 @@ import javax.sound.sampled.LineUnavailableException; /* * @test - * @bug 5070081 + * @key sound + * @bug 5070081 8376152 * @summary Tests that javax.sound.sampled.Clip does not loses position through * stop/start */ From 9d4fbbe36d85d71ce850bb83bbfb1ce1d3e8dd23 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 25 Feb 2026 17:43:05 +0000 Subject: [PATCH 045/636] 8374222: jpackage will exit with error if it fails to clean the temp directory Reviewed-by: almatvee --- .../internal/DefaultBundlingEnvironment.java | 4 +- .../jdk/jpackage/internal/TempDirectory.java | 145 ++++- .../resources/MainResources.properties | 3 + .../jpackage/test/PathDeletionPreventer.java | 173 ++++++ .../jpackage/internal/TempDirectoryTest.java | 570 ++++++++++++++++++ 5 files changed, 877 insertions(+), 18 deletions(-) create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PathDeletionPreventer.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/TempDirectoryTest.java diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java index 3a58180aa35..fc51f60aa66 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java @@ -195,11 +195,11 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { public void createBundle(BundlingOperationDescriptor op, Options cmdline) { final var bundler = getBundlerSupplier(op).get().orElseThrow(); Optional permanentWorkDirectory = Optional.empty(); - try (var tempDir = new TempDirectory(cmdline)) { + try (var tempDir = new TempDirectory(cmdline, Globals.instance().objectFactory())) { if (!tempDir.deleteOnClose()) { permanentWorkDirectory = Optional.of(tempDir.path()); } - bundler.accept(tempDir.options()); + bundler.accept(tempDir.map(cmdline)); } catch (IOException ex) { throw new UncheckedIOException(ex); } finally { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/TempDirectory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/TempDirectory.java index 50d1701bf0d..345a42c5051 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/TempDirectory.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/TempDirectory.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 @@ -26,29 +26,46 @@ package jdk.jpackage.internal; import java.io.Closeable; import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardOption; import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.Slot; final class TempDirectory implements Closeable { - TempDirectory(Options options) throws IOException { - final var tempDir = StandardOption.TEMP_ROOT.findIn(options); - if (tempDir.isPresent()) { - this.path = tempDir.orElseThrow(); - this.options = options; - } else { - this.path = Files.createTempDirectory("jdk.jpackage"); - this.options = options.copyWithDefaultValue(StandardOption.TEMP_ROOT, path); - } - - deleteOnClose = tempDir.isEmpty(); + TempDirectory(Options options, RetryExecutorFactory retryExecutorFactory) throws IOException { + this(StandardOption.TEMP_ROOT.findIn(options), retryExecutorFactory); } - Options options() { - return options; + TempDirectory(Optional tempDir, RetryExecutorFactory retryExecutorFactory) throws IOException { + this(tempDir.isEmpty() ? Files.createTempDirectory("jdk.jpackage") : tempDir.get(), + tempDir.isEmpty(), + retryExecutorFactory); + } + + TempDirectory(Path tempDir, boolean deleteOnClose, RetryExecutorFactory retryExecutorFactory) throws IOException { + this.path = Objects.requireNonNull(tempDir); + this.deleteOnClose = deleteOnClose; + this.retryExecutorFactory = Objects.requireNonNull(retryExecutorFactory); + } + + Options map(Options options) { + if (deleteOnClose) { + return options.copyWithDefaultValue(StandardOption.TEMP_ROOT, path); + } else { + return options; + } } Path path() { @@ -62,11 +79,107 @@ final class TempDirectory implements Closeable { @Override public void close() throws IOException { if (deleteOnClose) { - FileUtils.deleteRecursive(path); + retryExecutorFactory.retryExecutor(IOException.class) + .setMaxAttemptsCount(5) + .setAttemptTimeout(2, TimeUnit.SECONDS) + .setExecutable(context -> { + try { + FileUtils.deleteRecursive(path); + } catch (IOException ex) { + if (!context.isLastAttempt()) { + throw ex; + } else { + // Collect the list of leftover files. Collect at most the first 100 files. + var remainingFiles = DirectoryListing.listFilesAndEmptyDirectories( + path, MAX_REPORTED_UNDELETED_FILE_COUNT).paths(); + + if (remainingFiles.equals(List.of(path))) { + Log.info(I18N.format("warning.tempdir.cleanup-failed", path)); + } else { + remainingFiles.forEach(file -> { + Log.info(I18N.format("warning.tempdir.cleanup-file-failed", file)); + }); + } + + Log.verbose(ex); + } + } + return null; + }).execute(); + } + } + + record DirectoryListing(List paths, boolean complete) { + DirectoryListing { + Objects.requireNonNull(paths); + } + + static DirectoryListing listFilesAndEmptyDirectories(Path path, int limit) { + Objects.requireNonNull(path); + if (limit < 0) { + throw new IllegalArgumentException(); + } else if (limit == 0) { + return new DirectoryListing(List.of(), !Files.exists(path)); + } + + var paths = new ArrayList(); + var stopped = Slot.createEmpty(); + + stopped.set(false); + + try { + Files.walkFileTree(path, new FileVisitor<>() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + try (var walk = Files.walk(dir)) { + if (walk.skip(1).findAny().isEmpty()) { + // This is an empty directory, add it to the list. + return addPath(dir, FileVisitResult.SKIP_SUBTREE); + } + } catch (IOException ex) { + Log.verbose(ex); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + return addPath(file, FileVisitResult.CONTINUE); + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + return addPath(file, FileVisitResult.CONTINUE); + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) { + return FileVisitResult.CONTINUE; + } + + private FileVisitResult addPath(Path v, FileVisitResult result) { + if (paths.size() < limit) { + paths.add(v); + return result; + } else { + stopped.set(true); + } + return FileVisitResult.TERMINATE; + } + + }); + } catch (IOException ex) { + Log.verbose(ex); + } + + return new DirectoryListing(Collections.unmodifiableList(paths), !stopped.get()); } } private final Path path; - private final Options options; private final boolean deleteOnClose; + private final RetryExecutorFactory retryExecutorFactory; + + private final static int MAX_REPORTED_UNDELETED_FILE_COUNT = 100; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index 6e5de3d9729..233067d6457 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -109,6 +109,9 @@ error.tool-not-found.advice=Please install "{0}" error.tool-old-version=Can not find "{0}" {1} or newer error.tool-old-version.advice=Please install "{0}" {1} or newer +warning.tempdir.cleanup-failed=Warning: Failed to clean-up temporary directory {0} +warning.tempdir.cleanup-file-failed=Warning: Failed to delete "{0}" file in the temporary directory + error.output-bundle-cannot-be-overwritten=Output package file "{0}" exists and can not be removed. error.blocked.option=jlink option [{0}] is not permitted in --jlink-options diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PathDeletionPreventer.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PathDeletionPreventer.java new file mode 100644 index 00000000000..d16b63d9c69 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PathDeletionPreventer.java @@ -0,0 +1,173 @@ +/* + * 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 jdk.jpackage.test; + +import java.io.Closeable; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileLock; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.SetBuilder; + +/** + * Path deletion preventer. Encapsulates platform-specifics of how to make a + * file or a directory non-deletable. + *

+ * Implementation should be sufficient to make {@code Files#delete(Path)} + * applied to the path protected from deletion throw. + */ +public sealed interface PathDeletionPreventer { + + enum Implementation { + /** + * Uses Java file lock to prevent deletion of a file. + * Works on Windows. Doesn't work on Linux and macOS. + */ + FILE_CHANNEL_LOCK, + + /** + * Removes write permission from a non-empty directory to prevent its deletion. + * Works on Linux and macOS. Doesn't work on Windows. + */ + READ_ONLY_NON_EMPTY_DIRECTORY, + ; + } + + Implementation implementation(); + + Closeable preventPathDeletion(Path path) throws IOException; + + enum FileChannelLockPathDeletionPreventer implements PathDeletionPreventer { + INSTANCE; + + @Override + public Implementation implementation() { + return Implementation.FILE_CHANNEL_LOCK; + } + + @Override + public Closeable preventPathDeletion(Path path) throws IOException { + return new UndeletablePath(path); + } + + private static final class UndeletablePath implements Closeable { + + UndeletablePath(Path file) throws IOException { + var fos = new FileOutputStream(Objects.requireNonNull(file).toFile()); + boolean lockCreated = false; + try { + this.lock = fos.getChannel().lock(); + this.fos = fos; + lockCreated = true; + } finally { + if (!lockCreated) { + fos.close(); + } + } + } + + @Override + public void close() throws IOException { + try { + lock.close(); + } finally { + fos.close(); + } + } + + private final FileOutputStream fos; + private final FileLock lock; + } + } + + enum ReadOnlyDirectoryPathDeletionPreventer implements PathDeletionPreventer { + INSTANCE; + + @Override + public Implementation implementation() { + return Implementation.READ_ONLY_NON_EMPTY_DIRECTORY; + } + + @Override + public Closeable preventPathDeletion(Path path) throws IOException { + return new UndeletablePath(path); + } + + private static final class UndeletablePath implements Closeable { + + UndeletablePath(Path dir) throws IOException { + this.dir = Objects.requireNonNull(dir); + + // Deliberately don't use Files#createDirectories() as don't want to create missing directories. + try { + Files.createDirectory(dir); + Files.createFile(dir.resolve("empty")); + } catch (FileAlreadyExistsException ex) { + } + + perms = Files.getPosixFilePermissions(dir); + if (perms.contains(PosixFilePermission.OWNER_WRITE)) { + Files.setPosixFilePermissions(dir, SetBuilder.build() + .add(perms) + .remove(PosixFilePermission.OWNER_WRITE) + .emptyAllowed(true) + .create()); + } + } + + @Override + public void close() throws IOException { + if (perms.contains(PosixFilePermission.OWNER_WRITE)) { + Files.setPosixFilePermissions(dir, perms); + } + } + + private final Path dir; + private final Set perms; + } + } + + static final PathDeletionPreventer DEFAULT = new Supplier() { + + @Override + public PathDeletionPreventer get() { + switch (OperatingSystem.current()) { + case WINDOWS -> { + return FileChannelLockPathDeletionPreventer.INSTANCE; + } + default -> { + return ReadOnlyDirectoryPathDeletionPreventer.INSTANCE; + } + } + } + + }.get(); +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/TempDirectoryTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/TempDirectoryTest.java new file mode 100644 index 00000000000..221e7d9f433 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/TempDirectoryTest.java @@ -0,0 +1,570 @@ +/* + * 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 jdk.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardOption; +import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.RetryExecutor; +import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.test.PathDeletionPreventer; +import jdk.jpackage.test.PathDeletionPreventer.ReadOnlyDirectoryPathDeletionPreventer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +public class TempDirectoryTest { + + @Test + void test_directory_use(@TempDir Path tempDirPath) throws IOException { + try (var tempDir = new TempDirectory(Optional.of(tempDirPath), RetryExecutorFactory.DEFAULT)) { + assertEquals(tempDir.path(), tempDirPath); + assertFalse(tempDir.deleteOnClose()); + + var cmdline = Options.of(Map.of()); + assertSame(cmdline, tempDir.map(cmdline)); + } + + assertTrue(Files.isDirectory(tempDirPath)); + } + + @Test + void test_directory_new() throws IOException { + var tempDir = new TempDirectory(Optional.empty(), RetryExecutorFactory.DEFAULT); + try (tempDir) { + assertTrue(Files.isDirectory(tempDir.path())); + assertTrue(tempDir.deleteOnClose()); + + var cmdline = Options.of(Map.of()); + var mappedCmdline = tempDir.map(cmdline); + assertEquals(tempDir.path(), StandardOption.TEMP_ROOT.getFrom(mappedCmdline)); + } + + assertFalse(Files.isDirectory(tempDir.path())); + } + + @SuppressWarnings("try") + @ParameterizedTest + @MethodSource + void test_close(CloseType closeType, @TempDir Path root) { + Globals.main(ThrowingSupplier.toSupplier(() -> { + test_close_impl(closeType, root); + return 0; + })); + } + + @ParameterizedTest + @MethodSource + void test_DirectoryListing_listFilesAndEmptyDirectories( + ListFilesAndEmptyDirectoriesTestSpec test, @TempDir Path root) throws IOException { + test.run(root); + } + + @Test + void test_DirectoryListing_listFilesAndEmptyDirectories_negative(@TempDir Path root) throws IOException { + assertThrowsExactly(IllegalArgumentException.class, () -> { + TempDirectory.DirectoryListing.listFilesAndEmptyDirectories(root, -1); + }); + } + + @ParameterizedTest + @CsvSource({"100", "101", "1", "0"}) + void test_DirectoryListing_listFilesAndEmptyDirectories_nonexistent(int limit, @TempDir Path root) throws IOException { + + var path = root.resolve("foo"); + + var listing = TempDirectory.DirectoryListing.listFilesAndEmptyDirectories(path, limit); + assertTrue(listing.complete()); + + List expected; + if (limit == 0) { + expected = List.of(); + } else { + expected = List.of(path); + } + assertEquals(expected, listing.paths()); + } + + private static Stream test_close() { + switch (PathDeletionPreventer.DEFAULT.implementation()) { + case READ_ONLY_NON_EMPTY_DIRECTORY -> { + return Stream.of(CloseType.values()); + } + default -> { + return Stream.of(CloseType.values()) + .filter(Predicate.not(Set.of( + CloseType.FAIL_NO_LEFTOVER_FILES, + CloseType.FAIL_NO_LEFTOVER_FILES_VERBOSE)::contains)); + } + } + } + + @SuppressWarnings({ "try" }) + private void test_close_impl(CloseType closeType, Path root) throws IOException { + var logSink = new StringWriter(); + var logPrintWriter = new PrintWriter(logSink, true); + Globals.instance().loggerOutputStreams(logPrintWriter, logPrintWriter); + if (closeType.isVerbose()) { + Globals.instance().loggerVerbose(); + } + + final var workDir = root.resolve("workdir"); + Files.createDirectories(workDir); + + final Path leftoverPath; + final TempDirectory tempDir; + + switch (closeType) { + case FAIL_NO_LEFTOVER_FILES_VERBOSE, FAIL_NO_LEFTOVER_FILES -> { + leftoverPath = workDir; + tempDir = new TempDirectory(workDir, true, new RetryExecutorFactory() { + @Override + public RetryExecutor retryExecutor(Class exceptionType) { + return new RetryExecutor(exceptionType).setSleepFunction(_ -> {}); + } + }); + + // Lock the parent directory of the work directory and don't create any files in the work directory. + // This should trigger the error message about the failure to delete the empty work directory. + try (var lockWorkDir = ReadOnlyDirectoryPathDeletionPreventer.INSTANCE.preventPathDeletion(workDir.getParent())) { + tempDir.close(); + } + } + default -> { + Files.createFile(workDir.resolve("b")); + + final var lockedPath = workDir.resolve("a"); + switch (PathDeletionPreventer.DEFAULT.implementation()) { + case FILE_CHANNEL_LOCK -> { + Files.createFile(lockedPath); + leftoverPath = lockedPath; + } + case READ_ONLY_NON_EMPTY_DIRECTORY -> { + Files.createDirectories(lockedPath); + leftoverPath = lockedPath.resolve("a"); + Files.createFile(leftoverPath); + } + default -> { + throw new AssertionError(); + } + } + + tempDir = new TempDirectory(workDir, true, new RetryExecutorFactory() { + @Override + public RetryExecutor retryExecutor(Class exceptionType) { + var config = new RetryExecutorMock.Config(lockedPath, closeType.isSuccess()); + return new RetryExecutorMock<>(exceptionType, config); + } + }); + + tempDir.close(); + } + } + + logPrintWriter.flush(); + var logMessages = new BufferedReader(new StringReader(logSink.toString())).lines().toList(); + + assertTrue(Files.isDirectory(root)); + + if (closeType.isSuccess()) { + assertFalse(Files.exists(tempDir.path())); + assertEquals(List.of(), logMessages); + } else { + assertTrue(Files.isDirectory(tempDir.path())); + assertTrue(Files.exists(leftoverPath)); + assertFalse(Files.exists(tempDir.path().resolve("b"))); + + String errMessage; + switch (closeType) { + case FAIL_SOME_LEFTOVER_FILES_VERBOSE, FAIL_SOME_LEFTOVER_FILES -> { + errMessage = "warning.tempdir.cleanup-file-failed"; + } + case FAIL_NO_LEFTOVER_FILES_VERBOSE, FAIL_NO_LEFTOVER_FILES -> { + errMessage = "warning.tempdir.cleanup-failed"; + } + default -> { + throw new AssertionError(); + } + } + assertEquals(List.of(I18N.format(errMessage, leftoverPath)), logMessages.subList(0, 1)); + + if (closeType.isVerbose()) { + // Check the log contains a stacktrace + assertNotEquals(1, logMessages.size()); + } + FileUtils.deleteRecursive(tempDir.path()); + } + } + + private static Collection test_DirectoryListing_listFilesAndEmptyDirectories() { + + var testCases = new ArrayList(); + + Supplier builder = ListFilesAndEmptyDirectoriesTestSpec::build; + + Stream.of( + builder.get().dirs("").complete(), + builder.get().dirs("").limit(0), + builder.get().dirs("foo").complete(), + builder.get().dirs("foo").limit(0), + builder.get().dirs("foo").limit(1).complete(), + builder.get().dirs("foo").limit(2).complete(), + builder.get().dirs("a/b/c").files("foo").files("b/b", "b/c").complete(), + builder.get().dirs("a/b/c").files("foo").files("b/b", "b/c").limit(4).complete(), + builder.get().dirs("a/b/c").files("foo").files("b/b", "b/c").limit(3) + ).map(ListFilesAndEmptyDirectoriesTestSpec.Builder::create).forEach(testCases::add); + + if (!OperatingSystem.isWindows()) { + Stream.of( + // A directory with the sibling symlink pointing to this directory + builder.get().dirs("foo").symlink("foo-symlink", "foo").complete(), + // A file with the sibling symlink pointing to this file + builder.get().symlink("foo-symlink", "foo").files("foo").complete(), + // A dangling symlink + builder.get().nonexistent("foo/bar/buz").symlink("dangling-symlink", "foo/bar/buz").complete() + ).map(ListFilesAndEmptyDirectoriesTestSpec.Builder::create).forEach(testCases::add); + } + + return testCases; + } + + enum CloseType { + SUCCEED, + FAIL_SOME_LEFTOVER_FILES, + FAIL_SOME_LEFTOVER_FILES_VERBOSE, + FAIL_NO_LEFTOVER_FILES, + FAIL_NO_LEFTOVER_FILES_VERBOSE, + ; + + boolean isSuccess() { + return this == SUCCEED; + } + + boolean isVerbose() { + return name().endsWith("_VERBOSE"); + } + } + + private static final class RetryExecutorMock extends RetryExecutor { + + RetryExecutorMock(Class exceptionType, Config config) { + super(exceptionType); + setSleepFunction(_ -> {}); + this.config = Objects.requireNonNull(config); + } + + @SuppressWarnings({ "try", "unchecked" }) + @Override + public RetryExecutor setExecutable(ThrowingFunction>, T, E> v) { + return super.setExecutable(context -> { + if (context.isLastAttempt() && config.unlockOnLastAttempt()) { + return v.apply(context); + } else { + try (var lock = PathDeletionPreventer.DEFAULT.preventPathDeletion(config.lockedPath())) { + return v.apply(context); + } catch (IOException ex) { + if (exceptionType().isInstance(ex)) { + throw (E)ex; + } else { + throw new AssertionError(); + } + } + } + }); + }; + + private final Config config; + + record Config(Path lockedPath, boolean unlockOnLastAttempt) { + Config { + Objects.requireNonNull(lockedPath); + } + } + } + + sealed interface FileSpec extends Comparable { + Path path(); + Path create(Path root) throws IOException; + public default int compareTo(FileSpec other) { + return path().compareTo(other.path()); + } + + static File file(Path path) { + return new File(path); + } + + static Directory dir(Path path) { + return new Directory(path); + } + + static Nonexistent nonexistent(Path path) { + return new Nonexistent(path); + } + + static Symlink symlink(Path path, FileSpec target) { + return new Symlink(path, target); + } + }; + + record File(Path path) implements FileSpec { + File { + path = normalizePath(path); + if (path.getNameCount() == 0) { + throw new IllegalArgumentException(); + } + } + + @Override + public Path create(Path root) throws IOException { + var resolvedPath = root.resolve(path); + if (!Files.isRegularFile(resolvedPath)) { + Files.createDirectories(resolvedPath.getParent()); + Files.createFile(resolvedPath); + } + return resolvedPath; + } + + @Override + public String toString() { + return String.format("f:%s", path); + } + } + + record Nonexistent(Path path) implements FileSpec { + Nonexistent { + path = normalizePath(path); + if (path.getNameCount() == 0) { + throw new IllegalArgumentException(); + } + } + + @Override + public Path create(Path root) throws IOException { + return root.resolve(path); + } + + @Override + public String toString() { + return String.format("x:%s", path); + } + } + + record Symlink(Path path, FileSpec target) implements FileSpec { + Symlink { + path = normalizePath(path); + if (path.getNameCount() == 0) { + throw new IllegalArgumentException(); + } + Objects.requireNonNull(target); + } + + @Override + public Path create(Path root) throws IOException { + var resolvedPath = root.resolve(path); + var targetPath = target.create(root); + Files.createDirectories(resolvedPath.getParent()); + return Files.createSymbolicLink(resolvedPath, targetPath); + } + + @Override + public String toString() { + return String.format("s:%s->%s", path, target); + } + } + + record Directory(Path path) implements FileSpec { + Directory { + path = normalizePath(path); + } + + @Override + public Path create(Path root) throws IOException { + return Files.createDirectories(root.resolve(path)); + } + + @Override + public String toString() { + return String.format("d:%s", path); + } + } + + private static Path normalizePath(Path path) { + path = path.normalize(); + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + return path; + } + + private record ListFilesAndEmptyDirectoriesTestSpec(Set input, int limit, boolean complete) { + + ListFilesAndEmptyDirectoriesTestSpec { + Objects.requireNonNull(input); + + if (!(input instanceof SortedSet)) { + input = new TreeSet<>(input); + } + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + ListFilesAndEmptyDirectoriesTestSpec create() { + return new ListFilesAndEmptyDirectoriesTestSpec(Set.copyOf(input), limit, complete); + } + + Builder files(String... paths) { + Stream.of(paths).map(Path::of).map(FileSpec::file).forEach(input::add); + return this; + } + + Builder dirs(String... paths) { + Stream.of(paths).map(Path::of).map(FileSpec::dir).forEach(input::add); + return this; + } + + Builder nonexistent(String... paths) { + Stream.of(paths).map(Path::of).map(FileSpec::nonexistent).forEach(input::add); + return this; + } + + Builder symlink(String path, String target) { + Objects.requireNonNull(target); + + var targetSpec = input.stream().filter(v -> { + return v.path().equals(Path.of(target)); + }).findFirst(); + + if (targetSpec.isEmpty()) { + var v = FileSpec.file(Path.of(target)); + input.add(v); + targetSpec = Optional.ofNullable(v); + } + + input.add(FileSpec.symlink(Path.of(path), targetSpec.get())); + + return this; + } + + Builder limit(int v) { + limit = v; + return this; + } + + Builder complete(boolean v) { + complete = v; + return this; + } + + Builder complete() { + return complete(true); + } + + private final Set input = new HashSet<>(); + private int limit = Integer.MAX_VALUE; + private boolean complete; + } + + void run(Path root) throws IOException { + for (var v : input) { + v.create(root); + } + + for (var v : input) { + Predicate validator; + switch (v) { + case File _ -> { + validator = Files::isRegularFile; + } + case Directory _ -> { + validator = Files::isDirectory; + } + case Symlink _ -> { + validator = Files::isSymbolicLink; + } + case Nonexistent _ -> { + validator = Predicate.not(Files::exists); + } + } + assertTrue(validator.test(root.resolve(v.path()))); + } + + var listing = TempDirectory.DirectoryListing.listFilesAndEmptyDirectories(root, limit); + assertEquals(complete, listing.complete()); + + if (complete) { + var actual = listing.paths().stream().peek(p -> { + assertTrue(p.startsWith(root)); + }).map(root::relativize).sorted().toList(); + var expected = input.stream() + .filter(Predicate.not(Nonexistent.class::isInstance)) + .map(FileSpec::path) + .sorted() + .toList(); + assertEquals(expected, actual); + } else { + assertEquals(limit, listing.paths().size()); + } + } + + @Override + public String toString() { + return String.format("%s; limit=%d; complete=%s", input, limit, complete); + } + } +} From 36d67ffd0188070d0fb087beffece15fea4ba956 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 25 Feb 2026 20:09:48 +0000 Subject: [PATCH 046/636] 6434110: Color constructor parameter name is misleading Reviewed-by: prr, aivanov --- .../share/classes/java/awt/Color.java | 57 +++++++++------- .../ColorClass/ColorARGBConstructorTest.java | 66 +++++++++++++++++++ 2 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 test/jdk/java/awt/ColorClass/ColorARGBConstructorTest.java diff --git a/src/java.desktop/share/classes/java/awt/Color.java b/src/java.desktop/share/classes/java/awt/Color.java index 923afb91866..e129cbae23a 100644 --- a/src/java.desktop/share/classes/java/awt/Color.java +++ b/src/java.desktop/share/classes/java/awt/Color.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -334,7 +334,7 @@ public class Color implements Paint, java.io.Serializable { * The actual color used in rendering depends * on finding the best match given the color space * available for a given output device. - * Alpha is defaulted to 255. + * Alpha defaults to 255. * * @throws IllegalArgumentException if {@code r}, {@code g} * or {@code b} are outside of the range @@ -378,12 +378,15 @@ public class Color implements Paint, java.io.Serializable { } /** - * Creates an opaque sRGB color with the specified combined RGB value - * consisting of the red component in bits 16-23, the green component - * in bits 8-15, and the blue component in bits 0-7. The actual color - * used in rendering depends on finding the best match given the - * color space available for a particular output device. Alpha is - * defaulted to 255. + * Creates an opaque sRGB color with the specified RGB value consisting of + *

    + *
  • the red component in bits 16-23, + *
  • the green component in bits 8-15, and + *
  • the blue component in bits 0-7. + *
+ * The actual color used in rendering depends on finding the best match + * given the color space available for a particular output device. Alpha + * defaults to 255. * * @param rgb the combined RGB components * @see java.awt.image.ColorModel#getRGBdefault @@ -397,14 +400,17 @@ public class Color implements Paint, java.io.Serializable { } /** - * Creates an sRGB color with the specified combined RGBA value consisting - * of the alpha component in bits 24-31, the red component in bits 16-23, - * the green component in bits 8-15, and the blue component in bits 0-7. - * If the {@code hasalpha} argument is {@code false}, alpha - * is defaulted to 255. + * Creates an sRGB color with the specified ARGB value consisting of + *
    + *
  • the alpha component in bits 24-31, + *
  • the red component in bits 16-23, + *
  • the green component in bits 8-15, and + *
  • the blue component in bits 0-7. + *
+ * If the {@code hasAlpha} argument is {@code false}, alpha defaults to 255. * - * @param rgba the combined RGBA components - * @param hasalpha {@code true} if the alpha bits are valid; + * @param argb the combined ARGB components + * @param hasAlpha {@code true} if the alpha bits are valid; * {@code false} otherwise * @see java.awt.image.ColorModel#getRGBdefault * @see #getRed @@ -413,17 +419,17 @@ public class Color implements Paint, java.io.Serializable { * @see #getAlpha * @see #getRGB */ - public Color(int rgba, boolean hasalpha) { - if (hasalpha) { - value = rgba; + public Color(int argb, boolean hasAlpha) { + if (hasAlpha) { + value = argb; } else { - value = 0xff000000 | rgba; + value = 0xff000000 | argb; } } /** * Creates an opaque sRGB color with the specified red, green, and blue - * values in the range (0.0 - 1.0). Alpha is defaulted to 1.0. The + * values in the range (0.0 - 1.0). Alpha defaults to 1.0. The * actual color used in rendering depends on finding the best * match given the color space available for a particular output * device. @@ -570,9 +576,14 @@ public class Color implements Paint, java.io.Serializable { /** * Returns the RGB value representing the color in the default sRGB - * {@link ColorModel}. - * (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are - * blue). + * {@link ColorModel}, consisting of + *
    + *
  • the alpha component in bits 24-31, + *
  • the red component in bits 16-23, + *
  • the green component in bits 8-15, and + *
  • the blue component in bits 0-7. + *
+ * * @return the RGB value of the color in the default sRGB * {@code ColorModel}. * @see java.awt.image.ColorModel#getRGBdefault diff --git a/test/jdk/java/awt/ColorClass/ColorARGBConstructorTest.java b/test/jdk/java/awt/ColorClass/ColorARGBConstructorTest.java new file mode 100644 index 00000000000..3464e2057b9 --- /dev/null +++ b/test/jdk/java/awt/ColorClass/ColorARGBConstructorTest.java @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com Inc. 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. + */ + +import java.awt.Color; + +/** + * @test + * @bug 6434110 + * @summary Verify Color(int, boolean) constructor uses ARGB bit layout + */ +public final class ColorARGBConstructorTest { + + public static void main(String[] args) { + for (int argb : new int[]{0x00000000, 0x01020304, 0xC0903020, + 0x40302010, 0xD08040C0, 0x80000000, + 0x7FFFFFFF, 0xFFFFFFFF, 0xFF000000, + 0x00FF0000, 0x0000FF00, 0x000000FF}) + { + verify(argb, true); + verify(argb, false); + } + } + + private static void verify(int argb, boolean hasAlpha) { + var c = new Color(argb, hasAlpha); + int expRGB = hasAlpha ? argb : (0xFF000000 | argb); + int expA = hasAlpha ? (argb >>> 24) : 0xFF; + int expR = (argb >> 16) & 0xFF; + int expG = (argb >> 8) & 0xFF; + int expB = argb & 0xFF; + check("RGB", expRGB, c.getRGB(), argb, hasAlpha); + check("Alpha", expA, c.getAlpha(), argb, hasAlpha); + check("Red", expR, c.getRed(), argb, hasAlpha); + check("Green", expG, c.getGreen(), argb, hasAlpha); + check("Blue", expB, c.getBlue(), argb, hasAlpha); + } + + private static void check(String comp, int exp, int got, int argb, + boolean hasAlpha) + { + if (exp != got) { + throw new RuntimeException("0x%08X(%b) %s: 0x%08X != 0x%08X" + .formatted(argb, hasAlpha, comp, exp, got)); + } + } +} From 8ba3de98340dc66fee76a77f2d4721684b618916 Mon Sep 17 00:00:00 2001 From: David Holmes Date: Wed, 25 Feb 2026 20:11:51 +0000 Subject: [PATCH 047/636] 8377948: The ThreadWXEnable use of PerfTraceTime is not safe during VM shutdown Reviewed-by: aph, aartemov, fbredberg --- src/hotspot/share/runtime/perfData.hpp | 27 +++++++++++++++++-- src/hotspot/share/runtime/perfData.inline.hpp | 22 ++++++++++++++- .../share/runtime/threadWXSetters.inline.hpp | 7 +++-- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/runtime/perfData.hpp b/src/hotspot/share/runtime/perfData.hpp index ca1f1f410ad..d910f8662fe 100644 --- a/src/hotspot/share/runtime/perfData.hpp +++ b/src/hotspot/share/runtime/perfData.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -716,7 +716,7 @@ class PerfDataManager : AllStatic { // Utility Classes /* - * this class will administer a PerfCounter used as a time accumulator + * This class will administer a PerfCounter used as a time accumulator * for a basic block much like the TraceTime class. * * Example: @@ -731,6 +731,9 @@ class PerfDataManager : AllStatic { * Note: use of this class does not need to occur within a guarded * block. The UsePerfData guard is used with the implementation * of this class. + * + * But also note this class does not guard against shutdown races - + * see SafePerfTraceTime below. */ class PerfTraceTime : public StackObj { @@ -756,6 +759,26 @@ class PerfTraceTime : public StackObj { } }; +/* A variant of PerfTraceTime that guards against use during shutdown - + * see PerfDataManager::destroy. + */ +class SafePerfTraceTime : public StackObj { + + protected: + elapsedTimer _t; + PerfLongCounter* _timerp; + + public: + inline SafePerfTraceTime(PerfLongCounter* timerp); + + const char* name() const { + assert(_timerp != nullptr, "sanity"); + return _timerp->name(); + } + + inline ~SafePerfTraceTime(); +}; + /* The PerfTraceTimedEvent class is responsible for counting the * occurrence of some event and measuring the elapsed time of * the event in two separate PerfCounter instances. diff --git a/src/hotspot/share/runtime/perfData.inline.hpp b/src/hotspot/share/runtime/perfData.inline.hpp index 5212691b924..ca0b27e25bc 100644 --- a/src/hotspot/share/runtime/perfData.inline.hpp +++ b/src/hotspot/share/runtime/perfData.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -27,6 +27,7 @@ #include "runtime/perfData.hpp" +#include "utilities/globalCounter.inline.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" @@ -50,4 +51,23 @@ inline bool PerfDataManager::exists(const char* name) { } } +inline SafePerfTraceTime::SafePerfTraceTime(PerfLongCounter* timerp) : _timerp(timerp) { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData() || timerp == nullptr) { + return; + } + _t.start(); +} + +inline SafePerfTraceTime::~SafePerfTraceTime() { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData() || !_t.is_active()) { + return; + } + + _t.stop(); + _timerp->inc(_t.ticks()); +} + + #endif // SHARE_RUNTIME_PERFDATA_INLINE_HPP diff --git a/src/hotspot/share/runtime/threadWXSetters.inline.hpp b/src/hotspot/share/runtime/threadWXSetters.inline.hpp index e7e37fcde1b..87f8d38e4dc 100644 --- a/src/hotspot/share/runtime/threadWXSetters.inline.hpp +++ b/src/hotspot/share/runtime/threadWXSetters.inline.hpp @@ -42,7 +42,7 @@ class ThreadWXEnable { public: ThreadWXEnable(WXMode* new_mode, Thread* thread) : _thread(thread), _this_wx_mode(new_mode) { - NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());) + NOT_PRODUCT(SafePerfTraceTime ptt(ClassLoader::perf_change_wx_time());) JavaThread* javaThread = _thread && _thread->is_Java_thread() ? JavaThread::cast(_thread) : nullptr; @@ -55,7 +55,7 @@ public: } ThreadWXEnable(WXMode new_mode, Thread* thread) : _thread(thread), _this_wx_mode(nullptr) { - NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());) + NOT_PRODUCT(SafePerfTraceTime ptt(ClassLoader::perf_change_wx_time());) JavaThread* javaThread = _thread && _thread->is_Java_thread() ? JavaThread::cast(_thread) : nullptr; @@ -68,7 +68,7 @@ public: } ~ThreadWXEnable() { - NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());) + NOT_PRODUCT(SafePerfTraceTime ptt(ClassLoader::perf_change_wx_time());) if (_thread) { _thread->enable_wx(_old_mode); JavaThread* javaThread @@ -86,4 +86,3 @@ public: #endif // MACOS_AARCH64 #endif // SHARE_RUNTIME_THREADWXSETTERS_INLINE_HPP - From 32cc7f1f57aff093841d071b658a09b83f16ef2e Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Wed, 25 Feb 2026 23:02:07 +0000 Subject: [PATCH 048/636] 8377947: Test serviceability/sa/TestJhsdbJstackMixedCore.java failed on linux-x64 Reviewed-by: cjplummer, kevinw --- .../linux/native/libsaproc/DwarfParser.cpp | 4 +- .../linux/native/libsaproc/dwarf.cpp | 81 +++++++++++-------- .../linux/native/libsaproc/dwarf.hpp | 37 ++++----- 3 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp index 6c94992e1e2..62dbc84f88c 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp @@ -205,7 +205,7 @@ extern "C" JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getReturnAddressOffsetFromCFA (JNIEnv *env, jobject this_obj) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); - return parser->get_ra_cfa_offset(); + return parser->get_offset_from_cfa(RA); } /* @@ -217,5 +217,5 @@ extern "C" JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getBasePointerOffsetFromCFA (JNIEnv *env, jobject this_obj) { DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); - return parser->get_bp_cfa_offset(); + return parser->get_offset_from_cfa(RBP); } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp index 2636bdf691a..459e3cc57e9 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp @@ -24,10 +24,32 @@ */ #include +#include #include "dwarf.hpp" #include "libproc_impl.h" +DwarfParser::DwarfParser(lib_info *lib) : _lib(lib), + _buf(NULL), + _encoding(0), + _code_factor(0), + _data_factor(0), + _current_pc(0L) { + init_state(_initial_state); + init_state(_state); +} + +void DwarfParser::init_state(struct DwarfState& st) { + st.cfa_reg = MAX_VALUE; + st.return_address_reg = MAX_VALUE; + st.cfa_offset = 0; + + st.offset_from_cfa.clear(); + for (int reg = 0; reg < MAX_VALUE; reg++) { + st.offset_from_cfa[static_cast(reg)] = INT_MAX; + } +} + /* from read_leb128() in dwarf.c in binutils */ uintptr_t DwarfParser::read_leb(bool sign) { uintptr_t result = 0L; @@ -82,7 +104,7 @@ bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { _code_factor = read_leb(false); _data_factor = static_cast(read_leb(true)); - _return_address_reg = static_cast(*_buf++); + enum DWARF_Register initial_ra = static_cast(*_buf++); if (strpbrk(augmentation_string, "LP") != NULL) { // Language personality routine (P) and Language Specific Data Area (LSDA:L) @@ -99,14 +121,12 @@ bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { // Clear state _current_pc = 0L; - _cfa_reg = MAX_VALUE; - _return_address_reg = RA; - _cfa_offset = 0; - _ra_cfa_offset = 8; - _bp_cfa_offset = INT_MAX; + init_state(_state); + _state.return_address_reg = initial_ra; parse_dwarf_instructions(0L, static_cast(-1L), end); + _initial_state = _state; _buf = orig_pos; return true; } @@ -114,12 +134,7 @@ bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end) { uintptr_t operand1; _current_pc = begin; - - /* for remember state */ - enum DWARF_Register rem_cfa_reg = MAX_VALUE; - int rem_cfa_offset = 0; - int rem_ra_cfa_offset = 8; - int rem_bp_cfa_offset = INT_MAX; + std::stack remember_state; while ((_buf < end) && (_current_pc < pc)) { unsigned char op = *_buf++; @@ -138,21 +153,17 @@ void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const } break; case 0x0c: // DW_CFA_def_cfa - _cfa_reg = static_cast(read_leb(false)); - _cfa_offset = read_leb(false); + _state.cfa_reg = static_cast(read_leb(false)); + _state.cfa_offset = read_leb(false); break; case 0x80: {// DW_CFA_offset operand1 = read_leb(false); enum DWARF_Register reg = static_cast(opa); - if (reg == RBP) { - _bp_cfa_offset = operand1 * _data_factor; - } else if (reg == RA) { - _ra_cfa_offset = operand1 * _data_factor; - } + _state.offset_from_cfa[reg] = operand1 * _data_factor; break; } case 0xe: // DW_CFA_def_cfa_offset - _cfa_offset = read_leb(false); + _state.cfa_offset = read_leb(false); break; case 0x40: // DW_CFA_advance_loc if (_current_pc != 0L) { @@ -184,28 +195,28 @@ void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const } case 0x07: { // DW_CFA_undefined enum DWARF_Register reg = static_cast(read_leb(false)); - // We are only interested in BP here because CFA and RA should not be undefined. - if (reg == RBP) { - _bp_cfa_offset = INT_MAX; - } + _state.offset_from_cfa[reg] = INT_MAX; break; } - case 0x0d: {// DW_CFA_def_cfa_register - _cfa_reg = static_cast(read_leb(false)); + case 0x0d: // DW_CFA_def_cfa_register + _state.cfa_reg = static_cast(read_leb(false)); break; - } case 0x0a: // DW_CFA_remember_state - rem_cfa_reg = _cfa_reg; - rem_cfa_offset = _cfa_offset; - rem_ra_cfa_offset = _ra_cfa_offset; - rem_bp_cfa_offset = _bp_cfa_offset; + remember_state.push(_state); break; case 0x0b: // DW_CFA_restore_state - _cfa_reg = rem_cfa_reg; - _cfa_offset = rem_cfa_offset; - _ra_cfa_offset = rem_ra_cfa_offset; - _bp_cfa_offset = rem_bp_cfa_offset; + if (remember_state.empty()) { + print_debug("DWARF Error: DW_CFA_restore_state with empty stack.\n"); + return; + } + _state = remember_state.top(); + remember_state.pop(); break; + case 0xc0: {// DW_CFA_restore + enum DWARF_Register reg = static_cast(opa); + _state.offset_from_cfa[reg] = _initial_state.offset_from_cfa[reg]; + break; + } default: print_debug("DWARF: Unknown opcode: 0x%x\n", op); return; diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp index a2692738ce1..0a38c9a0f2e 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp @@ -26,6 +26,8 @@ #ifndef _DWARF_HPP_ #define _DWARF_HPP_ +#include + #include "libproc_impl.h" /* @@ -55,6 +57,13 @@ enum DWARF_Register { MAX_VALUE }; +struct DwarfState { + enum DWARF_Register cfa_reg; + enum DWARF_Register return_address_reg; + int cfa_offset; + std::map offset_from_cfa; +}; + /* * DwarfParser finds out CFA (Canonical Frame Address) from DWARF in ELF binary. * Also Return Address (RA) and Base Pointer (BP) are calculated from CFA. @@ -64,16 +73,14 @@ class DwarfParser { const lib_info *_lib; unsigned char *_buf; unsigned char _encoding; - enum DWARF_Register _cfa_reg; - enum DWARF_Register _return_address_reg; unsigned int _code_factor; int _data_factor; uintptr_t _current_pc; - int _cfa_offset; - int _ra_cfa_offset; - int _bp_cfa_offset; + struct DwarfState _initial_state; + struct DwarfState _state; + void init_state(struct DwarfState& st); uintptr_t read_leb(bool sign); uint64_t get_entry_length(); bool process_cie(unsigned char *start_of_entry, uint32_t id); @@ -82,24 +89,12 @@ class DwarfParser { unsigned int get_pc_range(); public: - DwarfParser(lib_info *lib) : _lib(lib), - _buf(NULL), - _encoding(0), - _cfa_reg(MAX_VALUE), - _return_address_reg(RA), - _code_factor(0), - _data_factor(0), - _current_pc(0L), - _cfa_offset(0), - _ra_cfa_offset(8), - _bp_cfa_offset(INT_MAX) {}; - + DwarfParser(lib_info *lib); ~DwarfParser() {} bool process_dwarf(const uintptr_t pc); - enum DWARF_Register get_cfa_register() { return _cfa_reg; } - int get_cfa_offset() { return _cfa_offset; } - int get_ra_cfa_offset() { return _ra_cfa_offset; } - int get_bp_cfa_offset() { return _bp_cfa_offset; } + enum DWARF_Register get_cfa_register() { return _state.cfa_reg; } + int get_cfa_offset() { return _state.cfa_offset; } + int get_offset_from_cfa(enum DWARF_Register reg) { return _state.offset_from_cfa[reg]; } bool is_in(long pc) { return (_lib->exec_start <= pc) && (pc < _lib->exec_end); From abec2124281bd9ffb3c3126b66b7b45dc4d88a79 Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Thu, 26 Feb 2026 00:24:46 +0000 Subject: [PATCH 049/636] 8377011: Shenandoah: assert_bounds should be only called when boundaries have changed Reviewed-by: wkemper, kdnilsen --- .../share/gc/shenandoah/shenandoahFreeSet.cpp | 32 +++++++++++++++---- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 3 ++ test/hotspot/jtreg/ProblemList.txt | 3 -- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index c4fe9103fcb..961800f20d9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -1194,6 +1194,18 @@ void ShenandoahRegionPartitions::assert_bounds() { assert(_humongous_waste[int(ShenandoahFreeSetPartitionId::Mutator)] == young_humongous_waste, "Mutator humongous waste must match"); } + +inline void ShenandoahRegionPartitions::assert_bounds_sanity() { + for (uint8_t i = 0; i < UIntNumPartitions; i++) { + ShenandoahFreeSetPartitionId partition = static_cast(i); + assert(leftmost(partition) == _max || membership(leftmost(partition)) == partition, "Left most boundry must be sane"); + assert(rightmost(partition) == -1 || membership(rightmost(partition)) == partition, "Right most boundry must be sane"); + + assert(leftmost_empty(partition) == _max || leftmost_empty(partition) >= leftmost(partition), "Left most empty must be sane"); + assert(rightmost_empty(partition) == -1 || rightmost_empty(partition) <= rightmost(partition), "Right most empty must be sane"); + } +} + #endif ShenandoahFreeSet::ShenandoahFreeSet(ShenandoahHeap* heap, size_t max_regions) : @@ -1654,6 +1666,12 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // Not old collector alloc, so this is a young collector gclab or shared allocation orig_partition = ShenandoahFreeSetPartitionId::Collector; } + DEBUG_ONLY(bool boundary_changed = false;) + if ((result != nullptr) && in_new_region) { + _partitions.one_region_is_no_longer_empty(orig_partition); + DEBUG_ONLY(boundary_changed = true;) + } + if (alloc_capacity(r) < PLAB::min_size() * HeapWordSize) { // Regardless of whether this allocation succeeded, if the remaining memory is less than PLAB:min_size(), retire this region. // Note that retire_from_partition() increases used to account for waste. @@ -1662,15 +1680,11 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // then retire the region so that subsequent searches can find available memory more quickly. size_t idx = r->index(); - if ((result != nullptr) && in_new_region) { - _partitions.one_region_is_no_longer_empty(orig_partition); - } size_t waste_bytes = _partitions.retire_from_partition(orig_partition, idx, r->used()); + DEBUG_ONLY(boundary_changed = true;) if (req.is_mutator_alloc() && (waste_bytes > 0)) { increase_bytes_allocated(waste_bytes); } - } else if ((result != nullptr) && in_new_region) { - _partitions.one_region_is_no_longer_empty(orig_partition); } switch (orig_partition) { @@ -1711,7 +1725,13 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah default: assert(false, "won't happen"); } - _partitions.assert_bounds(); +#ifdef ASSERT + if (boundary_changed) { + _partitions.assert_bounds(); + } else { + _partitions.assert_bounds_sanity(); + } +#endif return result; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 4e0aea80a9b..91c6024d522 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -402,6 +402,9 @@ public: // idx <= rightmost // } void assert_bounds() NOT_DEBUG_RETURN; + // this checks certain sanity conditions related to the bounds with much less effort than is required to + // more rigorously enforce correctness as is done by assert_bounds() + inline void assert_bounds_sanity() NOT_DEBUG_RETURN; }; // Publicly, ShenandoahFreeSet represents memory that is available to mutator threads. The public capacity(), used(), diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 09c0244addf..ba63f775223 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -96,9 +96,6 @@ gc/TestAllocHumongousFragment.java#g1 8298781 generic-all gc/TestAllocHumongousFragment.java#static 8298781 generic-all gc/shenandoah/oom/TestAllocOutOfMemory.java#large 8344312 linux-ppc64le gc/shenandoah/TestEvilSyncBug.java#generational 8345501 generic-all -gc/shenandoah/TestRetainObjects.java#no-tlab 8361099 generic-all -gc/shenandoah/TestSieveObjects.java#no-tlab 8361099 generic-all -gc/shenandoah/TestSieveObjects.java#no-tlab-genshen 8361099 generic-all ############################################################################# From d6044d3e280dc8bb988a6dd7ab6c9a65b1735608 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Thu, 26 Feb 2026 00:52:37 +0000 Subject: [PATCH 050/636] 8378194: Protect process_pending_interp_only() work with JvmtiThreadState_lock Reviewed-by: amenkov, lmesnik --- src/hotspot/share/prims/jvmtiThreadState.cpp | 5 +++-- src/hotspot/share/prims/jvmtiThreadState.hpp | 11 +++++++---- src/hotspot/share/prims/jvmtiThreadState.inline.hpp | 9 +++++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index f46feb95002..d7accfd9a0a 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -51,7 +51,7 @@ static const int UNKNOWN_STACK_DEPTH = -99; // JvmtiThreadState *JvmtiThreadState::_head = nullptr; -bool JvmtiThreadState::_seen_interp_only_mode = false; +Atomic JvmtiThreadState::_seen_interp_only_mode{false}; JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) : _thread_event_enable() { @@ -321,13 +321,14 @@ void JvmtiThreadState::add_env(JvmtiEnvBase *env) { void JvmtiThreadState::enter_interp_only_mode() { assert(_thread != nullptr, "sanity check"); + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); assert(!is_interp_only_mode(), "entering interp only when in interp only mode"); - _seen_interp_only_mode = true; _thread->set_interp_only_mode(true); invalidate_cur_stack_depth(); } void JvmtiThreadState::leave_interp_only_mode() { + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); assert(is_interp_only_mode(), "leaving interp only when not in interp only mode"); if (_thread == nullptr) { // Unmounted virtual thread updates the saved value. diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 43b568cf1fc..866d8828c4c 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -181,7 +181,7 @@ class JvmtiThreadState : public CHeapObj { inline JvmtiEnvThreadState* head_env_thread_state(); inline void set_head_env_thread_state(JvmtiEnvThreadState* ets); - static bool _seen_interp_only_mode; // interp_only_mode was requested at least once + static Atomic _seen_interp_only_mode; // interp_only_mode was requested at least once public: ~JvmtiThreadState(); @@ -204,15 +204,18 @@ class JvmtiThreadState : public CHeapObj { // Return true if any thread has entered interp_only_mode at any point during the JVMs execution. static bool seen_interp_only_mode() { - return _seen_interp_only_mode; + return _seen_interp_only_mode.load_acquire(); } void add_env(JvmtiEnvBase *env); // The pending_interp_only_mode is set when the interp_only_mode is triggered. // It is cleared by EnterInterpOnlyModeClosure handshake. - bool is_pending_interp_only_mode() { return _pending_interp_only_mode; } - void set_pending_interp_only_mode(bool val) { _pending_interp_only_mode = val; } + bool is_pending_interp_only_mode() { return _pending_interp_only_mode; } + void set_pending_interp_only_mode(bool val) { + _seen_interp_only_mode.release_store(true); + _pending_interp_only_mode = val; + } // Used by the interpreter for fullspeed debugging support bool is_interp_only_mode() { diff --git a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp index 2b060f1a2e4..831a2369a7e 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp @@ -167,8 +167,13 @@ inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* threa inline void JvmtiThreadState::process_pending_interp_only(JavaThread* current) { JvmtiThreadState* state = current->jvmti_thread_state(); - if (state != nullptr && state->is_pending_interp_only_mode()) { - JvmtiEventController::enter_interp_only_mode(state); + if (state != nullptr && seen_interp_only_mode()) { // avoid MutexLocker if possible + MutexLocker mu(JvmtiThreadState_lock); + if (state->is_pending_interp_only_mode()) { + assert(state->get_thread() == current, "sanity check"); + assert(!state->is_interp_only_mode(), "sanity check"); + JvmtiEventController::enter_interp_only_mode(state); + } } } #endif // SHARE_PRIMS_JVMTITHREADSTATE_INLINE_HPP From fd74232d5dc4c6bfbcddb82e1b2621289aa2f65a Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Thu, 26 Feb 2026 03:32:19 +0000 Subject: [PATCH 051/636] 8377526: Update Libpng to 1.6.55 Reviewed-by: azvegint, prr, serb --- src/java.desktop/share/legal/libpng.md | 3 ++- .../share/native/libsplashscreen/libpng/CHANGES | 12 +++++++++--- .../share/native/libsplashscreen/libpng/README | 14 +++++++------- .../share/native/libsplashscreen/libpng/png.c | 4 ++-- .../share/native/libsplashscreen/libpng/png.h | 14 +++++++------- .../share/native/libsplashscreen/libpng/pngconf.h | 2 +- .../native/libsplashscreen/libpng/pnglibconf.h | 2 +- .../share/native/libsplashscreen/libpng/pngrtran.c | 6 +++--- 8 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index 80d12248ec4..a2ffcca1974 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.54 +## libpng v1.6.55 ### libpng License
@@ -170,6 +170,7 @@ Authors, for copyright and licensing purposes.
  * Guy Eric Schalnat
  * James Yu
  * John Bowler
+ * Joshua Inscoe
  * Kevin Bracey
  * Lucas Chollet
  * Magnus Holmgren
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
index 3bb1baecd23..af9fcff6eb3 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
@@ -5988,7 +5988,7 @@ Version 1.6.32rc01 [August 18, 2017]
 
 Version 1.6.32rc02 [August 22, 2017]
   Added contrib/oss-fuzz directory which contains files used by the oss-fuzz
-    project (https://github.com/google/oss-fuzz/tree/master/projects/libpng).
+    project .
 
 Version 1.6.32 [August 24, 2017]
   No changes.
@@ -6323,15 +6323,21 @@ Version 1.6.53 [December 5, 2025]
 
 Version 1.6.54 [January 12, 2026]
   Fixed CVE-2026-22695 (medium severity):
-    Heap buffer over-read in `png_image_read_direct_scaled.
+    Heap buffer over-read in `png_image_read_direct_scaled`.
     (Reported and fixed by Petr Simecek.)
   Fixed CVE-2026-22801 (medium severity):
     Integer truncation causing heap buffer over-read in `png_image_write_*`.
   Implemented various improvements in oss-fuzz.
     (Contributed by Philippe Antoine.)
 
+Version 1.6.55 [February 9, 2026]
+  Fixed CVE-2026-25646 (high severity):
+    Heap buffer overflow in `png_set_quantize`.
+    (Reported and fixed by Joshua Inscoe.)
+  Resolved an oss-fuzz build issue involving nalloc.
+    (Contributed by Philippe Antoine.)
 
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
 Subscription is required; visit
-https://lists.sourceforge.net/lists/listinfo/png-mng-implement
+
 to subscribe.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README
index 63d1376edf7..6e0d1e33137 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/README
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/README
@@ -1,4 +1,4 @@
-README for libpng version 1.6.54
+README for libpng version 1.6.55
 ================================
 
 See the note about version numbers near the top of `png.h`.
@@ -24,14 +24,14 @@ for more things than just PNG files.  You can use zlib as a drop-in
 replacement for `fread()` and `fwrite()`, if you are so inclined.
 
 zlib should be available at the same place that libpng is, or at
-https://zlib.net .
+.
 
 You may also want a copy of the PNG specification.  It is available
 as an RFC, a W3C Recommendation, and an ISO/IEC Standard.  You can find
-these at http://www.libpng.org/pub/png/pngdocs.html .
+these at .
 
-This code is currently being archived at https://libpng.sourceforge.io
-in the download area, and at http://libpng.download/src .
+This code is currently being archived at 
+in the download area, and at .
 
 This release, based in a large way on Glenn's, Guy's and Andreas'
 earlier work, was created and will be supported by myself and the PNG
@@ -39,12 +39,12 @@ development group.
 
 Send comments, corrections and commendations to `png-mng-implement`
 at `lists.sourceforge.net`.  (Subscription is required; visit
-https://lists.sourceforge.net/lists/listinfo/png-mng-implement
+
 to subscribe.)
 
 Send general questions about the PNG specification to `png-mng-misc`
 at `lists.sourceforge.net`.  (Subscription is required; visit
-https://lists.sourceforge.net/lists/listinfo/png-mng-misc
+
 to subscribe.)
 
 Historical notes
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
index 5636b4a754e..955fda8dd7e 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
@@ -42,7 +42,7 @@
 #include "pngpriv.h"
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_54 Your_png_h_is_not_version_1_6_54;
+typedef png_libpng_version_1_6_55 Your_png_h_is_not_version_1_6_55;
 
 /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
  * corresponding macro definitions.  This causes a compile time failure if
@@ -849,7 +849,7 @@ png_get_copyright(png_const_structrp png_ptr)
    return PNG_STRING_COPYRIGHT
 #else
    return PNG_STRING_NEWLINE \
-      "libpng version 1.6.54" PNG_STRING_NEWLINE \
+      "libpng version 1.6.55" PNG_STRING_NEWLINE \
       "Copyright (c) 2018-2026 Cosmin Truta" PNG_STRING_NEWLINE \
       "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
       PNG_STRING_NEWLINE \
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
index ab8876a9626..e95c0444399 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.54
+ * libpng version 1.6.55
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
@@ -43,7 +43,7 @@
  *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
  *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
  *     Glenn Randers-Pehrson
- *   libpng versions 1.6.36, December 2018, through 1.6.54, January 2026:
+ *   libpng versions 1.6.36, December 2018, through 1.6.55, February 2026:
  *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
@@ -267,7 +267,7 @@
  *    ...
  *    1.5.30                  15    10530  15.so.15.30[.0]
  *    ...
- *    1.6.54                  16    10654  16.so.16.54[.0]
+ *    1.6.55                  16    10655  16.so.16.55[.0]
  *
  *    Henceforth the source version will match the shared-library major and
  *    minor numbers; the shared-library major version number will be used for
@@ -303,7 +303,7 @@
  */
 
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.54"
+#define PNG_LIBPNG_VER_STRING "1.6.55"
 #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
 
 /* The versions of shared library builds should stay in sync, going forward */
@@ -314,7 +314,7 @@
 /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
 #define PNG_LIBPNG_VER_MAJOR   1
 #define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 54
+#define PNG_LIBPNG_VER_RELEASE 55
 
 /* This should be zero for a public release, or non-zero for a
  * development version.
@@ -345,7 +345,7 @@
  * From version 1.0.1 it is:
  * XXYYZZ, where XX=major, YY=minor, ZZ=release
  */
-#define PNG_LIBPNG_VER 10654 /* 1.6.54 */
+#define PNG_LIBPNG_VER 10655 /* 1.6.55 */
 
 /* Library configuration: these options cannot be changed after
  * the library has been built.
@@ -455,7 +455,7 @@ extern "C" {
 /* This triggers a compiler error in png.c, if png.c and png.h
  * do not agree upon the version number.
  */
-typedef char *png_libpng_version_1_6_54;
+typedef char *png_libpng_version_1_6_55;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
index 959c604edbc..b957f8b5061 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.54
+ * libpng version 1.6.55
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
index b413b510acf..ae1ab462072 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
@@ -31,7 +31,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  */
-/* libpng version 1.6.54 */
+/* libpng version 1.6.55 */
 
 /* Copyright (c) 2018-2026 Cosmin Truta */
 /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
index 7680fe64828..fcce80da1cb 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * Copyright (c) 2018-2025 Cosmin Truta
+ * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
  * Copyright (c) 1996-1997 Andreas Dilger
  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -737,8 +737,8 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
                          break;
 
                      t->next = hash[d];
-                     t->left = (png_byte)i;
-                     t->right = (png_byte)j;
+                     t->left = png_ptr->palette_to_index[i];
+                     t->right = png_ptr->palette_to_index[j];
                      hash[d] = t;
                   }
                }

From 074044c2f37e2da5bb05ea2fc74f6d1e42735ab6 Mon Sep 17 00:00:00 2001
From: Jasmine Karthikeyan 
Date: Thu, 26 Feb 2026 05:15:30 +0000
Subject: [PATCH 052/636] 8342095: Add autovectorizer support for subword
 vector casts

Reviewed-by: epeter, qamai
---
 src/hotspot/share/opto/superword.cpp          |  17 +-
 src/hotspot/share/opto/superword.hpp          |   4 +-
 .../share/opto/superwordVTransformBuilder.cpp |  14 +
 src/hotspot/share/opto/vectornode.cpp         |   7 +
 src/hotspot/share/opto/vectornode.hpp         |   1 +
 src/hotspot/share/opto/vtransform.hpp         |   1 +
 .../jtreg/compiler/c2/TestMinMaxSubword.java  |  17 +-
 .../TestCompatibleUseDefTypeSize.java         | 241 ++++++++++++++++-
 .../loopopts/superword/TestReductions.java    | 248 +++++++++++++++---
 .../TestRotateByteAndShortVector.java         |  22 +-
 .../vectorization/TestSubwordTruncation.java  |  28 +-
 .../runner/ArrayShiftOpTest.java              |   5 +-
 .../runner/ArrayTypeConvertTest.java          |  70 +++--
 .../runner/BasicShortOpTest.java              |  12 +-
 .../bench/vm/compiler/VectorSubword.java      | 201 ++++++++++++++
 15 files changed, 782 insertions(+), 106 deletions(-)
 create mode 100644 test/micro/org/openjdk/bench/vm/compiler/VectorSubword.java

diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp
index 31cc8fa0460..d878b2b1d3d 100644
--- a/src/hotspot/share/opto/superword.cpp
+++ b/src/hotspot/share/opto/superword.cpp
@@ -2214,7 +2214,7 @@ bool SuperWord::is_vector_use(Node* use, int u_idx) const {
     return true;
   }
 
-  if (!is_velt_basic_type_compatible_use_def(use, def)) {
+  if (!is_velt_basic_type_compatible_use_def(use, def, d_pk->size())) {
     return false;
   }
 
@@ -2280,7 +2280,7 @@ Node_List* PackSet::strided_pack_input_at_index_or_null(const Node_List* pack, c
 
 // Check if the output type of def is compatible with the input type of use, i.e. if the
 // types have the same size.
-bool SuperWord::is_velt_basic_type_compatible_use_def(Node* use, Node* def) const {
+bool SuperWord::is_velt_basic_type_compatible_use_def(Node* use, Node* def, const uint pack_size) const {
   assert(in_bb(def) && in_bb(use), "both use and def are in loop");
 
   // Conversions are trivially compatible.
@@ -2306,8 +2306,17 @@ bool SuperWord::is_velt_basic_type_compatible_use_def(Node* use, Node* def) cons
            type2aelembytes(use_bt) == 4;
   }
 
-  // Default case: input size of use equals output size of def.
-  return type2aelembytes(use_bt) == type2aelembytes(def_bt);
+  // Input size of use equals output size of def
+  if (type2aelembytes(use_bt) == type2aelembytes(def_bt)) {
+    return true;
+  }
+
+  // Subword cast: Element sizes differ, but the platform supports a cast to change the def shape to the use shape.
+  if (VectorCastNode::is_supported_subword_cast(def_bt, use_bt, pack_size)) {
+    return true;
+  }
+
+  return false;
 }
 
 // Return nullptr if success, else failure message
diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp
index 9654465220b..4e6fce70e5c 100644
--- a/src/hotspot/share/opto/superword.hpp
+++ b/src/hotspot/share/opto/superword.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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
@@ -653,7 +653,7 @@ private:
   // Is use->in(u_idx) a vector use?
   bool is_vector_use(Node* use, int u_idx) const;
 
-  bool is_velt_basic_type_compatible_use_def(Node* use, Node* def) const;
+  bool is_velt_basic_type_compatible_use_def(Node* use, Node* def, const uint pack_size) const;
 
   bool do_vtransform() const;
 };
diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.cpp b/src/hotspot/share/opto/superwordVTransformBuilder.cpp
index 832e91603d9..f8b8bfe2ed0 100644
--- a/src/hotspot/share/opto/superwordVTransformBuilder.cpp
+++ b/src/hotspot/share/opto/superwordVTransformBuilder.cpp
@@ -254,6 +254,20 @@ VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_i
 
   Node_List* pack_in = _packset.pack_input_at_index_or_null(pack, index);
   if (pack_in != nullptr) {
+    Node* in_p0 = pack_in->at(0);
+    BasicType def_bt = _vloop_analyzer.types().velt_basic_type(in_p0);
+    BasicType use_bt = _vloop_analyzer.types().velt_basic_type(p0);
+
+    // If the use and def types are different, emit a cast node
+    if (use_bt != def_bt && !p0->is_Convert() && VectorCastNode::is_supported_subword_cast(def_bt, use_bt, pack->size())) {
+      VTransformNode* in = get_vtnode(pack_in->at(0));
+      const VTransformVectorNodeProperties properties = VTransformVectorNodeProperties::make_from_pack(pack, _vloop_analyzer);
+      VTransformNode* cast = new (_vtransform.arena()) VTransformElementWiseVectorNode(_vtransform, 2, properties, VectorCastNode::opcode(-1, def_bt));
+      cast->set_req(1, in);
+
+      return cast;
+    }
+
     // Input is a matching pack -> vtnode already exists.
     assert(index != 2 || !VectorNode::is_shift(p0), "shift's count cannot be vector");
     return get_vtnode(pack_in->at(0));
diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp
index ba88ae9496f..d332bf440f6 100644
--- a/src/hotspot/share/opto/vectornode.cpp
+++ b/src/hotspot/share/opto/vectornode.cpp
@@ -1561,6 +1561,13 @@ bool VectorCastNode::implemented(int opc, uint vlen, BasicType src_type, BasicTy
   return false;
 }
 
+bool VectorCastNode::is_supported_subword_cast(BasicType def_bt, BasicType use_bt, const uint pack_size) {
+  assert(def_bt != use_bt, "use and def types must be different");
+
+  // Opcode is only required to disambiguate half float, so we pass -1 as it can't be encountered here.
+  return (is_subword_type(def_bt) || is_subword_type(use_bt)) && VectorCastNode::implemented(-1, pack_size, def_bt, use_bt);
+}
+
 Node* VectorCastNode::Identity(PhaseGVN* phase) {
   if (!in(1)->is_top()) {
     BasicType  in_bt = in(1)->bottom_type()->is_vect()->element_basic_type();
diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp
index edbceb30635..f0d010ee735 100644
--- a/src/hotspot/share/opto/vectornode.hpp
+++ b/src/hotspot/share/opto/vectornode.hpp
@@ -1846,6 +1846,7 @@ class VectorCastNode : public VectorNode {
   static VectorNode* make(int vopc, Node* n1, BasicType bt, uint vlen);
   static int  opcode(int opc, BasicType bt, bool is_signed = true);
   static bool implemented(int opc, uint vlen, BasicType src_type, BasicType dst_type);
+  static bool is_supported_subword_cast(BasicType def_bt, BasicType use_bt, const uint pack_size);
 
   virtual Node* Identity(PhaseGVN* phase);
 };
diff --git a/src/hotspot/share/opto/vtransform.hpp b/src/hotspot/share/opto/vtransform.hpp
index b60c71945e1..2c535eca6d1 100644
--- a/src/hotspot/share/opto/vtransform.hpp
+++ b/src/hotspot/share/opto/vtransform.hpp
@@ -975,4 +975,5 @@ public:
   virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const override;
   NOT_PRODUCT(virtual const char* name() const override { return "StoreVector"; };)
 };
+
 #endif // SHARE_OPTO_VTRANSFORM_HPP
diff --git a/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java b/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java
index a7e90353f90..6b93f00cdb1 100644
--- a/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java
+++ b/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2022, Arm Limited. All rights reserved.
+ * 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
@@ -30,7 +31,7 @@ import java.util.Random;
 
 /*
  * @test
- * @bug 8294816
+ * @bug 8294816 8342095
  * @key randomness
  * @summary Test Math.min/max vectorization miscompilation for integer subwords
  * @library /test/lib /
@@ -58,11 +59,11 @@ public class TestMinMaxSubword {
         }
     }
 
-    // Ensure vector max/min instructions are not generated for integer subword types
-    // as Java APIs for Math.min/max do not support integer subword types and superword
-    // should not generate vectorized Min/Max nodes for them.
+    // Ensure that casts to/from subword types are emitted, as java APIs for Math.min/max do not support integer subword
+    // types and superword should generate int versions and then cast between them.
+
     @Test
-    @IR(failOn = {IRNode.MIN_VI, IRNode.MIN_VF, IRNode.MIN_VD})
+    @IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
     public static void testMinShort() {
         for (int i = 0; i < LENGTH; i++) {
            sb[i] = (short) Math.min(sa[i], val);
@@ -78,7 +79,7 @@ public class TestMinMaxSubword {
     }
 
     @Test
-    @IR(failOn = {IRNode.MAX_VI, IRNode.MAX_VF, IRNode.MAX_VD})
+    @IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
     public static void testMaxShort() {
         for (int i = 0; i < LENGTH; i++) {
             sb[i] = (short) Math.max(sa[i], val);
@@ -93,7 +94,7 @@ public class TestMinMaxSubword {
     }
 
     @Test
-    @IR(failOn = {IRNode.MIN_VI, IRNode.MIN_VF, IRNode.MIN_VD})
+    @IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" })
     public static void testMinByte() {
         for (int i = 0; i < LENGTH; i++) {
            bb[i] = (byte) Math.min(ba[i], val);
@@ -109,7 +110,7 @@ public class TestMinMaxSubword {
     }
 
     @Test
-    @IR(failOn = {IRNode.MAX_VI, IRNode.MAX_VF, IRNode.MAX_VD})
+    @IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" })
     public static void testMaxByte() {
         for (int i = 0; i < LENGTH; i++) {
             bb[i] = (byte) Math.max(ba[i], val);
diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java
index 0e17489e1c8..7399a1ec411 100644
--- a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java
+++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.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
@@ -34,7 +34,7 @@ import java.nio.ByteOrder;
 
 /*
  * @test
- * @bug 8325155
+ * @bug 8325155 8342095
  * @key randomness
  * @summary Test some cases that vectorize after the removal of the alignment boundaries code.
  *          Now, we instead check if use-def connections have compatible type size.
@@ -106,6 +106,22 @@ public class TestCompatibleUseDefTypeSize {
         tests.put("test9",       () -> { return test9(aL.clone(), bD.clone()); });
         tests.put("test10",      () -> { return test10(aL.clone(), bD.clone()); });
         tests.put("test11",      () -> { return test11(aC.clone()); });
+        tests.put("testByteToInt",   () -> { return testByteToInt(aB.clone(), bI.clone()); });
+        tests.put("testByteToShort", () -> { return testByteToShort(aB.clone(), bS.clone()); });
+        tests.put("testByteToChar",  () -> { return testByteToChar(aB.clone(), bC.clone()); });
+        tests.put("testByteToLong",  () -> { return testByteToLong(aB.clone(), bL.clone()); });
+        tests.put("testShortToByte", () -> { return testShortToByte(aS.clone(), bB.clone()); });
+        tests.put("testShortToChar", () -> { return testShortToChar(aS.clone(), bC.clone()); });
+        tests.put("testShortToInt",  () -> { return testShortToInt(aS.clone(), bI.clone()); });
+        tests.put("testShortToLong", () -> { return testShortToLong(aS.clone(), bL.clone()); });
+        tests.put("testIntToShort",  () -> { return testIntToShort(aI.clone(), bS.clone()); });
+        tests.put("testIntToChar",   () -> { return testIntToChar(aI.clone(), bC.clone()); });
+        tests.put("testIntToByte",   () -> { return testIntToByte(aI.clone(), bB.clone()); });
+        tests.put("testIntToLong",   () -> { return testIntToLong(aI.clone(), bL.clone()); });
+        tests.put("testLongToByte",  () -> { return testLongToByte(aL.clone(), bB.clone()); });
+        tests.put("testLongToShort", () -> { return testLongToShort(aL.clone(), bS.clone()); });
+        tests.put("testLongToChar",  () -> { return testLongToChar(aL.clone(), bC.clone()); });
+        tests.put("testLongToInt",   () -> { return testLongToInt(aL.clone(), bI.clone()); });
 
         // Compute gold value for all test methods before compilation
         for (Map.Entry entry : tests.entrySet()) {
@@ -128,7 +144,23 @@ public class TestCompatibleUseDefTypeSize {
                  "test8",
                  "test9",
                  "test10",
-                 "test11"})
+                 "test11",
+                 "testByteToInt",
+                 "testByteToShort",
+                 "testByteToChar",
+                 "testByteToLong",
+                 "testShortToByte",
+                 "testShortToChar",
+                 "testShortToInt",
+                 "testShortToLong",
+                 "testIntToShort",
+                 "testIntToChar",
+                 "testIntToByte",
+                 "testIntToLong",
+                 "testLongToByte",
+                 "testLongToShort",
+                 "testLongToChar",
+                 "testLongToInt"})
     public void runTests() {
         for (Map.Entry entry : tests.entrySet()) {
             String name = entry.getKey();
@@ -328,12 +360,12 @@ public class TestCompatibleUseDefTypeSize {
     }
 
     @Test
-    @IR(counts = {IRNode.STORE_VECTOR, "= 0"},
+    @IR(counts = {IRNode.STORE_VECTOR, "> 0"},
         applyIfPlatform = {"64-bit", "true"},
-        applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"})
+        applyIf = {"AlignVector", "false"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"})
     // "inflate"  method: 1 byte -> 2 byte.
     // Java scalar code has no explicit conversion.
-    // Vector code would need a conversion. We may add this in the future.
     static Object[] test1(byte[] src, char[] dst) {
         for (int i = 0; i < src.length; i++) {
             dst[i] = (char)(src[i]);
@@ -478,4 +510,201 @@ public class TestCompatibleUseDefTypeSize {
         }
         return new Object[]{ a, new char[] { m } };
     }
+
+    // Narrowing
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
+    public Object[] testIntToShort(int[] ints, short[] res) {
+        for (int i = 0; i < ints.length; i++) {
+            res[i] = (short) ints[i];
+        }
+
+        return new Object[] { ints, res };
+    }
+
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_char)", ">0" })
+    public Object[] testIntToChar(int[] ints, char[] res) {
+        for (int i = 0; i < ints.length; i++) {
+            res[i] = (char) ints[i];
+        }
+
+        return new Object[] { ints, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" })
+    public Object[] testIntToByte(int[] ints, byte[] res) {
+        for (int i = 0; i < ints.length; i++) {
+            res[i] = (byte) ints[i];
+        }
+
+        return new Object[] { ints, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_S2B, IRNode.VECTOR_SIZE + "min(max_short, max_byte)", ">0" })
+    public Object[] testShortToByte(short[] shorts, byte[] res) {
+        for (int i = 0; i < shorts.length; i++) {
+            res[i] = (byte) shorts[i];
+        }
+
+        return new Object[] { shorts, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx2", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_L2B, IRNode.VECTOR_SIZE + "min(max_long, max_byte)", ">0" })
+    public Object[] testLongToByte(long[] longs, byte[] res) {
+        for (int i = 0; i < longs.length; i++) {
+            res[i] = (byte) longs[i];
+        }
+
+        return new Object[] { longs, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_L2S, IRNode.VECTOR_SIZE + "min(max_long, max_short)", ">0" })
+    public Object[] testLongToShort(long[] longs, short[] res) {
+        for (int i = 0; i < longs.length; i++) {
+            res[i] = (short) longs[i];
+        }
+
+        return new Object[] { longs, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_L2S, IRNode.VECTOR_SIZE + "min(max_long, max_char)", ">0" })
+    public Object[] testLongToChar(long[] longs, char[] res) {
+        for (int i = 0; i < longs.length; i++) {
+            res[i] = (char) longs[i];
+        }
+
+        return new Object[] { longs, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_L2I, IRNode.VECTOR_SIZE + "min(max_long, max_int)", ">0" })
+    public Object[] testLongToInt(long[] longs, int[] res) {
+        for (int i = 0; i < longs.length; i++) {
+            res[i] = (int) longs[i];
+        }
+
+        return new Object[] { longs, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.STORE_VECTOR, ">0" })
+    public Object[] testShortToChar(short[] shorts, char[] res) {
+        for (int i = 0; i < shorts.length; i++) {
+            res[i] = (char) shorts[i];
+        }
+
+        return new Object[] { shorts, res };
+    }
+
+    // Widening
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_S2I, IRNode.VECTOR_SIZE + "min(max_short, max_int)", ">0" })
+    public Object[] testShortToInt(short[] shorts, int[] res) {
+        for (int i = 0; i < shorts.length; i++) {
+            res[i] = shorts[i];
+        }
+
+        return new Object[] { shorts, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_B2I, IRNode.VECTOR_SIZE + "min(max_byte, max_int)", ">0" })
+    public Object[] testByteToInt(byte[] bytes, int[] res) {
+        for (int i = 0; i < bytes.length; i++) {
+            res[i] = bytes[i];
+        }
+
+        return new Object[] { bytes, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_byte, max_short)", ">0" })
+    public Object[] testByteToShort(byte[] bytes, short[] res) {
+        for (int i = 0; i < bytes.length; i++) {
+            res[i] = bytes[i];
+        }
+
+        return new Object[] { bytes, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_byte, max_char)", ">0" })
+    public Object[] testByteToChar(byte[] bytes, char[] res) {
+        for (int i = 0; i < bytes.length; i++) {
+            res[i] = (char) bytes[i];
+        }
+
+        return new Object[] { bytes, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx2", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_B2L, IRNode.VECTOR_SIZE + "min(max_byte, max_long)", ">0" })
+    public Object[] testByteToLong(byte[] bytes, long[] res) {
+        for (int i = 0; i < bytes.length; i++) {
+            res[i] = bytes[i];
+        }
+
+        return new Object[] { bytes, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_S2L, IRNode.VECTOR_SIZE + "min(max_short, max_long)", ">0" })
+    public Object[] testShortToLong(short[] shorts, long[] res) {
+        for (int i = 0; i < shorts.length; i++) {
+            res[i] = shorts[i];
+        }
+
+        return new Object[] { shorts, res };
+    }
+
+    @Test
+    @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_I2L, IRNode.VECTOR_SIZE + "min(max_int, max_long)", ">0" })
+    public Object[] testIntToLong(int[] ints, long[] res) {
+        for (int i = 0; i < ints.length; i++) {
+            res[i] = ints[i];
+        }
+
+        return new Object[] { ints, res };
+    }
 }
diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java
index 1cd5cfa1e75..e0f44bcdb3b 100644
--- a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java
+++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.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
@@ -23,7 +23,7 @@
 
 /*
  * @test id=no-vectorization
- * @bug 8340093
+ * @bug 8340093 8342095
  * @summary Test vectorization of reduction loops.
  * @library /test/lib /
  * @run driver compiler.loopopts.superword.TestReductions P0
@@ -31,7 +31,7 @@
 
 /*
  * @test id=vanilla
- * @bug 8340093
+ * @bug 8340093 8342095
  * @summary Test vectorization of reduction loops.
  * @library /test/lib /
  * @run driver compiler.loopopts.superword.TestReductions P1
@@ -39,7 +39,7 @@
 
 /*
  * @test id=force-vectorization
- * @bug 8340093
+ * @bug 8340093 8342095
  * @summary Test vectorization of reduction loops.
  * @library /test/lib /
  * @run driver compiler.loopopts.superword.TestReductions P2
@@ -455,7 +455,13 @@ public class TestReductions {
 
     // ---------byte***Simple ------------------------------------------------------------
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.AND_REDUCTION_V, "> 0",
+                  IRNode.AND_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteAndSimple() {
         byte acc = (byte)0xFF; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -466,7 +472,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.OR_REDUCTION_V, "> 0",
+                  IRNode.OR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteOrSimple() {
         byte acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -477,7 +489,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.XOR_REDUCTION_V, "> 0",
+                  IRNode.XOR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteXorSimple() {
         byte acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -510,7 +528,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.MIN_REDUCTION_V, "> 0",
+                  IRNode.MIN_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteMinSimple() {
         byte acc = Byte.MAX_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -521,7 +545,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.MAX_REDUCTION_V, "> 0",
+                  IRNode.MAX_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteMaxSimple() {
         byte acc = Byte.MIN_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -533,7 +563,13 @@ public class TestReductions {
 
     // ---------byte***DotProduct ------------------------------------------------------------
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.AND_REDUCTION_V, "> 0",
+                  IRNode.AND_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteAndDotProduct() {
         byte acc = (byte)0xFF; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -544,7 +580,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.OR_REDUCTION_V, "> 0",
+                  IRNode.OR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteOrDotProduct() {
         byte acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -555,7 +597,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.XOR_REDUCTION_V, "> 0",
+                  IRNode.XOR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteXorDotProduct() {
         byte acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -588,7 +636,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.MIN_REDUCTION_V, "> 0",
+                  IRNode.MIN_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteMinDotProduct() {
         byte acc = Byte.MAX_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -599,7 +653,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.MAX_REDUCTION_V, "> 0",
+                  IRNode.MAX_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteMaxDotProduct() {
         byte acc = Byte.MIN_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -611,7 +671,13 @@ public class TestReductions {
 
     // ---------byte***Big ------------------------------------------------------------
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.AND_REDUCTION_V, "> 0",
+                  IRNode.AND_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteAndBig() {
         byte acc = (byte)0xFF; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -622,7 +688,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.OR_REDUCTION_V, "> 0",
+                  IRNode.OR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteOrBig() {
         byte acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -633,7 +705,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.XOR_REDUCTION_V, "> 0",
+                  IRNode.XOR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteXorBig() {
         byte acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -666,7 +744,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.MIN_REDUCTION_V, "> 0",
+                  IRNode.MIN_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteMinBig() {
         byte acc = Byte.MAX_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -677,7 +761,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_B) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                  IRNode.MAX_REDUCTION_V, "> 0",
+                  IRNode.MAX_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_B,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static byte byteMaxBig() {
         byte acc = Byte.MIN_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -923,7 +1013,13 @@ public class TestReductions {
 
     // ---------short***Simple ------------------------------------------------------------
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.AND_REDUCTION_V, "> 0",
+                  IRNode.AND_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortAndSimple() {
         short acc = (short)0xFFFF; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -934,7 +1030,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.OR_REDUCTION_V, "> 0",
+                  IRNode.OR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortOrSimple() {
         short acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -945,7 +1047,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.XOR_REDUCTION_V, "> 0",
+                  IRNode.XOR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortXorSimple() {
         short acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -978,7 +1086,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.MIN_REDUCTION_V, "> 0",
+                  IRNode.MIN_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortMinSimple() {
         short acc = Short.MAX_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -989,7 +1103,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.MAX_REDUCTION_V, "> 0",
+                  IRNode.MAX_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortMaxSimple() {
         short acc = Short.MIN_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1001,7 +1121,13 @@ public class TestReductions {
 
     // ---------short***DotProduct ------------------------------------------------------------
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.AND_REDUCTION_V, "> 0",
+                  IRNode.AND_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortAndDotProduct() {
         short acc = (short)0xFFFF; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1012,7 +1138,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.OR_REDUCTION_V, "> 0",
+                  IRNode.OR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortOrDotProduct() {
         short acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1023,7 +1155,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.XOR_REDUCTION_V, "> 0",
+                  IRNode.XOR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortXorDotProduct() {
         short acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1056,7 +1194,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.MIN_REDUCTION_V, "> 0",
+                  IRNode.MIN_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortMinDotProduct() {
         short acc = Short.MAX_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1067,7 +1211,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.MAX_REDUCTION_V, "> 0",
+                  IRNode.MAX_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortMaxDotProduct() {
         short acc = Short.MIN_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1079,7 +1229,13 @@ public class TestReductions {
 
     // ---------short***Big ------------------------------------------------------------
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.AND_REDUCTION_V, "> 0",
+                  IRNode.AND_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortAndBig() {
         short acc = (short)0xFFFF; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1090,7 +1246,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.OR_REDUCTION_V, "> 0",
+                  IRNode.OR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortOrBig() {
         short acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1101,7 +1263,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.XOR_REDUCTION_V, "> 0",
+                  IRNode.XOR_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortXorBig() {
         short acc = 0; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1134,7 +1302,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.MIN_REDUCTION_V, "> 0",
+                  IRNode.MIN_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortMinBig() {
         short acc = Short.MAX_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
@@ -1145,7 +1319,13 @@ public class TestReductions {
     }
 
     @Test
-    @IR(failOn = IRNode.LOAD_VECTOR_S) // does not vectorize for now, might in the future.
+    @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                  IRNode.MAX_REDUCTION_V, "> 0",
+                  IRNode.MAX_VI,          "> 0"},
+        applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"},
+        applyIf = {"AutoVectorizationOverrideProfitability", "> 0"})
+    @IR(failOn = IRNode.LOAD_VECTOR_S,
+        applyIf = {"AutoVectorizationOverrideProfitability", "= 0"})
     private static short shortMaxBig() {
         short acc = Short.MIN_VALUE; // neutral element
         for (int i = 0; i < SIZE; i++) {
diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java
index e130fc0ba6c..79cde2f0d26 100644
--- a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java
+++ b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2022, 2025 Loongson Technology Co. Ltd. All rights reserved.
  * Copyright (c) 2025, Rivos Inc. All rights reserved.
+ * 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
@@ -24,7 +25,7 @@
 
 /**
  * @test
- * @bug 8286847 8353600
+ * @bug 8286847 8353600 8342095
  * @key randomness
  * @summary Test vectorization of rotate byte and short
  * @library /test/lib /
@@ -116,11 +117,10 @@ public class TestRotateByteAndShortVector {
         }
     }
 
-    // NOTE: currently, there is no platform supporting RotateLeftV/RotateRightV intrinsic.
-    // If there is some implementation, it could probably in a wrong way which is different
-    // from what java language spec expects.
     @Test
-    @IR(failOn = { IRNode.ROTATE_LEFT_V })
+    @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                   IRNode.ROTATE_LEFT_V, "> 0" },
+        applyIfCPUFeature = {"avx512f", "true"})
     @IR(failOn = { IRNode.ROTATE_RIGHT_V })
     static void testRotateLeftByte(byte[] test, byte[] arr, int shift) {
         for (int i = 0; i < ARRLEN; i++) {
@@ -130,7 +130,9 @@ public class TestRotateByteAndShortVector {
 
     @Test
     @IR(failOn = { IRNode.ROTATE_LEFT_V })
-    @IR(failOn = { IRNode.ROTATE_RIGHT_V })
+    @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0",
+                   IRNode.ROTATE_RIGHT_V, "> 0" },
+        applyIfCPUFeature = {"avx512f", "true"})
     static void testRotateRightByte(byte[] test, byte[] arr, int shift) {
         for (int i = 0; i < ARRLEN; i++) {
             test[i] = (byte) ((arr[i] >>> shift) | (arr[i] << -shift));
@@ -138,7 +140,9 @@ public class TestRotateByteAndShortVector {
     }
 
     @Test
-    @IR(failOn = { IRNode.ROTATE_LEFT_V })
+    @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                   IRNode.ROTATE_LEFT_V, "> 0" },
+        applyIfCPUFeature = {"avx512f", "true"})
     @IR(failOn = { IRNode.ROTATE_RIGHT_V })
     static void testRotateLeftShort(short[] test, short[] arr, int shift) {
         for (int i = 0; i < ARRLEN; i++) {
@@ -148,7 +152,9 @@ public class TestRotateByteAndShortVector {
 
     @Test
     @IR(failOn = { IRNode.ROTATE_LEFT_V })
-    @IR(failOn = { IRNode.ROTATE_RIGHT_V })
+    @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0",
+                   IRNode.ROTATE_RIGHT_V, "> 0" },
+        applyIfCPUFeature = {"avx512f", "true"})
     static void testRotateRightShort(short[] test, short[] arr, int shift) {
         for (int i = 0; i < ARRLEN; i++) {
             test[i] = (short) ((arr[i] >>> shift) | (arr[i] << -shift));
diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java
index 53c1b89a203..29331cc1845 100644
--- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java
+++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.g
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@ import compiler.lib.generators.*;
 
 /*
  * @test
- * @bug 8350177 8362171 8369881
+ * @bug 8350177 8362171 8369881 8342095
  * @summary Ensure that truncation of subword vectors produces correct results
  * @library /test/lib /
  * @run driver compiler.vectorization.TestSubwordTruncation
@@ -73,7 +73,8 @@ public class TestSubwordTruncation {
     // Shorts
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupShortArray")
     public Object[] testShortLeadingZeros(short[] in) {
         short[] res = new short[SIZE];
@@ -98,7 +99,8 @@ public class TestSubwordTruncation {
     }
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupShortArray")
     public Object[] testShortTrailingZeros(short[] in) {
         short[] res = new short[SIZE];
@@ -123,7 +125,8 @@ public class TestSubwordTruncation {
     }
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupShortArray")
     public Object[] testShortReverse(short[] in) {
         short[] res = new short[SIZE];
@@ -148,7 +151,8 @@ public class TestSubwordTruncation {
     }
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupShortArray")
     public Object[] testShortBitCount(short[] in) {
         short[] res = new short[SIZE];
@@ -277,7 +281,8 @@ public class TestSubwordTruncation {
     // Bytes
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupByteArray")
     public Object[] testByteLeadingZeros(byte[] in) {
         byte[] res = new byte[SIZE];
@@ -302,7 +307,8 @@ public class TestSubwordTruncation {
     }
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupByteArray")
     public Object[] testByteTrailingZeros(byte[] in) {
         byte[] res = new byte[SIZE];
@@ -327,7 +333,8 @@ public class TestSubwordTruncation {
     }
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupByteArray")
     public Object[] testByteReverse(byte[] in) {
         byte[] res = new byte[SIZE];
@@ -403,7 +410,8 @@ public class TestSubwordTruncation {
 
 
     @Test
-    @IR(counts = { IRNode.STORE_VECTOR, "=0" })
+    @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" },
+        applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" })
     @Arguments(setup = "setupByteArray")
     public Object[] testByteBitCount(byte[] in) {
         byte[] res = new byte[SIZE];
diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java
index 1f0c8e668b7..a0434411da7 100644
--- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java
+++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java
@@ -247,9 +247,8 @@ public class ArrayShiftOpTest extends VectorizationTestRunner {
     }
 
     @Test
-    // Note that right shift operations on subword expressions cannot be
-    // vectorized since precise type info about signedness is missing.
-    @IR(failOn = {IRNode.STORE_VECTOR})
+    @IR(applyIfCPUFeature = {"avx", "true"},
+            counts = {IRNode.RSHIFT_VI, ">0"})
     public short[] subwordExpressionRightShift() {
         short[] res = new short[SIZE];
         for (int i = 0; i < SIZE; i++) {
diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java
index d3119a00c37..f9c5f6199f1 100644
--- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java
+++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2022, 2023, Arm Limited. All rights reserved.
- * 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
@@ -24,6 +24,7 @@
 
 /*
  * @test
+ * @bug 8183390 8340010 8342095
  * @summary Vectorization test on array type conversions
  * @library /test/lib /
  *
@@ -108,10 +109,9 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
 
     // ---------------- Integer Extension ----------------
     @Test
-    @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
-    // Assert the vectorization failure so that we are reminded to update
-    // the test when this limitation is addressed in the future.
+    @IR(applyIfCPUFeature = { "avx", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_S2I, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
     public int[] signExtension() {
         int[] res = new int[SIZE];
         for (int i = 0; i < SIZE; i++) {
@@ -122,7 +122,7 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
 
     @Test
     @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
+    // Subword vector casts with char do not work currently, see JDK-8349562.
     // Assert the vectorization failure so that we are reminded to update
     // the test when this limitation is addressed in the future.
     public int[] zeroExtension() {
@@ -134,10 +134,9 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
     }
 
     @Test
-    @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
-    // Assert the vectorization failure so that we are reminded to update
-    // the test when this limitation is addressed in the future.
+    @IR(applyIfCPUFeature = { "avx", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_B2I, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" })
     public int[] signExtensionFromByte() {
         int[] res = new int[SIZE];
         for (int i = 0; i < SIZE; i++) {
@@ -146,12 +145,23 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
         return res;
     }
 
+    @Test
+    @IR(applyIfCPUFeature = { "avx", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_short, max_byte)", ">0" })
+    public short[] signExtensionFromByteToShort() {
+        short[] res = new short[SIZE];
+        for (int i = 0; i < SIZE; i++) {
+            res[i] = bytes[i];
+        }
+        return res;
+    }
+
     // ---------------- Integer Narrow ----------------
     @Test
-    @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
-    // Assert the vectorization failure so that we are reminded to update
-    // the test when this limitation is addressed in the future.
+    @IR(applyIfCPUFeature = { "avx", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
     public short[] narrowToSigned() {
         short[] res = new short[SIZE];
         for (int i = 0; i < SIZE; i++) {
@@ -161,10 +171,9 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
     }
 
     @Test
-    @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
-    // Assert the vectorization failure so that we are reminded to update
-    // the test when this limitation is addressed in the future.
+    @IR(applyIfCPUFeature = { "avx", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_char)", ">0" })
     public char[] narrowToUnsigned() {
         char[] res = new char[SIZE];
         for (int i = 0; i < SIZE; i++) {
@@ -174,11 +183,10 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
     }
 
     @Test
-    @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
-    // Assert the vectorization failure so that we are reminded to update
-    // the test when this limitation is addressed in the future.
-    public byte[] NarrowToByte() {
+    @IR(applyIfCPUFeature = { "avx", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" })
+    public byte[] narrowToByte() {
         byte[] res = new byte[SIZE];
         for (int i = 0; i < SIZE; i++) {
             res[i] = (byte) ints[i];
@@ -186,6 +194,18 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
         return res;
     }
 
+    @Test
+    @IR(applyIfCPUFeature = { "avx", "true" },
+        applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
+        counts = { IRNode.VECTOR_CAST_S2B, IRNode.VECTOR_SIZE + "min(max_short, max_byte)", ">0" })
+    public byte[] narrowShortToByte() {
+        byte[] res = new byte[SIZE];
+        for (int i = 0; i < SIZE; i++) {
+            res[i] = (byte) shorts[i];
+        }
+        return res;
+    }
+
     // ---------------- Convert I/L to F/D ----------------
     @Test
     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"},
@@ -268,7 +288,7 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
 
     @Test
     @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
+    // Subword vector casts with char do not work currently, see JDK-8349562.
     // Assert the vectorization failure so that we are reminded to update
     // the test when this limitation is addressed in the future.
     public float[] convertCharToFloat() {
@@ -281,7 +301,7 @@ public class ArrayTypeConvertTest extends VectorizationTestRunner {
 
     @Test
     @IR(failOn = {IRNode.STORE_VECTOR})
-    // Subword vector casts do not work currently, see JDK-8342095.
+    // Subword vector casts with char do not work currently, see JDK-8349562.
     // Assert the vectorization failure so that we are reminded to update
     // the test when this limitation is addressed in the future.
     public double[] convertCharToDouble() {
diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java
index 63739584558..b957a00278a 100644
--- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java
+++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2022, 2023, Arm Limited. All rights reserved.
+ * 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
@@ -23,6 +24,7 @@
 
 /*
  * @test
+ * @bug 8183390 8342095
  * @summary Vectorization test on basic short operations
  * @library /test/lib /
  *
@@ -210,10 +212,10 @@ public class BasicShortOpTest extends VectorizationTestRunner {
         return res;
     }
 
+    // Min/Max vectorization requires a cast from subword to int and back to subword, to avoid losing the higher order bits.
+
     @Test
-    // Note that min operations on subword types cannot be vectorized
-    // because higher bits will be lost.
-    @IR(failOn = {IRNode.STORE_VECTOR})
+    @IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
     public short[] vectorMin() {
         short[] res = new short[SIZE];
         for (int i = 0; i < SIZE; i++) {
@@ -223,9 +225,7 @@ public class BasicShortOpTest extends VectorizationTestRunner {
     }
 
     @Test
-    // Note that max operations on subword types cannot be vectorized
-    // because higher bits will be lost.
-    @IR(failOn = {IRNode.STORE_VECTOR})
+    @IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
     public short[] vectorMax() {
         short[] res = new short[SIZE];
         for (int i = 0; i < SIZE; i++) {
diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorSubword.java b/test/micro/org/openjdk/bench/vm/compiler/VectorSubword.java
new file mode 100644
index 00000000000..5ade452b875
--- /dev/null
+++ b/test/micro/org/openjdk/bench/vm/compiler/VectorSubword.java
@@ -0,0 +1,201 @@
+/*
+ * 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 org.openjdk.bench.vm.compiler;
+
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.*;
+
+import java.util.concurrent.TimeUnit;
+import java.util.Random;
+
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Thread)
+@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(value = 3)
+public class VectorSubword {
+    @Param({"1024"})
+    public int SIZE;
+
+    private byte[] bytes;
+    private short[] shorts;
+    private char[] chars;
+    private int[] ints;
+    private long[] longs;
+
+    @Setup
+    public void init() {
+        bytes = new byte[SIZE];
+        shorts = new short[SIZE];
+        chars = new char[SIZE];
+        ints = new int[SIZE];
+        longs = new long[SIZE];
+    }
+
+    // Narrowing
+
+    @Benchmark
+    public void shortToByte() {
+        for (int i = 0; i < SIZE; i++) {
+            bytes[i] = (byte) shorts[i];
+        }
+    }
+
+    @Benchmark
+    public void shortToChar() {
+        for (int i = 0; i < SIZE; i++) {
+            chars[i] = (char) shorts[i];
+        }
+    }
+
+    @Benchmark
+    public void charToByte() {
+        for (int i = 0; i < SIZE; i++) {
+            bytes[i] = (byte) chars[i];
+        }
+    }
+
+    @Benchmark
+    public void charToShort() {
+        for (int i = 0; i < SIZE; i++) {
+            shorts[i] = (short) chars[i];
+        }
+    }
+
+    @Benchmark
+    public void intToByte() {
+        for (int i = 0; i < SIZE; i++) {
+            bytes[i] = (byte) ints[i];
+        }
+    }
+
+    @Benchmark
+    public void intToShort() {
+        for (int i = 0; i < SIZE; i++) {
+            shorts[i] = (short) ints[i];
+        }
+    }
+
+    @Benchmark
+    public void intToChar() {
+        for (int i = 0; i < SIZE; i++) {
+            chars[i] = (char) ints[i];
+        }
+    }
+
+    @Benchmark
+    public void longToByte() {
+        for (int i = 0; i < SIZE; i++) {
+            bytes[i] = (byte) longs[i];
+        }
+    }
+
+    @Benchmark
+    public void longToShort() {
+        for (int i = 0; i < SIZE; i++) {
+            shorts[i] = (short) longs[i];
+        }
+    }
+
+    @Benchmark
+    public void longToChar() {
+        for (int i = 0; i < SIZE; i++) {
+            chars[i] = (char) longs[i];
+        }
+    }
+
+    @Benchmark
+    public void longToInt() {
+        for (int i = 0; i < SIZE; i++) {
+            ints[i] = (int) longs[i];
+        }
+    }
+
+    // Widening
+
+    @Benchmark
+    public void byteToShort() {
+        for (int i = 0; i < SIZE; i++) {
+            shorts[i] = bytes[i];
+        }
+    }
+
+    @Benchmark
+    public void byteToChar() {
+        for (int i = 0; i < SIZE; i++) {
+            chars[i] = (char) bytes[i];
+        }
+    }
+
+    @Benchmark
+    public void byteToInt() {
+        for (int i = 0; i < SIZE; i++) {
+            ints[i] = bytes[i];
+        }
+    }
+
+    @Benchmark
+    public void byteToLong() {
+        for (int i = 0; i < SIZE; i++) {
+            longs[i] = bytes[i];
+        }
+    }
+
+    @Benchmark
+    public void shortToInt() {
+        for (int i = 0; i < SIZE; i++) {
+            ints[i] = shorts[i];
+        }
+    }
+
+    @Benchmark
+    public void shortToLong() {
+        for (int i = 0; i < SIZE; i++) {
+            longs[i] = shorts[i];
+        }
+    }
+
+    @Benchmark
+    public void charToInt() {
+        for (int i = 0; i < SIZE; i++) {
+            ints[i] = chars[i];
+        }
+    }
+
+    @Benchmark
+    public void charToLong() {
+        for (int i = 0; i < SIZE; i++) {
+            longs[i] = chars[i];
+        }
+    }
+
+    @Benchmark
+    public void intToLong() {
+        for (int i = 0; i < SIZE; i++) {
+            longs[i] = ints[i];
+        }
+    }
+
+}

From d7c8000a493e58c677fed2e04678bb56e70dffc4 Mon Sep 17 00:00:00 2001
From: Jayathirth D V 
Date: Thu, 26 Feb 2026 05:49:31 +0000
Subject: [PATCH 053/636] 8378623: Use unique font names in
 FormatCharAdvanceTest

Reviewed-by: psadhukhan
---
 .../TextLayout/FormatCharAdvanceTest.java     | 33 +++++++++++--------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/test/jdk/java/awt/font/TextLayout/FormatCharAdvanceTest.java b/test/jdk/java/awt/font/TextLayout/FormatCharAdvanceTest.java
index 63cd890f1db..4a3f759c559 100644
--- a/test/jdk/java/awt/font/TextLayout/FormatCharAdvanceTest.java
+++ b/test/jdk/java/awt/font/TextLayout/FormatCharAdvanceTest.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
@@ -67,9 +67,13 @@ public class FormatCharAdvanceTest {
      * font.em = 2048
      * font.ascent = 1638
      * font.descent = 410
-     * font.familyname = 'Test'
-     * font.fontname = 'Test'
-     * font.fullname = 'Test'
+     * font.familyname = 'TTFTest'
+     * font.fontname = 'TTFTest'
+     * font.fullname = 'TTFTest'
+     * #Use below values for Type 1 font
+     * #font.familyname = 'Type1Test'
+     * #font.fontname = 'Type1Test'
+     * #font.fullname = 'Type1Test'
      * font.copyright = ''
      * font.autoWidth(0, 0, 2048)
      *
@@ -107,29 +111,30 @@ public class FormatCharAdvanceTest {
      *
      * ttf = 'test.ttf'     # TrueType
      * t64 = 'test.ttf.txt' # TrueType Base64
-     * pfb = 'test.pfb'     # PostScript Type1
-     * p64 = 'test.pfb.txt' # PostScript Type1 Base64
+     * #Use commented lines to generate Type1 font
+     * #pfb = 'test.pfb'     # PostScript Type1
+     * #p64 = 'test.pfb.txt' # PostScript Type1 Base64
      *
      * font.generate(ttf)
-     * font.generate(pfb)
+     * #font.generate(pfb)
      *
      * with open(ttf, 'rb') as f1:
      *   encoded = base64.b64encode(f1.read())
      *   with open(t64, 'wb') as f2:
      *     f2.write(encoded)
      *
-     * with open(pfb, 'rb') as f3:
-     *   encoded = base64.b64encode(f3.read())
-     *   with open(p64, 'wb') as f4:
-     *     f4.write(encoded)
+     * #with open(pfb, 'rb') as f3:
+     *   #encoded = base64.b64encode(f3.read())
+     *   #with open(p64, 'wb') as f4:
+     *     #f4.write(encoded)
      * 
*/ - private static final String TTF_BYTES = "AAEAAAANAIAAAwBQRkZUTarBS1AAABbcAAAAHE9TLzKD7vqWAAABWAAAAGBjbWFw11zF/AAAAvwAAANSY3Z0IABEBREAAAZQAAAABGdhc3D//wADAAAW1AAAAAhnbHlmgVJ3qAAAB4gAAAnMaGVhZCqFqboAAADcAAAANmhoZWEIcgJiAAABFAAAACRobXR4L1UevAAAAbgAAAFEbG9jYb8EwZoAAAZUAAABNG1heHAA4ABCAAABOAAAACBuYW1lJWcF2wAAEVQAAAGJcG9zdBSfZd0AABLgAAAD8QABAAAAAQAAzMHptF8PPPUACwgAAAAAAORfr7QAAAAA5F+vtABEAAACZAVVAAAACAACAAAAAAAAAAEAAAVVAAAAuAJYAAAAAAJkAAEAAAAAAAAAAAAAAAAAAAAJAAEAAACZAAgAAgAIAAIAAgAAAAEAAQAAAEAALgABAAEABAJXAZAABQAABTMFmQAAAR4FMwWZAAAD1wBmAhIAAAIABQMAAAAAAACAACADAgAAABECAKgAAAAAUGZFZACAAAn//wZm/mYAuAVVAAAAAAABAAAAAADIAMgAAAAgAAEC7ABEAAAAAAJYAGQCWABkAlgAZAJYAGQCWABkAjkAAAJYAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAAAAFAAAAAwAAACwAAAAEAAAA7AABAAAAAAJMAAMAAQAAACwAAwAKAAAA7AAEAMAAAAAoACAABAAIAA0AIAA5AFoAegCFAK0GBQYcBt0HDwiRCOIYDiAPIC8gb/7///v//wAAAAkAIAAwAEEAYQCFAK0GAAYcBt0HDwiQCOIYDiALICggYP7///n//wAA/+f/2P/R/8v/wf+a+kj6Mvly+UH3wfdx6EbgSuAy4AIBcwAAAAEAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAMABAAFAAYAAgBzAHQAdQAMAAAAAAFgAAAAAAAAABwAAAAJAAAADAAAAAMAAAANAAAADQAAAAIAAAAgAAAAIAAAAAcAAAAwAAAAOQAAAAgAAABBAAAAWgAAABIAAABhAAAAegAAACwAAACFAAAAhQAAAEYAAACtAAAArQAAAEcAAAYAAAAGBQAAAEgAAAYcAAAGHAAAAE4AAAbdAAAG3QAAAE8AAAcPAAAHDwAAAFAAAAiQAAAIkQAAAFEAAAjiAAAI4gAAAFMAABgOAAAYDgAAAFQAACALAAAgDwAAAFUAACAoAAAgLwAAAFoAACBgAAAgbwAAAGIAAP7/AAD+/wAAAHIAAP/5AAD/+wAAAHMAARC9AAEQvQAAAHYAARDNAAEQzQAAAHcAATQwAAE0PwAAAHgAAbygAAG8owAAAIgAAdFzAAHRegAAAIwADgABAA4AAQAAAJQADgAgAA4AIQAAAJUADgB+AA4AfwAAAJcAAAEGAAABAAAAAAAAAAEDBAUGAgAAAAAAAAAAAAAAAAAAAAEAAAcAAAAAAAAAAAAAAAAAAAAICQoLDA0ODxARAAAAAAAAABITFBUWFxgZGhscHR4fICEiIyQlJicoKSorAAAAAAAALC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAURAAAALAAsADQAPABEAEwAVABUAFwAZABsAHQAfACEAIwAlACcAKQArAC0ALwAxADMANQA3ADkAOwA9AD8AQQBDAEUARwBJAEsATQBPAFEAUwBVAFcAWQBbAF0AYYBjgGWAZ4BpgGuAbYBvgHGAc4B1gHeAeYB7gH2Af4CBgIOAhYCHgImAi4CNgI+AkYCTgJWAl4CZgJuAnYCfgKGAo4ClgKeAqYCrgK2Ar4CxgLOAtYC3gLmAu4C9gL+AwYDDgMWAx4DJgMuAzYDPgNGA04DVgNeA2YDbgN2A34DhgOOA5YDngOmA64DtgO+A8YDzgPWA94D5gPuA/YD/gQGBA4EFgQeBCYELgQ2BD4ERgROBFYEXgRmBG4EdgR+BIYEjgSWBJ4EpgSuBLYEvgTGBM4E1gTeBOYAAgBEAAACZAVVAAMABwAusQEALzyyBwQA7TKxBgXcPLIDAgDtMgCxAwAvPLIFBADtMrIHBgH8PLIBAgDtMjMRIRElIREhRAIg/iQBmP5oBVX6q0QEzQAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAAAAgBkAGQB9ADIAAMABwAANzUhFSE1IRVkAZD+cAGQZGRkZGT//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAAAAAAOAK4AAQAAAAAAAAAAAAIAAQAAAAAAAQAEAA0AAQAAAAAAAgAHACIAAQAAAAAAAwAgAGwAAQAAAAAABAAEAJcAAQAAAAAABQAPALwAAQAAAAAABgAEANYAAwABBAkAAAAAAAAAAwABBAkAAQAIAAMAAwABBAkAAgAOABIAAwABBAkAAwBAACoAAwABBAkABAAIAI0AAwABBAkABQAeAJwAAwABBAkABgAIAMwAAAAAVABlAHMAdAAAVGVzdAAAUgBlAGcAdQBsAGEAcgAAUmVndWxhcgAARgBvAG4AdABGAG8AcgBnAGUAIAAyAC4AMAAgADoAIABUAGUAcwB0ACAAOgAgADMAMAAtADUALQAyADAAMgA1AABGb250Rm9yZ2UgMi4wIDogVGVzdCA6IDMwLTUtMjAyNQAAVABlAHMAdAAAVGVzdAAAVgBlAHIAcwBpAG8AbgAgADAAMAAxAC4AMAAwADAAAFZlcnNpb24gMDAxLjAwMAAAVABlAHMAdAAAVGVzdAAAAAAAAgAAAAAAAP9nAGYAAAAAAAAAAAAAAAAAAAAAAAAAAACZAAAAAQECAQMBBAEFAQYAAwATABQAFQAWABcAGAAZABoAGwAcACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0BBwEIAQkBCgELAQwBDQEOAQ8BEAERARIBEwEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEBIgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZB3VuaTAwMEQHdW5pMDAwOQd1bmkwMDBBB3VuaTAwMEIHdW5pMDAwQwd1bmkwMDg1B3VuaTAwQUQHdW5pMDYwMAd1bmkwNjAxB3VuaTA2MDIHdW5pMDYwMwd1bmkwNjA0B3VuaTA2MDUHdW5pMDYxQwd1bmkwNkREB3VuaTA3MEYHdW5pMDg5MAd1bmkwODkxB3VuaTA4RTIHdW5pMTgwRQd1bmkyMDBCB3VuaTIwMEMHdW5pMjAwRAd1bmkyMDBFB3VuaTIwMEYHdW5pMjAyOAd1bmkyMDI5B3VuaTIwMkEHdW5pMjAyQgd1bmkyMDJDB3VuaTIwMkQHdW5pMjAyRQd1bmkyMDJGB3VuaTIwNjAHdW5pMjA2MQd1bmkyMDYyB3VuaTIwNjMHdW5pMjA2NAd1bmkyMDY1B3VuaTIwNjYHdW5pMjA2Nwd1bmkyMDY4B3VuaTIwNjkHdW5pMjA2QQd1bmkyMDZCB3VuaTIwNkMHdW5pMjA2RAd1bmkyMDZFB3VuaTIwNkYHdW5pRkVGRgd1bmlGRkY5B3VuaUZGRkEHdW5pRkZGQgZ1MTEwQkQGdTExMENEBnUxMzQzMAZ1MTM0MzEGdTEzNDMyBnUxMzQzMwZ1MTM0MzQGdTEzNDM1BnUxMzQzNgZ1MTM0MzcGdTEzNDM4BnUxMzQzOQZ1MTM0M0EGdTEzNDNCBnUxMzQzQwZ1MTM0M0QGdTEzNDNFBnUxMzQzRgZ1MUJDQTAGdTFCQ0ExBnUxQkNBMgZ1MUJDQTMGdTFEMTczBnUxRDE3NAZ1MUQxNzUGdTFEMTc2BnUxRDE3NwZ1MUQxNzgGdTFEMTc5BnUxRDE3QQZ1RTAwMDEGdUUwMDIwBnVFMDAyMQZ1RTAwN0UGdUUwMDdGAAAAAAAAAf//AAIAAAABAAAAAOIB6+cAAAAA5F+vtAAAAADkX6+0"; + private static final String TTF_BYTES = "AAEAAAANAIAAAwBQRkZUTbCUBjwAABcAAAAAHE9TLzKD7vqWAAABWAAAAGBjbWFw11zF/AAAAvwAAANSY3Z0IABEBREAAAZQAAAABGdhc3D//wADAAAW+AAAAAhnbHlmgVJ3qAAAB4gAAAnMaGVhZC1MmToAAADcAAAANmhoZWEIcgJiAAABFAAAACRobXR4L1UevAAAAbgAAAFEbG9jYb8EwZoAAAZUAAABNG1heHAA4ABCAAABOAAAACBuYW1lLzI4NgAAEVQAAAGtcG9zdBSfZd0AABMEAAAD8QABAAAAAQAAp/gvll8PPPUACwgAAAAAAOXDJ3QAAAAA5cMndABEAAACZAVVAAAACAACAAAAAAAAAAEAAAVVAAAAuAJYAAAAAAJkAAEAAAAAAAAAAAAAAAAAAAAJAAEAAACZAAgAAgAIAAIAAgAAAAEAAQAAAEAALgABAAEABAJXAZAABQAABTMFmQAAAR4FMwWZAAAD1wBmAhIAAAIABQMAAAAAAACAACADAgAAABECAKgAAAAAUGZFZACAAAn//wZm/mYAuAVVAAAAAAABAAAAAADIAMgAAAAgAAEC7ABEAAAAAAJYAGQCWABkAlgAZAJYAGQCWABkAjkAAAJYAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAZABkAGQAAAAFAAAAAwAAACwAAAAEAAAA7AABAAAAAAJMAAMAAQAAACwAAwAKAAAA7AAEAMAAAAAoACAABAAIAA0AIAA5AFoAegCFAK0GBQYcBt0HDwiRCOIYDiAPIC8gb/7///v//wAAAAkAIAAwAEEAYQCFAK0GAAYcBt0HDwiQCOIYDiALICggYP7///n//wAA/+f/2P/R/8v/wf+a+kj6Mvly+UH3wfdx6EbgSuAy4AIBcwAAAAEAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAMABAAFAAYAAgBzAHQAdQAMAAAAAAFgAAAAAAAAABwAAAAJAAAADAAAAAMAAAANAAAADQAAAAIAAAAgAAAAIAAAAAcAAAAwAAAAOQAAAAgAAABBAAAAWgAAABIAAABhAAAAegAAACwAAACFAAAAhQAAAEYAAACtAAAArQAAAEcAAAYAAAAGBQAAAEgAAAYcAAAGHAAAAE4AAAbdAAAG3QAAAE8AAAcPAAAHDwAAAFAAAAiQAAAIkQAAAFEAAAjiAAAI4gAAAFMAABgOAAAYDgAAAFQAACALAAAgDwAAAFUAACAoAAAgLwAAAFoAACBgAAAgbwAAAGIAAP7/AAD+/wAAAHIAAP/5AAD/+wAAAHMAARC9AAEQvQAAAHYAARDNAAEQzQAAAHcAATQwAAE0PwAAAHgAAbygAAG8owAAAIgAAdFzAAHRegAAAIwADgABAA4AAQAAAJQADgAgAA4AIQAAAJUADgB+AA4AfwAAAJcAAAEGAAABAAAAAAAAAAEDBAUGAgAAAAAAAAAAAAAAAAAAAAEAAAcAAAAAAAAAAAAAAAAAAAAICQoLDA0ODxARAAAAAAAAABITFBUWFxgZGhscHR4fICEiIyQlJicoKSorAAAAAAAALC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAURAAAALAAsADQAPABEAEwAVABUAFwAZABsAHQAfACEAIwAlACcAKQArAC0ALwAxADMANQA3ADkAOwA9AD8AQQBDAEUARwBJAEsATQBPAFEAUwBVAFcAWQBbAF0AYYBjgGWAZ4BpgGuAbYBvgHGAc4B1gHeAeYB7gH2Af4CBgIOAhYCHgImAi4CNgI+AkYCTgJWAl4CZgJuAnYCfgKGAo4ClgKeAqYCrgK2Ar4CxgLOAtYC3gLmAu4C9gL+AwYDDgMWAx4DJgMuAzYDPgNGA04DVgNeA2YDbgN2A34DhgOOA5YDngOmA64DtgO+A8YDzgPWA94D5gPuA/YD/gQGBA4EFgQeBCYELgQ2BD4ERgROBFYEXgRmBG4EdgR+BIYEjgSWBJ4EpgSuBLYEvgTGBM4E1gTeBOYAAgBEAAACZAVVAAMABwAusQEALzyyBwQA7TKxBgXcPLIDAgDtMgCxAwAvPLIFBADtMrIHBgH8PLIBAgDtMjMRIRElIREhRAIg/iQBmP5oBVX6q0QEzQAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAAAAgBkAGQB9ADIAAMABwAANzUhFSE1IRVkAZD+cAGQZGRkZGT//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAAAAAAOAK4AAQAAAAAAAAAAAAIAAQAAAAAAAQAHABMAAQAAAAAAAgAHACsAAQAAAAAAAwAjAHsAAQAAAAAABAAHAK8AAQAAAAAABQAPANcAAQAAAAAABgAHAPcAAwABBAkAAAAAAAAAAwABBAkAAQAOAAMAAwABBAkAAgAOABsAAwABBAkAAwBGADMAAwABBAkABAAOAJ8AAwABBAkABQAeALcAAwABBAkABgAOAOcAAAAAVABUAEYAVABlAHMAdAAAVFRGVGVzdAAAUgBlAGcAdQBsAGEAcgAAUmVndWxhcgAARgBvAG4AdABGAG8AcgBnAGUAIAAyAC4AMAAgADoAIABUAFQARgBUAGUAcwB0ACAAOgAgADIANAAtADIALQAyADAAMgA2AABGb250Rm9yZ2UgMi4wIDogVFRGVGVzdCA6IDI0LTItMjAyNgAAVABUAEYAVABlAHMAdAAAVFRGVGVzdAAAVgBlAHIAcwBpAG8AbgAgADAAMAAxAC4AMAAwADAAAFZlcnNpb24gMDAxLjAwMAAAVABUAEYAVABlAHMAdAAAVFRGVGVzdAAAAAAAAgAAAAAAAP9nAGYAAAAAAAAAAAAAAAAAAAAAAAAAAACZAAAAAQECAQMBBAEFAQYAAwATABQAFQAWABcAGAAZABoAGwAcACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0BBwEIAQkBCgELAQwBDQEOAQ8BEAERARIBEwEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEBIgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZB3VuaTAwMEQHdW5pMDAwOQd1bmkwMDBBB3VuaTAwMEIHdW5pMDAwQwd1bmkwMDg1B3VuaTAwQUQHdW5pMDYwMAd1bmkwNjAxB3VuaTA2MDIHdW5pMDYwMwd1bmkwNjA0B3VuaTA2MDUHdW5pMDYxQwd1bmkwNkREB3VuaTA3MEYHdW5pMDg5MAd1bmkwODkxB3VuaTA4RTIHdW5pMTgwRQd1bmkyMDBCB3VuaTIwMEMHdW5pMjAwRAd1bmkyMDBFB3VuaTIwMEYHdW5pMjAyOAd1bmkyMDI5B3VuaTIwMkEHdW5pMjAyQgd1bmkyMDJDB3VuaTIwMkQHdW5pMjAyRQd1bmkyMDJGB3VuaTIwNjAHdW5pMjA2MQd1bmkyMDYyB3VuaTIwNjMHdW5pMjA2NAd1bmkyMDY1B3VuaTIwNjYHdW5pMjA2Nwd1bmkyMDY4B3VuaTIwNjkHdW5pMjA2QQd1bmkyMDZCB3VuaTIwNkMHdW5pMjA2RAd1bmkyMDZFB3VuaTIwNkYHdW5pRkVGRgd1bmlGRkY5B3VuaUZGRkEHdW5pRkZGQgZ1MTEwQkQGdTExMENEBnUxMzQzMAZ1MTM0MzEGdTEzNDMyBnUxMzQzMwZ1MTM0MzQGdTEzNDM1BnUxMzQzNgZ1MTM0MzcGdTEzNDM4BnUxMzQzOQZ1MTM0M0EGdTEzNDNCBnUxMzQzQwZ1MTM0M0QGdTEzNDNFBnUxMzQzRgZ1MUJDQTAGdTFCQ0ExBnUxQkNBMgZ1MUJDQTMGdTFEMTczBnUxRDE3NAZ1MUQxNzUGdTFEMTc2BnUxRDE3NwZ1MUQxNzgGdTFEMTc5BnUxRDE3QQZ1RTAwMDEGdUUwMDIwBnVFMDAyMQZ1RTAwN0UGdUUwMDdGAAAAAAAAAf//AAIAAAABAAAAAOUNt1MAAAAA5cMndAAAAADlwyd0"; /** - * Same font as above, but in PostScript Type1 (PFB) format. + * Same font as above, but in PostScript Type1 (PFB) format with different font name. */ - private static final String TYPE1_BYTES = "gAFSBwAAJSFQUy1BZG9iZUZvbnQtMS4wOiBUZXN0IDAwMS4wMDAKJSVUaXRsZTogVGVzdAolVmVyc2lvbjogMDAxLjAwMAolJUNyZWF0aW9uRGF0ZTogRnJpIE1heSAzMCAyMDo1NTo0OCAyMDI1CiUlQ3JlYXRvcjogRGFuaWVsIEdyZWRsZXIKJSAyMDI1LTUtMzA6IENyZWF0ZWQgd2l0aCBGb250Rm9yZ2UgKGh0dHA6Ly9mb250Zm9yZ2Uub3JnKQolIEdlbmVyYXRlZCBieSBGb250Rm9yZ2UgMjAyMzAxMDEgKGh0dHA6Ly9mb250Zm9yZ2Uuc2YubmV0LykKJSVFbmRDb21tZW50cwoKMTAgZGljdCBiZWdpbgovRm9udFR5cGUgMSBkZWYKL0ZvbnRNYXRyaXggWzAuMDAwNDg4MjgxIDAgMCAwLjAwMDQ4ODI4MSAwIDAgXXJlYWRvbmx5IGRlZgovRm9udE5hbWUgL1Rlc3QgZGVmCi9Gb250QkJveCB7MTAwIDEwMCA1MDAgMjAwIH1yZWFkb25seSBkZWYKL1BhaW50VHlwZSAwIGRlZgovRm9udEluZm8gMTAgZGljdCBkdXAgYmVnaW4KIC92ZXJzaW9uICgwMDEuMDAwKSByZWFkb25seSBkZWYKIC9Ob3RpY2UgKCkgcmVhZG9ubHkgZGVmCiAvRnVsbE5hbWUgKFRlc3QpIHJlYWRvbmx5IGRlZgogL0ZhbWlseU5hbWUgKFRlc3QpIHJlYWRvbmx5IGRlZgogL1dlaWdodCAoUmVndWxhcikgcmVhZG9ubHkgZGVmCiAvRlNUeXBlIDAgZGVmCiAvSXRhbGljQW5nbGUgMCBkZWYKIC9pc0ZpeGVkUGl0Y2ggZmFsc2UgZGVmCiAvVW5kZXJsaW5lUG9zaXRpb24gLTIwNC44IGRlZgogL1VuZGVybGluZVRoaWNrbmVzcyAxMDIuNCBkZWYKZW5kIHJlYWRvbmx5IGRlZgovRW5jb2RpbmcgMjU2IGFycmF5CiAwIDEgMjU1IHsgMSBpbmRleCBleGNoIC8ubm90ZGVmIHB1dH0gZm9yCmR1cCA5L3VuaTAwMDkgcHV0CmR1cCAxMC91bmkwMDBBIHB1dApkdXAgMTEvdW5pMDAwQiBwdXQKZHVwIDEyL3VuaTAwMEMgcHV0CmR1cCAxMy91bmkwMDBEIHB1dApkdXAgMzIvc3BhY2UgcHV0CmR1cCA0OC96ZXJvIHB1dApkdXAgNDkvb25lIHB1dApkdXAgNTAvdHdvIHB1dApkdXAgNTEvdGhyZWUgcHV0CmR1cCA1Mi9mb3VyIHB1dApkdXAgNTMvZml2ZSBwdXQKZHVwIDU0L3NpeCBwdXQKZHVwIDU1L3NldmVuIHB1dApkdXAgNTYvZWlnaHQgcHV0CmR1cCA1Ny9uaW5lIHB1dApkdXAgNjUvQSBwdXQKZHVwIDY2L0IgcHV0CmR1cCA2Ny9DIHB1dApkdXAgNjgvRCBwdXQKZHVwIDY5L0UgcHV0CmR1cCA3MC9GIHB1dApkdXAgNzEvRyBwdXQKZHVwIDcyL0ggcHV0CmR1cCA3My9JIHB1dApkdXAgNzQvSiBwdXQKZHVwIDc1L0sgcHV0CmR1cCA3Ni9MIHB1dApkdXAgNzcvTSBwdXQKZHVwIDc4L04gcHV0CmR1cCA3OS9PIHB1dApkdXAgODAvUCBwdXQKZHVwIDgxL1EgcHV0CmR1cCA4Mi9SIHB1dApkdXAgODMvUyBwdXQKZHVwIDg0L1QgcHV0CmR1cCA4NS9VIHB1dApkdXAgODYvViBwdXQKZHVwIDg3L1cgcHV0CmR1cCA4OC9YIHB1dApkdXAgODkvWSBwdXQKZHVwIDkwL1ogcHV0CmR1cCA5Ny9hIHB1dApkdXAgOTgvYiBwdXQKZHVwIDk5L2MgcHV0CmR1cCAxMDAvZCBwdXQKZHVwIDEwMS9lIHB1dApkdXAgMTAyL2YgcHV0CmR1cCAxMDMvZyBwdXQKZHVwIDEwNC9oIHB1dApkdXAgMTA1L2kgcHV0CmR1cCAxMDYvaiBwdXQKZHVwIDEwNy9rIHB1dApkdXAgMTA4L2wgcHV0CmR1cCAxMDkvbSBwdXQKZHVwIDExMC9uIHB1dApkdXAgMTExL28gcHV0CmR1cCAxMTIvcCBwdXQKZHVwIDExMy9xIHB1dApkdXAgMTE0L3IgcHV0CmR1cCAxMTUvcyBwdXQKZHVwIDExNi90IHB1dApkdXAgMTE3L3UgcHV0CmR1cCAxMTgvdiBwdXQKZHVwIDExOS93IHB1dApkdXAgMTIwL3ggcHV0CmR1cCAxMjEveSBwdXQKZHVwIDEyMi96IHB1dApkdXAgMTMzL3VuaTAwODUgcHV0CmR1cCAxNzMvdW5pMDBBRCBwdXQKcmVhZG9ubHkgZGVmCmN1cnJlbnRkaWN0IGVuZApjdXJyZW50ZmlsZSBlZXhlYwqAAo0WAAB0P4QT82NsqFqf/vtQtLsnMCpcwKtuL5Wb8g0yDDc8ISjQoM5wcrH2cqCqOMPA7OsEtEyxdKHDFhLXH/ogyQlUJWN4Ny95WwvylB9DfwWfQa4FmMAFFf7xhzM1V/Ms4yqe59S6tl2lND+ScH4s/PPozkRuWtrDn8N+zmS2izVs4NcQ9FsefyzXaKvKFDYINmh2GhAJRkFi0FTB9r8sRqMZs8ZkUodjFsE6kmRW02YpWP1WdA1YVnNuYQT4E+0xkVZ3eTTuscVEWua/buWeWxlwi0KH20ubr5iCkavAb953/MMe4XMKlPvGzsx0Slmp9qOOLD0ko2287peB1DvLavHCG/r2rr8MhqsKVoSyFr36sVd9hMSQrwFxpRjVEjtt5HDGcbHwy3ylp8b1oopSl5AQmeoW/oGT0rPr+358A+Qd2/wpQgStXDcDExQWDrdOL7J5Fr7VBDu8NNORXc7c5uKb2iVQq5AOF+Yfa5bH4reIFcWrAItBML9L9b8sOf1QKzGymFzgcDW3EEx07Y07ys95zSLkYtY5lkjm07GFSyM9cuWhRctY6RTcjTAFNdbxOUBXbIGf4Q8nhdXXi3IKhYdqdgKEsWphwKEdQeP7QYqW4bPSB881DjF/e7r6PIkua8MXGI3G1I0C53TIFnHXai5dtVJ4cZhdaTzgoD9K6zYMNsb8Tof6THYx+cRUzPbJmb+VbprjDk96+92jF+P0EwubV0GQMLEdCyPDV1u/vIHPePqSJt2/YjgkXEugRbyqzFwfZJdeAmkzcqs1CU9EiqrwgqbEfdmWWwv9BDxgkWD4omPKAKX1A6asv/UVfH8lebHSbrKddpR8XU/C2I/t7UhI19oTFqIvUL061MrZPiHZaFE9bnvJw+P91hF+MoPs69ddhD4Kj6z6MJYNGlvAZMuUM/RdAUwE3HHhwGiCN3Hqi4V+byVeB95B/8YLQK1CiW/pgfU7TqZ1d5nWGoAXAcAu6UYGvZZ0ChX3AEPlIFJ4x4hXfBo0I/k/TEHus/u5dNm/+ixyBcr/rX/e2yPIBfJkBJDG8x/y7JaxOFAD1NMQ4zWjbzP1uPpiGzRKfCXYLF6YXLfGDKJ1w3IENGwIMXBgqOx6O2v2xkYhcAUkrZ2Nw3xpRrnj2RqGCGwz00AHrl9AU7CNaM8BkSe6d86xT8teVYuOk926Xj4/+Kx6rfu526Ylz91kxebopcMvql6ysRVzzSGsW2ec9ZPJo1Q/WKb+tCvBm8OdUnRi+DldIajpytyl08TmlS+IRUcHZbxoIjxb7ZCvF8hd03yDhs9bsUSO6h7jbhegenLIiQPX7RtsGAg3logZLD0NUjcAm2tKBieMHhMxAD89lVmuMkNj5r6EaixXvkvhgqzhjPExdu30Knife5IEnlCvIlMCh1EXQsY9KRkzeTRTKfnZTJ/idze+cX0nCEGcFrvCCjZRfNuaLRHx73o2KlDHmoYBm1mFEEvfUReQxS987AiVfSF7cs8IrLEqV9qe2mCAv7ATbVUFbnvYxTBYvHL9sc4892rLrjSu/yP5RZmmIMOpTH8CStLw1zGAcXH3W3ZfPjQkEA4jzvo1U5Oi9EkBKqlgqjj2pFLWepwuNG/L3Iyr36frW6NFtqCxyVQYcNwOTnmQjY0LXPAKxyz2l+xyo/gcDY8nqtWms5aqINgemqM0pG2wBt3GawARbEVkkKllyB/WJbRw0GGHktmj73ixkTulD6nHZ+vMd+aS8iKXPa2ASEOPpxp515DZxg+VYZIrM0h4mDdRff4To/8NKs7Kx4IZx2qzlhIKS2zHpLwuY/U2Wt80e9nqMCFEtnS8sKpcy1rrE9FzXsFUNtYE6LdIat4ygQxoq24Y6D3bfDpl3E8wMvECaVCJX8lfBhQcGGU6+Jba1qfQ7jonjQM0zfoOwHrApk/dkPkSdjQy7UM+dZNMtgZf2N46UuWC2YCtcqCN1X+o5SpojTgXNmTjUy3KOy6L4GNYOxutsDhWiah6rJ+FGVL9D51MJvT3l02T1+LpNt8ZeZQSkZpx7NdFFTFA4/SE8zycfBLNm71Q9pjQKa30TnCY9ZkMEH9rnArLV0w2Gi6LoJoWpT7GoAB2Cdvi3x9wGDdt60K6oABWiD5mpAv4KfrJMBn5dRDJVyRPARXxwBUROAc3BxY8St3WuWKqDqqef0t0rlqAXGynPrcAjqFMXc/GBWORQ7W/dTvbu/eZb+apjKEX7szjaY4cSRsNge4gTyCEsfhLrdBkncM7up9jj206tIaqZQILxHAnrVwYplJ5D2EvSSLYvxFEVHSrI19aNJEvDK64tzXCV+P4yOCOuXpBmc15PDlrMQEauqrD+xneCO+CrCbudHXOJsbmvQq56+ovLp5SNBfdEPQpqo5tBgQaNHcEjg/iVt++uLxT3vAEFW5d7WXPMYdy+XqoMmdRDOzey0Mdx5BZ92rNM/LthQa6F54nvzkypM64HdxCYlri8cO3k0G6bp2eWdseP0P4zMc5QikuXc/Dr4NztBYe2yJLbLLesWSB8nOWcl+gd0GMX5PY4P5mn5WJDNStLWPdD9NapzMsyx6lZwR9Tfv/XSmay9LEo9YaaxysWfllDIasUCdvhZVi7LJvPu/0GbuHLOz9mP6prkF8h8KmjBYMlMktouA23G74M+Pkdfbj9KAM8zOYCgQFZUaXx3iZ4m3uBtDPeKjUcdFZSnHdW11eSv5aCz2fMbExV/qnPOSMbkR5rvpR71WwVZP6j3tNWr8jlPQ8d60k8jXPy7d8wjw9obnDEpvKzKreEzjfA0wCovNt7FLH9oxWmO5TF8JMg/U7+ToEv0fPAW475dXTXdue98/k/c+3+LCJNbHzBBx14CzpteKXKqGNA3jwgJUhfDsTISiN4gF222zIi1deY9BspjPl7jergbwh+ZPdfk2BLaPTDeNUycKdeiFoJEd8gKLvjMrnPa9IX4HwR9gJSjsQ0UWhnYuaXdk12zk8sVkGJAJLrphh5tHcd4LwG2rWcVXwM/2zw1YXQR+jffC1uxUEPVaq1fSf9RB6iEb8LgIollE+gDEybc+Dw3Q/gxVe5/Nu0vHHur6B+4YEcxvz3O5cYKUQTxDgG4mXJhqZ6z7etSWINChQyP6PUFFbsUIDXjfuHZ9oF4ce1NNOnn/RIwm/u/+AduDTtqloUooZjqak4VbvmBEZcW75liKm0MggeilL0nl5arItzTwE8OudJDBkJ/xnf1yZIgeFKSODIz5aQrIiSMJX3BGRxHEEQrm1xtwCfjtMPpFhFtG+o5fjMlCnowJ6+/HrG3FdZKn4/gv7qCxF2/3NW2J4OWuXbjEsUlSHu57DFvhrVl/q8jv81OfXhZKutYQWjaDWwOQHR+ym6wvMyQ9YfiiJpBXa+Ig9uGAbERyApc8wsCLcy0d8Msxx9PAfhrDa+Gf2yhYGA57t8iGxEcoI0A4B8zKVqYXKf4AdsrVzE9cUpfRN/HH8OGWyv9mKMTJyGJQql/OH5hYHnvJVKZXIz6l3nL/7gnpYaGtASiV1q+JwO7WSk1pfwT9SE4sFl7uS7f3pu4BwPhcz/wyN82f144yav11mrGqypOMaMDR27/L0sW1c3hWBBa8BMU/EuwF7ldclS9nxmFHOc35RLruyQ26cp1niukcpK42wg3ozqGQLoQRIoin0AWnUwnOmkdBEkILqFCiWgERDZuCB7G3pZJW/32HPgD3byO0lH8JF6OiINsUmM+75Y3qdvuepTHxXwpDa9Isero3x/f+UWGGUO12s+Ou2yQKtLsQaBNSTaGEBVCAtpJ58TgZ+KfVyhgRQdINr7GuGq9iRPQBY00anspxvsrhfffggqOariHO8h0jdEZPJarJJAufQbYgKiACtJ8iG6jlIs3GfWRI0jRYDw+kHPc2/A6fS5XGF360aX1I6iy5J1pZpN+6stJq5MQ8QRHopTJWQ0DW1Pq46BglmHH9av/X4tvLFFyyC4X6OB2jDb9+XwwcuJ2t/352IcJC06IsWKDsr6E6vF3hC3GUBUy9I7JbqlAh7zlYK52MITQzGINZD2p8+TJjmDycEr/UM/JHqBzbJniUzxg8vo2/FjpZggU8NuBsTZ5P/YZSgRsZcVKkhkGYAn+euvrQIiZ2LMwOygD0gW/zy7v+Z1aHfxKig7kkOR1ZuVCFE8FWx1unCnFHtkjFIx9JY4cYHboveIlF2IzNhfnKJncBt7DuZORqfAT3gQDZlIFNx06tSnTe1v5l7u7VSKMXRhc3bbeCqfvO/NSOqfLz1QYlj2b0C5sm0AcUe1RpC7rOu6muPLeJ38DOIhJHzs+wpqBZTHJr9zdnjZl+OyXaAAbgCsxamEQW91iGdRmVHqyT+0+XQxssYOF795eGYpsXy9Bik6Z4apt/AcuZQFu3XthVXvjyVCi0Rj3gXjOkFdT1YaiRe6yPRdBB2GorcxCzQQvjZXTuh5P4a7MD/6W0mJYoh0BzQHYC0O/T9v3d8GepAbQTytL7MWIWk9C2qZ0pPciuIfXNmGRWBUkCCts1BAsW39l4giNBCwDQ6CauWkz8iWaxx2krBQpKl0WyBBSd/SIJUFM4psvYNaGNk+xR18s0OrPqvxQvc3NNgVwpAljSM/wt3IzIcIsJHF/u0T60hXgEv4H0f44/SzROL0iGUZIfRZK6sJRGBQH2ZVrFCJ6ageJX7WTBLVwKAtxgIUK2dXAemsYgGm/CIe3JCmwL/YmEh8I8W4LrWzXq4vNF6/Sq1hQ2r4dndAO+HEZ8nYpPr+9pUCUgSvnN7aVWZpc/xJaj5L4U6W28hVZ2mb+UUKDpq0bSxKdkVr2SC/7Q/8WimMNDYCaExzXQ+RzZyv/4227pn/k3y1avulXGz3ujy+lqdciVVWHmPYyJPhqeoyDEkM6g46KL9kg912jsB1SGP7Yc2l7XENW59mh5RE+fNiW8wWjxW0Oa+xSe2+RTOgRA+Ojq2L8+g9sL7jKw7BPAPjnYTo8gMat4DurAKbJrtaRLZMzXkFyE7heHr9NsLJl/d1JLRwNoGDpnvLjyPNBUjW/2CTEEIyI6yq3oOJivAwe1yiJib7IUTkh5MCBSnEssvxOYJRUaR/ILAHMJpAoEV/VOCfBlh/I55gVYZWa+Y/5/AMs5Po1w9/18Q6V4ichv80Qr+Up7+2s8sV6zM7m78WMmQp3IR6uTJasqI2Vnr99KAY31BJVpBelRq2xjIruSHx/BAr2kSHA+Kuk1I9+Y1dEaSbfUpjcly0WDUh9x1VTkrUu1u6XN/i/SkFeO1k8o2o7s0rVXNI+VipXQt69H+yVDea7AYuvy9Rp2kKuq7o7mgcFCVq1Vbeyv++vDSKdYmKbyQ2N7ubU6u41Td59YNfRldl56ec0bl7hgyMUb/7/ifjdRXOVl1sTTMUiETnT+44vuAiiUeOHRgKmRlLgHp9iMnPPZVF3DqExLV1IPraZO/olA9QIc4bM2g6SPebITaH94rZn80oQzWzBAFUzK/B2vY39XGdPSTAZTFn1yAYcRHIhqWZs9+FtJvBN9f9lUxPFHUs3vyKBC8sFph/yee7KNIKyTinMNAs1+jcnVS4LPqR4L4cn3ancasZ6wBgexKIjjwf8KPkgUcMwZqk+KwDeJbmyGblqsRzSuhrwPq0U+n4n1ktcqI11Y4D/u93UrU04LgZwWPEZPrgjGUgciPrRa3BIJb6oT1s5VSfgiupwHaFYgF5sJ3VD0ZcT1zwdet8tPprxRq00SIZMZKZeVSIWD0ai82pPxH4PYLUGwUbW3ocaYouILRf9sF9DP1gaVfd4SDRUuKUtZRiG6lpSopmFO24N1vWq90caknzY91uUWX2v4+C/JHDd47e4h/g1Lf13euI6csefzsnQDTDsyrh5zSJDo26B81kY5RyKv+AnsvTnTl8uroRsh1KqeIUywpWImmlnnWT8rpNg822BjraGaWc9NHFzkUSmOmD/iJRdfRjno1JaeDVk8KBniY0vnMqdBG1BWRMsnUAhWxHJ4Jn5fXCltO2C5OV+x+jYocNhWw4Mgu3CVZp9erAEPsDEUsY4qaMitjRq6yJPn3nwkQRpFHGbXv187fPU/z8+BQ5/W4Qfg/qEmJKcp1T5VcrwLAlZQTomKAt/7xNlJIxlx9bCMVXnvk3Y8hB7kCTaSQmvjC8IXCHbJyMCFQV7qAKdmaihyV7zmpqWcDqfELQChm2KxmQJ1dPYJ1+jR+aYm3an5bPEoCdbsZauFF8qXHIhx8JKvRGgxPrTGHi8N5FcfuNsCA3xOkoSGX9Sxg6h5KEtoeFdkwf5BKqhJj5pcsBewAHkWng86BzAqt7R+a9TnyvLMWvmpI/0fkpRkbg731aWOnWOvbpz6f+nXSxGgrvs/B6xSgFPDo6Ty3ivL+CGitfK6XwdHA07TV/eK5vd5oyNe/Ay5wMdXiypEUWukN+jiuGmrVMkke/0DmXmtqqY3T45iLrqfgwRhXsT9kEdzcWVqM4OkAEqEzpjI0NHG74cslhQvDPbX/ZRp57/bcOuarHpCDakvzajblF2NXf0Wp0dO0NltExHFoY8myAlBu/SMn3VN6GF5faMq370MevoNi4eNRD8XiGUYh+QzVphqtLIeqyyt6W4MzTjwSLgAB0kITRYcG0KVi89iUgZMQdwIOfMUBmS/q1GpBtTFKqI2TwC3dz9oBnNzwi3Ru9yyNuX1mw1p0Sd8UNY0yVbdb7UrLrd0ldVrx2FWNVCEedkn0npe9xstIJvgMuRLA0DNN7vex3hfvGvV7qCuQi5ssNVYUkoyEjlFR3xCThuVCgwzK77JmvWMpeB0eR0O3SH9KK/1qaAkXK/bhyazFdT+WHrexsFYZuO5I1MiQWJzNDQ9r3act5mt0senky0dQ+Sb233mB+RX2AWxOf9SUcRtkKjiTvfSdcKHmLx7ns2F8MQdwFoKz7j4a7sPsuruL+BMHWuDj/VTfnVmG6nlzIjs92LVWKnZ010UOv156HJYQvhlSV+WzPv8EC/2A2LhBx0g1ZJ3vYQIu2G1AuhcZurtEOfbhcIsS6cq9P2YoRYTYTHeiXHNzKot/25uSAFs85GQhWyMz+2L3HBiszTmSdwtf0ZzHgGEBc90JD6yNfjwqF4FJ7AcuLYw5euNz/VEON3z2C021shibn6LGd4RjflA8XmBGSakJ0nkpplflYaQ9Awcv2eKYR4ebAVjhyErm4zHikUC5Vgtz/0UbghbNcX0RIEESW2GaGtFNYIYNPHJNtj4QMYLK7BrUOnzITF+Kx/FZG6oIeySnU+TJoZIkJBIqGLOYSkw+QuEbCbf1BM5nERddA8z+IFxYz7Goxg4XMpc3+L/Hx29UZzkNSY55Y2Swii1qxBWgBaRGpzI66cKfr6mHES+cbb9vNAdFPuBgJttyT/t3Jf+W5yRYt8M4VyIlEiUjAGqL3bqakDP5IwywMSpYeS1PmqWBsbUcV9bAavsLMnnEHvUue8GboIr+EeBMCk6XPBKFahrRZBTQq802IGDYOi++VjG7805Tleu2pX6H/IY/LDQVLnqVhxDaVonTvQqMffVvSwXp7EFNvR/XKxh+IJxO/W3tQeAi8Jn77E3itRU+tQEl8QQ65psW7FBQXujG6YYWK9XbPUPI/AkdVmaAuQQt7RPni0MhxB0Q8k6BqzhwbpyLMPa/XCkHdJLrO2xGupnrwYvt0OcF7Ia/hN9+HAekLtq8l+HcNGIY/JA2RPYOYGSEgAEVAgAACjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMApjbGVhcnRvbWFyawqAAw=="; + private static final String TYPE1_BYTES = "gAFfBwAAJSFQUy1BZG9iZUZvbnQtMS4wOiBUeXBlMVRlc3QgMDAxLjAwMAolJVRpdGxlOiBUeXBlMVRlc3QKJVZlcnNpb246IDAwMS4wMDAKJSVDcmVhdGlvbkRhdGU6IFR1ZSBGZWIgMjQgMTU6MzQ6MDUgMjAyNgolJUNyZWF0b3I6IEpheWF0aGlydGggUmFvIEQgVgolIDIwMjYtMi0yNDogQ3JlYXRlZCB3aXRoIEZvbnRGb3JnZSAoaHR0cDovL2ZvbnRmb3JnZS5vcmcpCiUgR2VuZXJhdGVkIGJ5IEZvbnRGb3JnZSAyMDI1MTAwOSAoaHR0cDovL2ZvbnRmb3JnZS5zZi5uZXQvKQolJUVuZENvbW1lbnRzCgoxMCBkaWN0IGJlZ2luCi9Gb250VHlwZSAxIGRlZgovRm9udE1hdHJpeCBbMC4wMDA0ODgyODEgMCAwIDAuMDAwNDg4MjgxIDAgMCBdcmVhZG9ubHkgZGVmCi9Gb250TmFtZSAvVHlwZTFUZXN0IGRlZgovRm9udEJCb3ggezEwMCAxMDAgNTAwIDIwMCB9cmVhZG9ubHkgZGVmCi9QYWludFR5cGUgMCBkZWYKL0ZvbnRJbmZvIDkgZGljdCBkdXAgYmVnaW4KIC92ZXJzaW9uICgwMDEuMDAwKSByZWFkb25seSBkZWYKIC9Ob3RpY2UgKCkgcmVhZG9ubHkgZGVmCiAvRnVsbE5hbWUgKFR5cGUxVGVzdCkgcmVhZG9ubHkgZGVmCiAvRmFtaWx5TmFtZSAoVHlwZTFUZXN0KSByZWFkb25seSBkZWYKIC9XZWlnaHQgKFJlZ3VsYXIpIHJlYWRvbmx5IGRlZgogL0l0YWxpY0FuZ2xlIDAgZGVmCiAvaXNGaXhlZFBpdGNoIGZhbHNlIGRlZgogL1VuZGVybGluZVBvc2l0aW9uIC0yMDQuOCBkZWYKIC9VbmRlcmxpbmVUaGlja25lc3MgMTAyLjQgZGVmCmVuZCByZWFkb25seSBkZWYKL0VuY29kaW5nIDI1NiBhcnJheQogMCAxIDI1NSB7IDEgaW5kZXggZXhjaCAvLm5vdGRlZiBwdXR9IGZvcgpkdXAgOS91bmkwMDA5IHB1dApkdXAgMTAvdW5pMDAwQSBwdXQKZHVwIDExL3VuaTAwMEIgcHV0CmR1cCAxMi91bmkwMDBDIHB1dApkdXAgMTMvdW5pMDAwRCBwdXQKZHVwIDMyL3NwYWNlIHB1dApkdXAgNDgvemVybyBwdXQKZHVwIDQ5L29uZSBwdXQKZHVwIDUwL3R3byBwdXQKZHVwIDUxL3RocmVlIHB1dApkdXAgNTIvZm91ciBwdXQKZHVwIDUzL2ZpdmUgcHV0CmR1cCA1NC9zaXggcHV0CmR1cCA1NS9zZXZlbiBwdXQKZHVwIDU2L2VpZ2h0IHB1dApkdXAgNTcvbmluZSBwdXQKZHVwIDY1L0EgcHV0CmR1cCA2Ni9CIHB1dApkdXAgNjcvQyBwdXQKZHVwIDY4L0QgcHV0CmR1cCA2OS9FIHB1dApkdXAgNzAvRiBwdXQKZHVwIDcxL0cgcHV0CmR1cCA3Mi9IIHB1dApkdXAgNzMvSSBwdXQKZHVwIDc0L0ogcHV0CmR1cCA3NS9LIHB1dApkdXAgNzYvTCBwdXQKZHVwIDc3L00gcHV0CmR1cCA3OC9OIHB1dApkdXAgNzkvTyBwdXQKZHVwIDgwL1AgcHV0CmR1cCA4MS9RIHB1dApkdXAgODIvUiBwdXQKZHVwIDgzL1MgcHV0CmR1cCA4NC9UIHB1dApkdXAgODUvVSBwdXQKZHVwIDg2L1YgcHV0CmR1cCA4Ny9XIHB1dApkdXAgODgvWCBwdXQKZHVwIDg5L1kgcHV0CmR1cCA5MC9aIHB1dApkdXAgOTcvYSBwdXQKZHVwIDk4L2IgcHV0CmR1cCA5OS9jIHB1dApkdXAgMTAwL2QgcHV0CmR1cCAxMDEvZSBwdXQKZHVwIDEwMi9mIHB1dApkdXAgMTAzL2cgcHV0CmR1cCAxMDQvaCBwdXQKZHVwIDEwNS9pIHB1dApkdXAgMTA2L2ogcHV0CmR1cCAxMDcvayBwdXQKZHVwIDEwOC9sIHB1dApkdXAgMTA5L20gcHV0CmR1cCAxMTAvbiBwdXQKZHVwIDExMS9vIHB1dApkdXAgMTEyL3AgcHV0CmR1cCAxMTMvcSBwdXQKZHVwIDExNC9yIHB1dApkdXAgMTE1L3MgcHV0CmR1cCAxMTYvdCBwdXQKZHVwIDExNy91IHB1dApkdXAgMTE4L3YgcHV0CmR1cCAxMTkvdyBwdXQKZHVwIDEyMC94IHB1dApkdXAgMTIxL3kgcHV0CmR1cCAxMjIveiBwdXQKZHVwIDEzMy91bmkwMDg1IHB1dApkdXAgMTczL3VuaTAwQUQgcHV0CnJlYWRvbmx5IGRlZgpjdXJyZW50ZGljdCBlbmQKY3VycmVudGZpbGUgZWV4ZWMKgAKNFgAAdD+EE/NjbKhan/77ULS7JzAqXMCrbi+Vm/INMgw3PCEo0KDOcHKx9nKgqjjDwOzrBLRMsXShwxYS1x/6IMkJVCVjeDcveVsL8pQfQ38Fn0GuBZjABRX+8YczNVfzLOMqnufUurZdpTQ/knB+LPzz6M5Eblraw5/Dfs5ktos1bODXEPRbHn8s12iryhQ2CDZodhoQCUZBYtBUwfa/LEajGbPGZFKHYxbBOpJkVtNmKVj9VnQNWFZzbmEE+BPtMZFWd3k07rHFRFrmv27lnlsZcItCh9tLm6+YgpGrwG/ed/zDHuFzCpT7xs7MdEpZqfajjiw9JKNtvO6XgdQ7y2rxwhv69q6/DIarClaEsha9+rFXfYTEkK8BcaUY1RI7beRwxnGx8Mt8pafG9aKKUpeQEJnqFv6Bk9Kz6/t+fAPkHdv8KUIErVw3AxMUFg63Ti+yeRa+1QQ7vDTTkV3O3Obim9olUKuQDhfmH2uWx+K3iBXFqwCLQTC/S/W/LDn9UCsxsphc4HA1txBMdO2NO8rPec0i5GLWOZZI5tOxhUsjPXLloUXLWOkU3I0wBTXW8TlAV2yBn+EPJ4XV14tyCoWHanYChLFqYcChHUHj+0GKluGz0gfPNQ4xf3u6+jyJLmvDFxiNxtSNAud0yBZx12ouXbVSeHGYXWk84KA/Sus2DDbG/E6H+kx2MfnEVMz2yZm/lW6a4w5Pevvdoxfj9BMLm1dBkDCxHQsjw1dbv7yBz3j6kibdv2I4JFxLoEW8qsxcH2SXXgJpM3KrNQlPRIqq8IKmxH3ZllsL/QQ8YJFg+KJjygCl9QOmrL/1FXx/JXmx0m6ynXaUfF1PwtiP7e1ISNfaExaiL1C9OtTK2T4h2WhRPW57ycPj/dYRfjKD7OvXXYQ+Co+s+jCWDRpbwGTLlDP0XQFMBNxx4cBogjdx6ouFfm8lXgfeQf/GC0CtQolv6YH1O06mdXeZ1hqAFwHALulGBr2WdAoV9wBD5SBSeMeIV3waNCP5P0xB7rP7uXTZv/oscgXK/61/3tsjyAXyZASQxvMf8uyWsThQA9TTEOM1o28z9bj6Yhs0Snwl2CxemFy3xgyidcNyBDRsCDFwYKjsejtr9sZGIXAFJK2djcN8aUa549kahghsM9NAB65fQFOwjWjPAZEnunfOsU/LXlWLjpPdul4+P/iseq37udumJc/dZMXm6KXDL6pesrEVc80hrFtnnPWTyaNUP1im/rQrwZvDnVJ0Yvg5XSGo6crcpdPE5pUviEVHB2W8aCI8W+2QrxfIXdN8g4bPW7FEjuoe424XoHpyyIkD1+0MyVNgCVywIoFjugUfAu5NKoFo+7+WyIvtrHlgEuMjEK70a2RhFp7wVuod4VD0EHJ+MWXUglKomSB7mA07KQiQDck++iAwwPmxKLmUdUItfeOcEji8RBoUkcTu9YuNij22HrsBxawVzE8Yz/ZRKqlbHpuuRHao7z2gIobOg8wHb/+lMC1o4vjwayYLSNfFyvf6XivjCkz2KUQlrLX+8WyTVA6JDHsI1xfhI+kr3NTJ2fZBKweobTge72fpGuiVEjV82l52w9UZBH9P70XJUqd1lYIstqLN+mDdVSJ1axsp9sDufLMEFWX9xUlCOyAdOefz02cniXnJcxIVdH8q01OLa5jxMlJ+CBeXO0D4j+r7qY8ZCTyeRT9eEpAykqEajhOoZPjhjtMlw5jgyZJ9COaM30bC3coKF93YSmRQaIkE9b1kw5dGlFvpGGYL4bAzo0SNSkIO00So24z3TISF9aJ1ssBn2072KoV99O/5QN5cWKdDvaSFkMJp0YFRHXNNHe1Zo0kV5Shq0uEygfikR1XDyuLuiycQSHWnFxVr2xSaMnA5CfKwfQu8hQ5ljap6spe1+Qh6Am4TkDa/CaO1QlQ03mRUcyyxFwGZJljNAkI7mVgG6DUlxxVSK8wJMH7otzpS0L2LYTHTG6LV0iXQBjNB4wuH5LqyqTgsiA8x7UASJFYY0RJGrD9moFp/ON0t+NPT6Rfw3Semq239oYpxMQr/mHkHfExaIA0AX31DjfMqWzj4bypd8B9zZFvOogC2EHPobmhYhjzFcbMdVXwnAENC0EVbEMdLzDQFeSFRpDwF6MJ5CyqFS4wD7PsyF/aVlPwi0rLnr6+xygmhFhtpsOi/zVT8qT0BCWBG4+9UZIh6MfLm+JWkLNQcEGmhTglxzIhntDTZkBO9MkYhs2vOM9pAWS9Pjcba8OR6+aiVMOZVReGm7FH3BixWPTHOeqMGpZYH09MRTScHecT7G/TgiZIcUReqM7OdmyExBR2IJ0tLuaEuHxyn7SC5Gdlcf9mL9Kz15sZFzTkZMjJAtCOTPMaHq/0eQIr/Ln1YMDd6O+ApErFrIGDQd4hBA2H8tIZMIuSct/KMzwj83QiB+wvvF/ec6vJCSEjU7FPayLiPt9ELxOKC0p3+k4wfc4PCX0VLozKllmEQ2+m7gkcI+mvWXGkjziNZtg7HsgEZbRTjH2H8LuaonHwNzpASWjt8e6deQzOglya7sRNKamea3s1oGPx1ZAZmGs5EZGtW0wT7h7WbQkM0Pv0Xcmb2d+0MWyOpvThTQOqog9wOrBZp8WQwFMbimf5h3nqrmgeRcmQL5YGrA0lLaShxIB9fiC8RBJ2ndEs8vW4MJU3u95ZJmQUwTDewI2WhsgV4CG9t74y790nLu/OWuRtm83j6FLEvtmgs0fMoE0WAtqrbzB2BLblT4GN7VXbZSE7K8B+zPTlx2Lw1mH4AwW0RK/6Hl6P8Y2KMsVSnLDDhKXxXSpaQQiiMV6lGAT4JLO9dh4cSAl0KVseZ62jLfcpN3T5UpiNAXr/OMqe0Vki/TLdwUCbfNL606j7Ki8v84s/ob3kaoYzKE5HDhgxKRYkd5NXmT321GSDdGW5OG4J5kwcF7KgJ7lY4HwLAkumbF4oMWMDz3FYRK5BxlYA65pSMxhCYKV5XMg2cPDPpLj8S55CI0Nq8WX8vnIY8JhSA2nbnTwzrSU30sN6BpXPILan6UBbNn91PgLOawA9Qnqebuq3BwqHOYAFecMIf3W/70HZ+iFc87AiEPK7kmn2Fm4az69PV3jVBZlIKAYGUHQvTm3+KYnNEGKSoOLB0bUGhklOy3vnbCuZt7Z2i0OpM2d7J9SqaCF1qrmIItsPKwGI6E1XyOhs1pVMwbWUzwHh4EU/wvU0bmcMinR9PWfvyusq4wUeP/rSQoRd7TfFNHZaCg9HJkgDdtjcTjpPBv/SPMNahf6KdknrkRunKE1+XxvdjKxo+7DXcijK8kPwDB5ZAv//BxZ6NEkippShPuCXnlP6huGWMNaM4Ascw+BU3R5P6SHODlrafn48qAqemBBnoUfmCw2mu+su+agNtVYx5MEacrmpyZGaQizFF/IAygncdllch8YCF6upwv0J8ehKxqIrdB5EpS8NwVmSl9HnL4Xkob5/T/PzCK5wCZYkECwKZEF1atSrcoKBeIHzn7FsCGLNDU7WPEseSYZvRNOUmunflZDysYSLBf7XMKkeaLijeB1qrD4P4fone9c9g1KrdZO597AT3DFfL/fTuKfvzLFS2dFJW3JxBhtIAEbVyd7hhE1ik1QulOOh7ZyPiuu60urRcrGU+fQVbXtdCVCZ+NMn/gvFbipQ+4w4Ry8xfBpyWQrIFTyzQ/hyBgecQnA7iJhrSiV56aCErXrHDhNCXvApKd9+rUB6w8Ltrp2ag/AIn3jA414KjLwvcvh2ztTYW9kGsA9c/TQYKYXrgbaWoSd7Z357cmY9I4UOEVad3Fxk8QZ2zWFdvydAyOrGU+7lo6fvirobuADWo1P6/4BSh7ZdzHxs5s3RvDAYrWwoO/WpmMdWzF5RvAXKXXcYhme5QBalZw270QjmB3VOvaZLQgkBFYV/USdi2Pl5yJRxmeRLHxW+9U3gi/vUxowUiFPaJ4hjgzuogct3zF0oJ3ZrSamVut85DZC88fY5RdC/GR53FR+nt51wzu+PPjh2UtQgZTdTD4tfmZ8MGc2BaHiQ+yhEPpXWi2sfkFwkVlOMnHrJOFwKil0Uy8OoHHiDlSQNX22YinDSmcY8silpqltMmVnibm4mQodIocSfNY+DMA6v5oiFFsXUCEiS5P8975OihDJIokTGt4E/z7VQag2vChKPSEMaK6NIhQjGn1qAVq26MDnXkLe1ScmDDVhdBH7F9OiPdGEoGz0R/EHqAiPC6LaGByJwvKm87QgQ0bP/CIWKAGOmV9b9GV18G8nN4+T1xu4bcatp+0RzLNXxPXBMvFYBDvhBv0JqM1Bzkff5VHc8Cly7qfWJ9GPfghDC1cdFtXf/qNzdrCNvEeT5BloKFZ6s7vkDEfGPLqgMxt5T3ywQPJhdh0J5m7HurwNA+BwBycoPts7h33elU2NlSNe7iRbTViFFLirJdYGSlZaelccnNqAJEWC55V5gxbZiA+nym/7KRlEeUpFmLtFBBPKHOsbhK5uNWw2w2cCCJuwlC8FcLoqsGaykaQimUJNrg4r5lDLwcdIPslHivyRywMP4f/rr509DL/ZsvrEZHXpC1UHiuUnpoYp5uD8Sd5t7TCG1pKC5Wqw6QnE3j5Ra0Zd3WaY9MJkTtmj5zAmPvBWtda/uhdM++vqUD5pm2FkmZjpcxdQ+Dw2ebN3eOKQsDcRrNc07d8qWC2XkqAxyolN21qFg4FBcM68Szc1/50XwOK8QyEBhG24XJbMDfwTGOtzKCExUaTje30HoD3Nzzee39rTe8t3aQJ56VVEdDPBsxqjrYHdbEPJFqwRSExlngpbIWjOiEyWu5YbcX0cQgk/TSlnRGV16o9pg2qBbcvWm5gpPZ0Kj3QrxPpmJIUqq6tYoc2zt2LZUbrTnopjvISz8+fm1jIQJt2vpRgnwLwC9uAsf3vBEoZvnxGQKVsG+1B6uVnlK4hKB0retrDEbdcGGpiACMc8IuXH+vQztfpFYR8aCY79MpHm+oHiIUWsBiosbWoYNOZv1D3T28W+A3xBrKABzqhwvymGnMrec/JzNPObaj4szJYJegxC7SyWuNbA8Je5xNon8SKM9KvtSh6MWDOhOMk4eTOyiuwUpUYRiMpazBdMe0msnhuE2qlRvQC08QaDEUZMN+EWEX63wnjN6CfXw5qIdlAHoG9ZypCQy7G3mYVqYLfywMdFBKE7JY+u31XsYp7UNuRkKAN6VGQux6Qym64jGLBO5M5gvzIHmUluXR+y9D6SzcOtocea4Y9/YXggV1MWSmW2dq7hvV58ZYbix1wWwuv10uG8OIUwiaMet6vVag6bIK1RG+7yPB3caz9gCcqoXnU4zlHNYqu6Zcg3226k91SSDWcvy5fHoKSZOtSwkIEb0m+dJURp7QFJ1AeuWoy6oxD6xAM995YBRyLCX3N9gHGiJZ33BbKq85oGrzQN8SeXs6j8+FrglsRqcjvdk7vgZCLRl7qcWsmkFXCzikZsHPn2stZMJm7bVfILwdMTF4A42iV9wy1idF4qXYzAZHLdEP9BDpdGMKRiICY8avlTv0nYMW1qgBz4cXrdmSrBTPV4Ts5/3yDP44/foH/rju3wLoWprGkQMU1cNj66iCCuKnhExh/bRb40wLE1N6H86Le9WYBUbJEJA5Knx3ELAboZ2xK+e8zK2jPZdcv1AGGfPE7V6G7/7YCOepiP4cv+RTru77QaND/sf+eX1N+pb8WDxgTMPQ5/AJeibBWmEO7zw+L8CBY+rAphiofoUzY9U32dBwf82tdPDwvqrtik+HqZfG74h6We9fRJrfUjR3J/5g7P8IpcJAI7XQ6N7IvujSTMCtDtOd5Pf4mzuvFj/y3A5cLXuFT8wvqtm+BaIMLCe0lgWg4h6vtXEeKGmMsCVnZP16QiuaftHgo71UaXH18Z/rafUcV1TirxHs9ddST6hNPZ31IMPVt6L2Z8p3ucsMRPjlqY4SpYjQClVEiUc6R2FPbGstgmH/OU+uDx7oNhRgFV91/h3p/9klZ1PXOHWzTtPN91bKkITWttjft16yhKkY9Qi2vXG/f8BdyNenjFoUHATcsr4n9UtjNFBIptLX7UQFf4KkNnbyeKV1Cqdl3L+NEz8/gXBeAXJgEEHNRBjz9sQLiyj82rL4wgonrKDtxvVRCBVOc3g2BYdnqsKpe7be5GFrhxyYIufRfj64AVfn9tExQC4y7NoQcMUCXHcDXSQaO4DI5kh4hWftJhYRQciNx7okOXWgQakxRz0BLMquW2zr16Eh+a1wMV+RRRbnOQUrdSuOptLp0HeXxDPBmd23mb+o9EaBZopKJcsZ5uVRnRPG5MpSDZBX/dD+ZcFhNxAkwftTJGxbHU2FvDZxZMau4ORlvRpe0jQqK0KAPjVhxygj+efvJhwqE7SjMk92H8tSlUcJNKTPRWlbzC+nXIfQmrUKWHB9WkJYPkygDNksMh29qtDKBoTrPFKd8TjtqAmPPYOARdnuOZ5GBLvk29+HuyIxm1reGzc0gAVPFM4+a5KuvdXP5RDQsfOYhrjfhryXiSjnafEa4Vr8bQV4tCrvM1wB6GwJNUMUIOLnZMKC1Wpa42CVKIjTsED1U11qk0c/z2pdQSW8bkze+S96EspK6YwFRpG+l2w57H45pXKCM/OtEoUFAsNYp0aUI03VLX0vayOPXwZjxfm0Sbxw8GlVveOcDjxP9PubGwJWB3zThbP3mWiciTTtQYHzJ12nStPUWrHe09we4wUPO5jzfTMVhXA/IGP6+xomVdm6BR6Iffn12RP9G+HUeWBB/vSHCL6FMcBSGEKo8kX9sVT43WPthgSJGxH+5cxSVsRZSpa3WEwJI7W6R30TpkUmhLMOROdMF9Oh6+G+73HC6kdt27xFupu9ZkFRCBxpa/QAQxLUKMekm90PTVMdiWBALcRG/5a+GbCJsEwasen3e8NWD8CDFqPPeDNQEhkDNzaWbvtiNttHV9ESmFC5Ty642U6taS7g3yI/aeMBjh6zebr+UmXv17XeNyO6N6KJBOstv8Ry1f/TmkuuQm3fXu3n99fkb5gLZUHIvUuQ+Q3vTnWrPkCt2Ezbp125iAKvlRyhk1pXLvrr/EA1OhtaGnLqEeq5XqBpwh6StXPQXk9qfZT9wNzhylhcM929FxVeGolbk+68k//XdoJhQg4nxUqYBavhqCEpNTvL+tCJP/DOX4MSAH+3VYuzPQGp03X4exr4V3CKf9vCE6BUiBvHjDic5quOPsKCg8oYqU98UUr7f8aF1IwzW5woFt5CcScIS7mxk93T9ZB3WasKAPuMgV97OniMMOLqV1q6x5g1kHT67a7lQG3t1rPIFzDu/YvRh0ro/ldnLOAG7kgcZgBxG+GUlSMJMFzA51cKCE6zulbDK0U4lGyF+0fQzK5J7JGySCNyspkbL2KoioJJPWZ9kEggqZlKQk9ps0HQbBLNKkad1ZVIyuJHrQTddcr6SB8TnUI+FEH9/dyBpfXAx3XCL9xWWSqzEsQ9h24a+oRnfkm9lEQDrR9YpPyFavVP2QhljRuQJ6TXwqrrBe0LbEL7kn3st3w98B99u4MtdP/mbeSDjk5/8iYKLsilg30/mllHfbIv8kue12+JmZJo01gCc+Dehm0rlMPMxeqqrCKlXj5n5FerqqPfkNs/rKfXFFlgu5cl/cBKqP17G4xLPLOLA7wHfabHXZB0vQDeE9SpIAZPmzR9VjXx/quSDUV8p4ABFQIAAAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKY2xlYXJ0b21hcmsKgAM="; public static void main(String[] args) throws Exception { From fd48f68a2cc69a0cccbc2503f29aad2de19ec098 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 26 Feb 2026 07:29:56 +0000 Subject: [PATCH 054/636] 8378166: C2 VectorAPI: NBody / particle life demo Co-authored-by: Paul Sandoz Reviewed-by: sviswanathan, psandoz, jbhateja --- .../jtreg/compiler/gallery/ParticleLife.java | 812 ++++++++++++++++++ .../compiler/gallery/TestParticleLife.java | 219 +++++ 2 files changed, 1031 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/gallery/ParticleLife.java create mode 100644 test/hotspot/jtreg/compiler/gallery/TestParticleLife.java diff --git a/test/hotspot/jtreg/compiler/gallery/ParticleLife.java b/test/hotspot/jtreg/compiler/gallery/ParticleLife.java new file mode 100644 index 00000000000..b6c393d328b --- /dev/null +++ b/test/hotspot/jtreg/compiler/gallery/ParticleLife.java @@ -0,0 +1,812 @@ +/* + * 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.gallery; + +import java.util.Random; + +import jdk.incubator.vector.*; + +import javax.swing.*; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Font; +import java.awt.Dimension; +import java.awt.BorderLayout; +import java.awt.RenderingHints; +import java.awt.geom.Ellipse2D; + +/** + * This is a visual demo of the Vector API, presenting an N-Body simulation, + * where every body (particle) interacts with every other body. + * + * This is a stand-alone test that you can run directly with: + * java --add-modules=jdk.incubator.vector ParticleLife.java + * + * On x86, you can also play with the UseAVX flag: + * java --add-modules=jdk.incubator.vector -XX:UseAVX=2 ParticleLife.java + * + * There is a JTREG test that automatically runs this demo, + * see {@link TestParticleLife}. + * + * The motivation for this demo is to present a realistic computation, + * such as a physics simulation, but which is currently not auto + * vectorized. It is thus a good candidate for the use of the Vector API. + * This demo is based on the work of Tom Mohr and others before him: + * https://particle-life.com/ + * https://www.youtube.com/@tom-mohr + * + * If you are interested in understanding the components, then look at these: + * - State.update: one step in the simulation. This consists of two parts: + * - updateForce*: This computes the forces between all the particles, which affects the velocities. + * We have multiple implementations (scalar and Vector API). + * This is the most expensive part of the simulation. + * - updatePositions: The velocities are added to the position. + */ +public class ParticleLife { + public static final Random RANDOM = new Random(123); + + // Increasing this number will make the demo slower. + public static int NUMBER_OF_PARTICLES = 2560; + public static int NUMBER_OF_GROUPS = 50; + + public static float ZOOM = 1500f; + + public static float SCALE1 = 0.02f; + public static float SCALE2 = 0.04f; + public static float SCALE3 = 1f; + public static float FORCE_PARTICLE = 0.0001f; + public static float FORCE_ORIGIN = 0.05f; + + // Dampening factor, applied to the velocity. + // 0: no velocity carried to next update, particles have no momentum + // 1: no dampening, can lead to increase in energy in the system over time. + public static float DAMPENING = 0.3f; + + // Time step size of each update. Larger makes the simulation faster. + // But if it is too large, this can lead to numerical instability. + public static float DT = 1f; + + enum Implementation { + Scalar, VectorAPI_Inner_Gather, VectorAPI_Inner_Rearranged, VectorAPI_Outer + } + + public static Implementation IMPLEMENTATION = Implementation.Scalar; + + enum PoleGen { + Default, Random, Rainbow, Sparse + } + + public static PoleGen POLE_GEN = PoleGen.Default; + + private static final VectorSpecies SPECIES_F = FloatVector.SPECIES_PREFERRED; + + public static State STATE = new State(); + + static void main() { + System.out.println("Welcome to the Particle Life Demo!"); + + // Set up a panel we can draw on, and put it in a window. + JFrame frame = new JFrame("Particle Life Demo (VectorAPI)"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(1400, 1000); + frame.setResizable(false); + frame.setLayout(new BorderLayout()); + + ParticlePanel panel = new ParticlePanel(); + panel.setPreferredSize(new Dimension(1000, 0)); + + JPanel controlPanel = new JPanel(); + controlPanel.setLayout(null); + controlPanel.setPreferredSize(new Dimension(400, 0)); + + int y = 10; + // ---------------------------- Reset Button ------------------------- + JButton button = new JButton("Reset"); + button.setBounds(10, y, 120, 30); + button.setToolTipText("Reset state, with new numbers of particles and groups, and new poles."); + controlPanel.add(button); + + button.addActionListener(_ -> { STATE = new State(); }); + y += 40; + + // ---------------------------- Computation Selector ------------------------- + { + JLabel label = new JLabel("Implementation"); + label.setBounds(10, y, 150, 30); + controlPanel.add(label); + + String[] options = {"Scalar", "VectorAPI Inner Gather", "VectorAPI Inner Rearranged", "VectorAPI Outer"}; + JComboBox comboBox = new JComboBox<>(options); + comboBox.setBounds(160, y, 210, 30); + comboBox.setToolTipText("Choose the implementation of the force computation. Using the VectorAPI should be faster."); + controlPanel.add(comboBox); + + comboBox.addActionListener(_ -> { + String selected = (String) comboBox.getSelectedItem(); + switch (selected) { + case "Scalar" -> IMPLEMENTATION = Implementation.Scalar; + case "VectorAPI Inner Gather" -> IMPLEMENTATION = Implementation.VectorAPI_Inner_Gather; + case "VectorAPI Inner Rearranged" -> IMPLEMENTATION = Implementation.VectorAPI_Inner_Rearranged; + case "VectorAPI Outer" -> IMPLEMENTATION = Implementation.VectorAPI_Outer; + } + }); + } + y += 40; + + // ---------------------------- Zoom Slider ------------------------- + JLabel zoomLabel = new JLabel("Zoom"); + zoomLabel.setBounds(10, y, 80, 30); + controlPanel.add(zoomLabel); + + JSlider zoomSlider = new JSlider(JSlider.HORIZONTAL, 10, 2500, (int)ZOOM); + zoomSlider.setBounds(160, y, 200, 30); + zoomSlider.setMajorTickSpacing(100); + zoomSlider.setPaintTicks(false); + zoomSlider.setPaintLabels(false); + controlPanel.add(zoomSlider); + + zoomSlider.addChangeListener(_ -> { + ZOOM = zoomSlider.getValue(); + }); + zoomSlider.setValue((int)ZOOM); + y += 40; + + // ---------------------------- :Particles Slider ------------------------- + JLabel particlesLabel = new JLabel("Particles"); + particlesLabel.setBounds(10, y, 150, 30); + controlPanel.add(particlesLabel); + + JSlider particlesSlider = new JSlider(JSlider.HORIZONTAL, 64, 10000, 64); + particlesSlider.setBounds(160, y, 200, 30); + particlesSlider.setMajorTickSpacing(100); + particlesSlider.setPaintTicks(false); + particlesSlider.setPaintLabels(false); + particlesSlider.setToolTipText("More particles make the simulation slower. Only applied on Reset."); + controlPanel.add(particlesSlider); + + particlesSlider.addChangeListener(_ -> { + NUMBER_OF_PARTICLES = particlesSlider.getValue() / 64 * 64; + particlesLabel.setText("Particles = " + NUMBER_OF_PARTICLES); + }); + particlesSlider.setValue(NUMBER_OF_PARTICLES); + y += 40; + + // ---------------------------- Groups Slider ------------------------- + JLabel groupsLabel = new JLabel("Groups"); + groupsLabel.setBounds(10, y, 150, 30); + controlPanel.add(groupsLabel); + + JSlider groupsSlider = new JSlider(JSlider.HORIZONTAL, 1, 100, 1); + groupsSlider.setBounds(160, y, 200, 30); + groupsSlider.setMajorTickSpacing(100); + groupsSlider.setPaintTicks(false); + groupsSlider.setPaintLabels(false); + groupsSlider.setToolTipText("More groups lead to more complex behavior. Only applied on Reset."); + controlPanel.add(groupsSlider); + + groupsSlider.addChangeListener(_ -> { + NUMBER_OF_GROUPS = groupsSlider.getValue(); + groupsLabel.setText("Groups = " + NUMBER_OF_GROUPS); + }); + groupsSlider.setValue(NUMBER_OF_GROUPS); + y += 40; + + // ---------------------------- Pole Gen Selector ------------------------- + { + JLabel label = new JLabel("Poles"); + label.setBounds(10, y, 150, 30); + controlPanel.add(label); + + String[] options = {"Default", "Random", "Rainbow", "Sparse"}; + JComboBox comboBox = new JComboBox<>(options); + comboBox.setBounds(160, y, 210, 30); + comboBox.setToolTipText("Poles define attraction/repulsion between groups. Only applied on Reset."); + controlPanel.add(comboBox); + + comboBox.addActionListener(_ -> { + String selected = (String) comboBox.getSelectedItem(); + switch (selected) { + case "Default" -> POLE_GEN = PoleGen.Default; + case "Random" -> POLE_GEN = PoleGen.Random; + case "Rainbow" -> POLE_GEN = PoleGen.Rainbow; + case "Sparse" -> POLE_GEN = PoleGen.Sparse; + } + }); + } + y += 40; + + // ---------------------------- Scale1 Slider ------------------------- + JLabel scale1Label = new JLabel("scale1"); + scale1Label.setBounds(10, y, 150, 30); + controlPanel.add(scale1Label); + + JSlider scale1Slider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); + scale1Slider.setBounds(160, y, 200, 30); + scale1Slider.setMajorTickSpacing(100); + scale1Slider.setPaintTicks(false); + scale1Slider.setPaintLabels(false); + scale1Slider.setToolTipText("Defines (inner) radius: repulsion between all particles."); + controlPanel.add(scale1Slider); + + scale1Slider.addChangeListener(_ -> { + SCALE1 = scale1Slider.getValue() * 0.002f + 0.001f; + scale1Label.setText("scale1 = " + String.format("%.4f", SCALE1)); + }); + scale1Slider.setValue(10); + y += 40; + + // ---------------------------- Scale2 Slider ------------------------- + JLabel scale2Label = new JLabel("scale2"); + scale2Label.setBounds(10, y, 150, 30); + controlPanel.add(scale2Label); + + JSlider scale2Slider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); + scale2Slider.setBounds(160, y, 200, 30); + scale2Slider.setMajorTickSpacing(100); + scale2Slider.setPaintTicks(false); + scale2Slider.setPaintLabels(false); + scale2Slider.setToolTipText("Defines (outer) radius: attraction/repulsion depending on poles/groups."); + controlPanel.add(scale2Slider); + + scale2Slider.addChangeListener(_ -> { + SCALE2 = scale2Slider.getValue() * 0.002f + 0.001f; + scale2Label.setText("scale2 = " + String.format("%.4f", SCALE2)); + }); + scale2Slider.setValue(20); + y += 40; + + // ---------------------------- Scale3 Slider ------------------------- + JLabel scale3Label = new JLabel("scale3"); + scale3Label.setBounds(10, y, 150, 30); + controlPanel.add(scale3Label); + + JSlider scale3Slider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); + scale3Slider.setBounds(160, y, 200, 30); + scale3Slider.setMajorTickSpacing(101); + scale3Slider.setPaintTicks(false); + scale3Slider.setPaintLabels(false); + scale3Slider.setToolTipText("Poles factor: adjust attraction/repulsion strenght."); + controlPanel.add(scale3Slider); + + scale3Slider.addChangeListener(_ -> { + SCALE3 = scale3Slider.getValue() * 0.02f; + scale3Label.setText("scale3 = " + String.format("%.4f", SCALE3)); + }); + scale3Slider.setValue(50); + y += 40; + + // ---------------------------- FORCE_PARTICLE Slider ------------------------- + JLabel forceParticlesLabel = new JLabel("fParticles"); + forceParticlesLabel.setBounds(10, y, 150, 30); + controlPanel.add(forceParticlesLabel); + + JSlider forceParticlesSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); + forceParticlesSlider.setBounds(160, y, 200, 30); + forceParticlesSlider.setMajorTickSpacing(100); + forceParticlesSlider.setPaintTicks(false); + forceParticlesSlider.setPaintLabels(false); + forceParticlesSlider.setToolTipText("Particles force factor: adjust force strength between particles."); + controlPanel.add(forceParticlesSlider); + + forceParticlesSlider.addChangeListener(_ -> { + FORCE_PARTICLE = forceParticlesSlider.getValue() * 0.00001f; + forceParticlesLabel.setText("fParticles = " + String.format("%.5f", FORCE_PARTICLE)); + }); + forceParticlesSlider.setValue(10); + y += 40; + + // ---------------------------- FORCE_ORIGIN Slider ------------------------- + JLabel forceOriginLabel = new JLabel("fOrigin"); + forceOriginLabel.setBounds(10, y, 150, 30); + controlPanel.add(forceOriginLabel); + + JSlider forceOriginSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); + forceOriginSlider.setBounds(160, y, 200, 30); + forceOriginSlider.setMajorTickSpacing(100); + forceOriginSlider.setPaintTicks(false); + forceOriginSlider.setPaintLabels(false); + forceOriginSlider.setToolTipText("Origin force factor: adjust force attracting all particles to the center/origin."); + controlPanel.add(forceOriginSlider); + + forceOriginSlider.addChangeListener(_ -> { + FORCE_ORIGIN = forceOriginSlider.getValue() * 0.0005f; + forceOriginLabel.setText("fOrigin = " + String.format("%.5f", FORCE_ORIGIN)); + }); + forceOriginSlider.setValue(50); + y += 40; + + // ---------------------------- DAMPENING Slider ------------------------- + JLabel dampeningLabel = new JLabel("dampening"); + dampeningLabel.setBounds(10, y, 150, 30); + controlPanel.add(dampeningLabel); + + JSlider dampeningSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); + dampeningSlider.setBounds(160, y, 200, 30); + dampeningSlider.setMajorTickSpacing(100); + dampeningSlider.setPaintTicks(false); + dampeningSlider.setPaintLabels(false); + dampeningSlider.setToolTipText("Dampening removes energy from the system over time. 1 = no dampening."); + controlPanel.add(dampeningSlider); + + dampeningSlider.addChangeListener(_ -> { + DAMPENING = dampeningSlider.getValue() * 0.01f; + dampeningLabel.setText("dampening = " + String.format("%.5f", DAMPENING)); + }); + dampeningSlider.setValue(30); + y += 40; + + // ---------------------------- DT Slider ------------------------- + JLabel dtLabel = new JLabel("dt"); + dtLabel.setBounds(10, y, 150, 30); + controlPanel.add(dtLabel); + + JSlider dtSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); + dtSlider.setBounds(160, y, 200, 30); + dtSlider.setMajorTickSpacing(100); + dtSlider.setPaintTicks(false); + dtSlider.setPaintLabels(false); + dtSlider.setToolTipText("Time delta between simulation steps. Small values lead to slow simulation, large values can lead to simulation instability."); + controlPanel.add(dtSlider); + + dtSlider.addChangeListener(_ -> { + DT = dtSlider.getValue() * 0.04f + 0.001f; + dtLabel.setText("dt = " + String.format("%.3f", DT)); + }); + dtSlider.setValue(25); + y += 40; + + // ---------------------------- Force Panel ------------------------- + ForcePanel forcePanel = new ForcePanel(); + forcePanel.setBounds(10, y, 350, 350); + forcePanel.setToolTipText("Displays the attraction/repulsion between the groups. Only updated on Reset."); + controlPanel.add(forcePanel); + y += 360; + + frame.add(panel, BorderLayout.WEST); + frame.add(controlPanel, BorderLayout.CENTER); + frame.setVisible(true); + + System.out.println("Running Demo..."); + try { + // Tight loop where we redraw the panel as fast as possible. + while (true) { + Thread.sleep(1); + STATE.update(); + panel.repaint(); + forcePanel.repaint(); + } + } catch (InterruptedException e) { + System.out.println("Interrputed, terminating demo."); + } finally { + System.out.println("Shut down demo."); + frame.setVisible(false); + frame.dispose(); + } + } + + /** + * State of the simulation. + */ + public static class State { + public long lastTime; + public float fps; + + // "struct of arrays" approach allows adjacent vector loads. + public float[] x; + public float[] y; + public float[] vx; + public float[] vy; + public int[] group; // group index of the particle + + public Color[] colors; // color of the group + + // Matrix of the poles: defines attraction/repulsion between groups i and j + public float[][] poles; + public float[][] polesT; // transpose of poles + public float[] polesScratch; + + public State() { + int n = NUMBER_OF_PARTICLES; + int g = NUMBER_OF_GROUPS; + x = new float[n]; + y = new float[n]; + vx = new float[n]; + vy = new float[n]; + group = new int[n]; + + for (int i = 0; i < n; i++) { + x[i] = 0.2f * (RANDOM.nextFloat() - 0.5f); + y[i] = 0.2f * (RANDOM.nextFloat() - 0.5f); + group[i] = RANDOM.nextInt(g); + } + + colors = new Color[g]; + for (int i = 0; i < g; i++) { + float h = i / (float)g; + colors[i] = Color.getHSBColor(h, 1f, 1f); + } + + poles = new float[g][g]; + polesT = new float[g][g]; + polesScratch = new float[n]; + for (int i = 0; i < g; i++) { + for (int j = 0; j < g; j++) { + poles[i][j] = poleGen(i, j, g); + polesT[j][i] = poles[i][j]; + } + } + + // Set up the FPS tracker + lastTime = System.nanoTime(); + } + + public static float poleGen(int i, int j, int g) { + int offset = (i - j + g) % g; + return switch (POLE_GEN) { + case PoleGen.Default -> (i == j) ? -1f : RANDOM.nextFloat() * 2f - 1f; + case PoleGen.Random -> RANDOM.nextFloat() * 2f - 1f; + case PoleGen.Rainbow -> (i == j) ? -1f : ((offset == 1) ? -0.5f : 0f); + case PoleGen.Sparse -> (i == j) ? -1f : (RANDOM.nextInt(g) <= 2 ? -0.5f : 0.3f); + }; + } + + public void update() { + long nowTime = System.nanoTime(); + float newFPS = 1e9f / (nowTime - lastTime); + fps = 0.9f * fps + 0.1f * newFPS; + lastTime = nowTime; + + switch (IMPLEMENTATION) { + case Implementation.Scalar -> updateForcesScalar(); + case Implementation.VectorAPI_Inner_Gather -> updateForcesVectorAPI_Inner_Gather(); + case Implementation.VectorAPI_Inner_Rearranged -> updateForcesVectorAPI_Inner_Rearranged(); + case Implementation.VectorAPI_Outer -> updateForcesVectorAPI_Outer(); + default -> throw new RuntimeException("not implemented"); + } + + updatePositions(); + } + + public void updateForcesScalar() { + for (int i = 0; i < x.length; i++) { + float pix = x[i]; + float piy = y[i]; + float pivx = vx[i]; + float pivy = vy[i]; + for (int j = 0; j < x.length; j++) { + float pjx = x[j]; + float pjy = y[j]; + + float dx = pix - pjx; + float dy = piy - pjy; + float d = (float)Math.sqrt(dx * dx + dy * dy); + + // Ignoring d=0 avoids division by zero. + // This would happen for i==j which we want to exclude anyway, + // of if two particles have identical position. + if (d > 0f) { + float pole = poles[group[i]][group[j]]; + // If the distance is very large, the force is zero. + float f = 0; + if (d < SCALE1) { + // Small distance: repell all particles + f = (SCALE1 - d) / SCALE1; + } else if (d < SCALE1 + SCALE2) { + // Medium distance: attract/repell according to pole + f = (d - SCALE1) / SCALE2 * pole * SCALE3; + } else if (d < SCALE1 + 2f * SCALE2) { + // Medium distance: attract/repell according to pole + f = ((SCALE1 + 2f * SCALE2) - d) / SCALE2 * pole * SCALE3; + } + // The force is adjustable by the user via FORCE_PARTICLE. + // Additionally we need to respect the DT factor of the simulation + // time step. Finally, we need to normalize dx and dy by dividing + // by d. + f *= FORCE_PARTICLE * DT / d; + pivx += dx * f; + pivy += dy * f; + } + } + vx[i] = pivx; + vy[i] = pivy; + } + } + + // Inner loop vectorization, the inner loop is vectorized. + public void updateForcesVectorAPI_Inner_Gather() { + // We don't want to deal with tail loops, so we just assert that the number of + // particles is a multiple of the vector length. + if (x.length % SPECIES_F.length() != 0) { + throw new RuntimeException("Number of particles is not a multiple of the vector length."); + } + + for (int i = 0; i < x.length; i++) { + float pix = x[i]; + float piy = y[i]; + + // We consider the force of multiple (j) particles on particle i. + var fx = FloatVector.zero(SPECIES_F); + var fy = FloatVector.zero(SPECIES_F); + + for (int j = 0; j < x.length; j += SPECIES_F.length()) { + var pjx = FloatVector.fromArray(SPECIES_F, x, j); + var pjy = FloatVector.fromArray(SPECIES_F, y, j); + + var dx = pjx.sub(pix).neg(); + var dy = pjy.sub(piy).neg(); + var d2 = ( dx.mul(dx) ).add( dy.mul(dy) ); + var d = d2.lanewise(VectorOperators.SQRT); + + // We directly gather the poles from the matrix. + var pole = FloatVector.fromArray(SPECIES_F, poles[group[i]], 0, group, j); + + // We need to compute all 3 piece-wise liner parts. + var poleDivScale2 = pole.mul(SCALE3 / SCALE2); + var f1 = d.sub(SCALE1).neg().mul(1f / SCALE1); + var f2 = d.sub(SCALE1).mul(poleDivScale2); + var f3 = d.sub(SCALE1 + SCALE2 * 2f).neg().mul(poleDivScale2); + + // And we need to perform all checks, for the boundaries of the piece-wise parts. + var f0Mask = d.compare(VectorOperators.GT, 0); + var f1Mask = d.compare(VectorOperators.LT, SCALE1); + var f2Mask = d.compare(VectorOperators.LT, SCALE1 + SCALE2); + var f3Mask = d.compare(VectorOperators.LT, SCALE1 + SCALE2 * 2f); + var f03Mask = f0Mask.and(f3Mask); + + // Then, we put together the 3 middle parts. + var f12 = f2.blend(f1, f1Mask); + var f123 = f3.blend(f12, f2Mask); + + f123 = f123.mul(FORCE_PARTICLE * DT).div(d); + + // And we only apply the middle (non-zero) parts if the mask is enabled. + fx = fx.add(dx.mul(f123), f03Mask); + fy = fy.add(dy.mul(f123), f03Mask); + } + // We need to add the force of all the (j) particles onto i's velocity. + vx[i] += fx.reduceLanes(VectorOperators.ADD); + vy[i] += fy.reduceLanes(VectorOperators.ADD); + } + } + + // Inner loop vectorization, the inner loop is vectorized. But instead of gathering the poles + // in the inner loop, we rearrange it in the outer loop, so the inner loop has a linear access. + public void updateForcesVectorAPI_Inner_Rearranged() { + // We don't want to deal with tail loops, so we just assert that the number of + // particles is a multiple of the vector length. + if (x.length % SPECIES_F.length() != 0) { + throw new RuntimeException("Number of particles is not a multiple of the vector length."); + } + + for (int i = 0; i < x.length; i++) { + // Rearrange data to avoid rearrange in the loop. + // We could also use the VectorAPI for this loop, but it is not even necessary for speedups. + float[] polesgi = poles[group[i]]; + for (int j = 0; j < x.length; j++) { + polesScratch[j] = polesgi[group[j]]; + } + + float pix = x[i]; + float piy = y[i]; + + // We consider the force of multiple (j) particles on particle i. + var fx = FloatVector.zero(SPECIES_F); + var fy = FloatVector.zero(SPECIES_F); + + for (int j = 0; j < x.length; j += SPECIES_F.length()) { + var pjx = FloatVector.fromArray(SPECIES_F, x, j); + var pjy = FloatVector.fromArray(SPECIES_F, y, j); + + var dx = pjx.sub(pix).neg(); + var dy = pjy.sub(piy).neg(); + var d2 = ( dx.mul(dx) ).add( dy.mul(dy) ); + var d = d2.lanewise(VectorOperators.SQRT); + + // We can now access the poles from scratch in a linear access, avoiding the + // repeated gather in each inner loop. This helps especially if gather is + // not supported on a platform. But it also improves the access pattern on + // platforms where gather would be supported, but linear access is faster. + var pole = FloatVector.fromArray(SPECIES_F, polesScratch, j); + + // We need to compute all 3 piece-wise liner parts. + var poleDivScale2 = pole.mul(SCALE3 / SCALE2); + var f1 = d.sub(SCALE1).neg().mul(1f / SCALE1); + var f2 = d.sub(SCALE1).mul(poleDivScale2); + var f3 = d.sub(SCALE1 + SCALE2 * 2f).neg().mul(poleDivScale2); + + // And we need to perform all checks, for the boundaries of the piece-wise parts. + var f0Mask = d.compare(VectorOperators.GT, 0); + var f1Mask = d.compare(VectorOperators.LT, SCALE1); + var f2Mask = d.compare(VectorOperators.LT, SCALE1 + SCALE2); + var f3Mask = d.compare(VectorOperators.LT, SCALE1 + SCALE2 * 2f); + var f03Mask = f0Mask.and(f3Mask); + + // Then, we put together the 3 middle parts. + var f12 = f2.blend(f1, f1Mask); + var f123 = f3.blend(f12, f2Mask); + + f123 = f123.mul(FORCE_PARTICLE * DT).div(d); + + // And we only apply the middle (non-zero) parts if the mask is enabled. + fx = fx.add(dx.mul(f123), f03Mask); + fy = fy.add(dy.mul(f123), f03Mask); + } + // We need to add the force of all the (j) particles onto i's velocity. + vx[i] += fx.reduceLanes(VectorOperators.ADD); + vy[i] += fy.reduceLanes(VectorOperators.ADD); + } + } + + // Instead of vectorizing the inner loop, we can also vectorize the outer loop. + public void updateForcesVectorAPI_Outer() { + // We don't want to deal with tail loops, so we just assert that the number of + // particles is a multiple of the vector length. + if (x.length % SPECIES_F.length() != 0) { + throw new RuntimeException("Number of particles is not a multiple of the vector length."); + } + + for (int i = 0; i < x.length; i += SPECIES_F.length()) { + var pix = FloatVector.fromArray(SPECIES_F, x, i); + var piy = FloatVector.fromArray(SPECIES_F, y, i); + var pivx = FloatVector.fromArray(SPECIES_F, vx, i); + var pivy = FloatVector.fromArray(SPECIES_F, vy, i); + + // Let's consider the force of the j particle on all of the i particles in the vector. + for (int j = 0; j < x.length; j++) { + float pjx = x[j]; + float pjy = y[j]; + + var dx = pix.sub(pjx); + var dy = piy.sub(pjy); + var d2 = ( dx.mul(dx) ).add( dy.mul(dy) ); + var d = d2.lanewise(VectorOperators.SQRT); + + // We need to access transpose of poles, because we need to access adjacent i's + var pole = FloatVector.fromArray(SPECIES_F, polesT[group[j]], 0, group, i); + + var poleDivScale2 = pole.mul(SCALE3 / SCALE2); + var f1 = d.sub(SCALE1).neg().mul(1f / SCALE1); + var f2 = d.sub(SCALE1).mul(poleDivScale2); + var f3 = d.sub(SCALE1 + SCALE2 * 2f).neg().mul(poleDivScale2); + + var f0Mask = d.compare(VectorOperators.GT, 0); + var f1Mask = d.compare(VectorOperators.LT, SCALE1); + var f2Mask = d.compare(VectorOperators.LT, SCALE1 + SCALE2); + var f3Mask = d.compare(VectorOperators.LT, SCALE1 + SCALE2 * 2f); + var f03Mask = f0Mask.and(f3Mask); + + var f12 = f2.blend(f1, f1Mask); + var f123 = f3.blend(f12, f2Mask); + + f123 = f123.mul(FORCE_PARTICLE * DT).div(d); + pivx = pivx.add(dx.mul(f123), f03Mask); + pivy = pivy.add(dy.mul(f123), f03Mask); + } + pivx.intoArray(vx, i); + pivy.intoArray(vy, i); + } + } + + // The loop is so simple that it can be auto vectorized + public void updatePositions() { + float effectiveDampening = (float)Math.pow(DAMPENING, DT); + for (int i = 0; i < x.length; i++) { + float px = x[i]; + float py = y[i]; + float pvx = vx[i]; + float pvy = vy[i]; + + // Force that pulls to origin, based on distance. + float d = (float)Math.sqrt(px * px + py * py); + pvx -= px * d * FORCE_ORIGIN * DT; + pvy -= py * d * FORCE_ORIGIN * DT; + + // Update position and put drag on speed + px += pvx * DT; + py += pvy * DT; + pvx *= effectiveDampening; + pvy *= effectiveDampening; + + x[i] = px; + y[i] = py; + vx[i] = pvx; + vy[i] = pvy; + } + } + } + + /** + * This panel displays the simulation. + **/ + public static class ParticlePanel extends JPanel { + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g; + + // Rendering settings for smoother circles + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + g2d.setColor(new Color(0, 0, 0)); + g2d.fillRect(0, 0, 1000, 1000); + + // Draw position of points + for (int i = 0; i < STATE.x.length; i++) { + g2d.setColor(STATE.colors[STATE.group[i]]); + int xx = (int)(STATE.x[i] * ZOOM + 500f); + int yy = (int)(STATE.y[i] * ZOOM + 500f); + //g2d.fillRect(xx - 3, yy - 3, 6, 6); + g2d.fill(new Ellipse2D.Double(xx - 3, yy - 3, 6, 6)); + } + + g2d.setColor(new Color(0, 0, 0)); + g2d.fillRect(0, 0, 150, 35); + g2d.setColor(new Color(255, 255, 255)); + g2d.setFont(new Font("Consolas", Font.PLAIN, 30)); + g2d.drawString("FPS: " + (int)Math.floor(STATE.fps), 0, 30); + + g2d.setColor(new Color(255, 255, 255)); + int r1 = (int)(ZOOM * SCALE1); + int r2 = (int)(ZOOM * (SCALE1 + SCALE2 * 2f)); + g2d.drawOval(900 - r1, 100 - r1, 2 * r1, 2 * r1); + g2d.drawOval(900 - r2, 100 - r2, 2 * r2, 2 * r2); + } + } + + /** + * This panel displays the pole matrix. + **/ + public static class ForcePanel extends JPanel { + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g; + + g2d.setColor(new Color(0, 0, 0)); + g2d.fillRect(0, 0, 350, 350); + + int nGroups = STATE.poles.length; + int scale = (int)(300f / nGroups); + for (int i = 0; i < nGroups; i++) { + g2d.setColor(STATE.colors[i]); + g2d.fillRect(scale * (i + 1), 0, scale, scale); + g2d.fillRect(0, scale * (i + 1), scale, scale); + + for (int j = 0; j < nGroups; j++) { + float p = STATE.poles[i][j]; + float cr = Math.max(0, p); + float cg = Math.max(0, -p); + g2d.setColor(new Color(cr, cg, 0)); + g2d.fillRect(scale * (i + 1), scale * (j + 1), scale, scale); + } + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/gallery/TestParticleLife.java b/test/hotspot/jtreg/compiler/gallery/TestParticleLife.java new file mode 100644 index 00000000000..e0fb6164acf --- /dev/null +++ b/test/hotspot/jtreg/compiler/gallery/TestParticleLife.java @@ -0,0 +1,219 @@ +/* + * 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 id=ir + * @bug 8378166 + * @summary Visual example of the Vector API: NBody / Particle Life simulation. + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver ${test.main.class} ir + */ + +/* + * @test id=visual + * @key headful + * @library /test/lib / + * @modules jdk.incubator.vector + * @run main ${test.main.class} visual + */ + +package compiler.gallery; + +import jdk.test.lib.Utils; + +import compiler.lib.ir_framework.*; + +/** + * This test is the JTREG version for automatic verification of the stand-alone + * {@link ParticleLife}. If you just want to run the demo and play with it, + * go look at the documentation in {@link ParticleLife}. + * Here, we launch both a visual version that just runs for a few seconds, to see + * that there are no crashes, but we don't do any specific verification. + * We also have an IR test, that ensures that we get vectorization. + */ +public class TestParticleLife { + public static void main(String[] args) throws InterruptedException { + String mode = args[0]; + System.out.println("Running JTREG test in mode: " + mode); + + switch (mode) { + case "ir" -> runIR(); + case "visual" -> runVisual(); + default -> throw new RuntimeException("Unknown mode: " + mode); + } + } + + private static void runIR() { + System.out.println("Testing with IR rules..."); + TestFramework.runWithFlags("-XX:CompileCommand=inline,compiler.gallery.ParticleLife$State::update*", + "--add-modules=jdk.incubator.vector"); + } + + private static void runVisual() throws InterruptedException { + System.out.println("Testing with 2d Graphics (visual)..."); + + // We will not do anything special here, just launch the application, + // tell it to run for 10 second, interrupt it and let it shut down. + Thread thread = new Thread() { + public void run() { + ParticleLife.main(); + } + }; + thread.setDaemon(true); + thread.start(); + Thread.sleep(Utils.adjustTimeout(10000)); // let demo run for 10 seconds + thread.interrupt(); + Thread.sleep(Utils.adjustTimeout(1000)); // allow demo 1 second for shutdown + } + + // ---------------------- For the IR testing part only -------------------------------- + ParticleLife.State state = new ParticleLife.State(); + + @Test + @Warmup(100) + @IR(counts = {IRNode.REPLICATE_F, "> 0", + IRNode.LOAD_VECTOR_F, "> 0", + IRNode.SUB_VF, "> 0", + IRNode.MUL_VF, "> 0", + IRNode.ADD_VF, "> 0", + IRNode.SQRT_VF, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIf = {"AlignVector", "false"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + private void testIR_updatePositions() { + // This call should inline given the CompileCommand above. + // We expect auto vectorization of the relatively simple loop. + state.updatePositions(); + } + + @Test + @Warmup(10) + @IR(counts = {IRNode.REPLICATE_F, "= 0", + IRNode.LOAD_VECTOR_F, "= 0", + IRNode.REPLICATE_I, "= 0", + IRNode.LOAD_VECTOR_I, "= 0", + IRNode.ADD_VI, "= 0", + IRNode.LOAD_VECTOR_GATHER, "= 0", + IRNode.SUB_VF, "= 0", + IRNode.MUL_VF, "= 0", + IRNode.ADD_VF, "= 0", + IRNode.NEG_VF, "= 0", + IRNode.SQRT_VF, "= 0", + IRNode.DIV_VF, "= 0", + IRNode.VECTOR_MASK_CMP, "= 0", + IRNode.VECTOR_MASK_CAST, "= 0", + IRNode.AND_V_MASK, "= 0", + IRNode.VECTOR_BLEND_F, "= 0", + IRNode.STORE_VECTOR, "= 0", + IRNode.ADD_REDUCTION_VF, "= 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + private void testIR_updateForcesScalar() { + // This call should inline given the CompileCommand above. + // We expect no vectorization, though it may in principle be possible + // to auto vectorize one day. + state.updateForcesScalar(); + } + + @Test + @Warmup(10) + @IR(counts = {IRNode.REPLICATE_F, "> 0", + IRNode.LOAD_VECTOR_F, "> 0", + IRNode.REPLICATE_I, "> 0", + IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.LOAD_VECTOR_GATHER, "> 0", + IRNode.SUB_VF, "> 0", + IRNode.MUL_VF, "> 0", + IRNode.ADD_VF, "> 0", + IRNode.NEG_VF, "> 0", + IRNode.SQRT_VF, "> 0", + IRNode.DIV_VF, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_MASK_CAST, "> 0", + IRNode.VECTOR_BLEND_F, "> 0", + IRNode.ADD_REDUCTION_VF, "> 0"}, // instead we reduce the vector to a scalar + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeature = {"avx2", "true"}) + private void testIR_updateForcesVectorAPI_Inner_Gather() { + // This call should inline given the CompileCommand above. + // We expect the VectorAPI calls to intrinsify. + state.updateForcesVectorAPI_Inner_Gather(); + } + + @Test + @Warmup(10) + @IR(counts = {IRNode.REPLICATE_F, "> 0", + IRNode.LOAD_VECTOR_F, "> 0", + IRNode.REPLICATE_I, "= 0", // No gather operation + IRNode.LOAD_VECTOR_I, "= 0", // No gather operation + IRNode.ADD_VI, "= 0", // No gather operation + IRNode.LOAD_VECTOR_GATHER, "= 0", // No gather operation + IRNode.SUB_VF, "> 0", + IRNode.MUL_VF, "> 0", + IRNode.ADD_VF, "> 0", + IRNode.NEG_VF, "> 0", + IRNode.SQRT_VF, "> 0", + IRNode.DIV_VF, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_MASK_CAST, "> 0", + IRNode.VECTOR_BLEND_F, "> 0", + IRNode.ADD_REDUCTION_VF, "> 0"}, // instead we reduce the vector to a scalar + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + private void testIR_updateForcesVectorAPI_Inner_Rearranged() { + // This call should inline given the CompileCommand above. + // We expect the VectorAPI calls to intrinsify. + state.updateForcesVectorAPI_Inner_Rearranged(); + } + + + @Test + @Warmup(10) + @IR(counts = {IRNode.REPLICATE_F, "> 0", + IRNode.LOAD_VECTOR_F, "> 0", + IRNode.REPLICATE_I, "> 0", + IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.LOAD_VECTOR_GATHER, "> 0", + IRNode.SUB_VF, "> 0", + IRNode.MUL_VF, "> 0", + IRNode.ADD_VF, "> 0", + IRNode.NEG_VF, "> 0", + IRNode.SQRT_VF, "> 0", + IRNode.DIV_VF, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_MASK_CAST, "> 0", + IRNode.VECTOR_BLEND_F, "> 0", + IRNode.STORE_VECTOR, "> 0", // store back a vector + IRNode.ADD_REDUCTION_VF, "= 0"}, // and no reduction operation + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeature = {"avx2", "true"}) + private void testIR_updateForcesVectorAPI_Outer() { + // This call should inline given the CompileCommand above. + // We expect the VectorAPI calls to intrinsify. + state.updateForcesVectorAPI_Outer(); + } +} From a39a1f10f75c15b93a709eca7bfae2d808cf7b91 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 26 Feb 2026 08:10:37 +0000 Subject: [PATCH 055/636] 8268850: AST model for 'var' variables should more closely adhere to the source code Reviewed-by: vromero --- .../classes/com/sun/source/tree/Tree.java | 7 + .../com/sun/source/tree/TreeVisitor.java | 9 + .../com/sun/source/tree/VarTypeTree.java | 36 ++ .../com/sun/source/tree/VariableTree.java | 11 +- .../sun/source/util/SimpleTreeVisitor.java | 15 + .../com/sun/source/util/TreeScanner.java | 15 + .../com/sun/tools/javac/comp/Analyzer.java | 2 - .../com/sun/tools/javac/comp/Attr.java | 60 +-- .../com/sun/tools/javac/comp/AttrRecover.java | 5 - .../com/sun/tools/javac/comp/Lower.java | 5 +- .../com/sun/tools/javac/comp/MemberEnter.java | 12 +- .../com/sun/tools/javac/comp/TreeDiffer.java | 6 + .../sun/tools/javac/parser/JavacParser.java | 39 +- .../com/sun/tools/javac/tree/JCTree.java | 38 +- .../com/sun/tools/javac/tree/Pretty.java | 9 + .../com/sun/tools/javac/tree/TreeCopier.java | 8 +- .../com/sun/tools/javac/tree/TreeInfo.java | 2 - .../com/sun/tools/javac/tree/TreeMaker.java | 10 +- .../com/sun/tools/javac/tree/TreeScanner.java | 3 + .../sun/tools/javac/tree/TreeTranslator.java | 4 + .../share/classes/jdk/jshell/Eval.java | 2 +- .../jdk/jshell/SourceCodeAnalysisImpl.java | 4 +- .../failures/target/VarVariables-old.out | 4 +- .../failures/target/VarVariables.out | 4 +- .../tools/javac/lvti/VarAccessibility.java | 393 ++++++++++++++++++ .../tools/javac/lvti/VarWarnings.java | 193 +++++++++ .../javac/parser/DeclarationEndPositions.java | 8 +- .../tools/javac/parser/JavacParserTest.java | 37 +- .../patterns/BindingPatternVarTypeModel.java | 2 +- .../javac/patterns/InstanceOfModelTest.java | 2 +- test/langtools/tools/javac/tree/VarTree.java | 32 +- .../tools/javac/tree/VarWarnPosition.java | 3 + .../tools/javac/tree/VarWarnPosition.out | 6 +- 33 files changed, 873 insertions(+), 113 deletions(-) create mode 100644 src/jdk.compiler/share/classes/com/sun/source/tree/VarTypeTree.java create mode 100644 test/langtools/tools/javac/lvti/VarAccessibility.java create mode 100644 test/langtools/tools/javac/lvti/VarWarnings.java diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java index 845203c8735..152883b4930 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java @@ -266,6 +266,13 @@ public interface Tree { */ PRIMITIVE_TYPE(PrimitiveTypeTree.class), + /** + * Used for instances of {@link VarTypeTree}. + * + * @since 27 + */ + VAR_TYPE(VarTypeTree.class), + /** * Used for instances of {@link ReturnTree}. */ diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java index a2a33ccf8eb..f2a067971d1 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java @@ -498,6 +498,15 @@ public interface TreeVisitor { */ R visitPrimitiveType(PrimitiveTypeTree node, P p); + /** + * Visits a {@code VarTypeTree} node. + * @param node the node being visited + * @param p a parameter value + * @return a result value + * @since 27 + */ + R visitVarType(VarTypeTree node, P p); + /** * Visits a {@code TypeParameterTree} node. * @param node the node being visited diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/VarTypeTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/VarTypeTree.java new file mode 100644 index 00000000000..bfe13ebdce1 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/VarTypeTree.java @@ -0,0 +1,36 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.source.tree; + +import javax.lang.model.type.TypeKind; + +/** + * A tree node for a {@code var} contextual keyword used as a type. + * + * @since 27 + */ +public interface VarTypeTree extends Tree { +} diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java index a1191b2f8e6..11dc41577be 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/VariableTree.java @@ -65,10 +65,13 @@ public interface VariableTree extends StatementTree { */ ExpressionTree getNameExpression(); - /** - * Returns the type of the variable being declared. - * @return the type - */ + /// {@return the type of the variable being declared.} + /// + /// @apiNote + /// The type of the variable can be one of the following: + /// - if the variable is declared using {@code var}, then the returned value is a {@link VarTypeTree}, + /// - if the variable is a lambda parameter declared without a type (i.e. relying on type inferrence), then the returned value is {@code null}, + /// - otherwise, the variable is declared with an explicit type, and the returned value is that type. Tree getType(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java index 77305d56b1e..917df861b70 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java @@ -803,6 +803,21 @@ public class SimpleTreeVisitor implements TreeVisitor { return defaultAction(node, p); } + /** + * {@inheritDoc} + * + * @implSpec This implementation calls {@code defaultAction}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code defaultAction} + * @since 27 + */ + @Override + public R visitVarType(VarTypeTree node, P p) { + return defaultAction(node, p); + } + /** * {@inheritDoc} * diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java index 29e81c206e0..ca8b785d8cb 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java @@ -939,6 +939,21 @@ public class TreeScanner implements TreeVisitor { return null; } + /** + * {@inheritDoc} + * + * @implSpec This implementation returns {@code null}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of scanning + * @since 27 + */ + @Override + public R visitVarType(VarTypeTree node, P p) { + return null; + } + /** * {@inheritDoc} * diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java index f45e8500000..fb5576a82e1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -370,7 +370,6 @@ public class Analyzer { */ JCVariableDecl rewriteVarType(JCVariableDecl oldTree) { JCVariableDecl newTree = copier.copy(oldTree); - newTree.vartype = null; return newTree; } @@ -751,7 +750,6 @@ public class Analyzer { if (oldLambda.paramKind == ParameterKind.IMPLICIT) { //reset implicit lambda parameters (whose type might have been set during attr) newLambda.paramKind = ParameterKind.IMPLICIT; - newLambda.params.forEach(p -> p.vartype = null); } return newLambda; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 48e0238fb07..83b684e1225 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1263,18 +1263,18 @@ public class Attr extends JCTree.Visitor { // parameters have already been entered env.info.scope.enter(tree.sym); } else { - if (tree.isImplicitlyTyped() && (tree.getModifiers().flags & PARAMETER) == 0) { + if (tree.isImplicitlyTyped() && (tree.getModifiers().flags & PARAMETER) == 0 && tree.type == null) { if (tree.init == null) { //cannot use 'var' without initializer log.error(tree, Errors.CantInferLocalVarType(tree.name, Fragments.LocalMissingInit)); - tree.vartype = make.at(tree.pos()).Erroneous(); + tree.type = syms.errType; } else { Fragment msg = canInferLocalVarType(tree); if (msg != null) { //cannot use 'var' with initializer which require an explicit target //(e.g. lambda, method reference, array initializer). log.error(tree, Errors.CantInferLocalVarType(tree.name, msg)); - tree.vartype = make.at(tree.pos()).Erroneous(); + tree.type = syms.errType; } } } @@ -1323,7 +1323,7 @@ public class Attr extends JCTree.Visitor { } } if (tree.isImplicitlyTyped()) { - setSyntheticVariableType(tree, v.type); + setupImplicitlyTypedVariable(tree, v.type); } } result = tree.type = v.type; @@ -1597,7 +1597,8 @@ public class Attr extends JCTree.Visitor { } if (tree.var.isImplicitlyTyped()) { Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name); - setSyntheticVariableType(tree.var, inferredType); + tree.var.type = inferredType; + setupImplicitlyTypedVariable(tree.var, inferredType); } attribStat(tree.var, loopEnv); chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type); @@ -3223,8 +3224,10 @@ public class Attr extends JCTree.Visitor { Type argType = arityMismatch ? syms.errType : actuals.head; - if (params.head.isImplicitlyTyped()) { - setSyntheticVariableType(params.head, argType); + if (params.head.type == null && + params.head.isImplicitlyTyped()) { //error recovery + params.head.type = argType; + setupImplicitlyTypedVariable(params.head, argType); } params.head.sym = null; actuals = actuals.isEmpty() ? @@ -3442,7 +3445,7 @@ public class Attr extends JCTree.Visitor { JCLambda lambda = (JCLambda)tree; List argtypes = List.nil(); for (JCVariableDecl param : lambda.params) { - argtypes = param.vartype != null && param.vartype.type != null ? + argtypes = !param.isImplicitlyTyped() && param.vartype.type != null ? argtypes.append(param.vartype.type) : argtypes.append(syms.errType); } @@ -4218,7 +4221,7 @@ public class Attr extends JCTree.Visitor { public void visitBindingPattern(JCBindingPattern tree) { Type type; - if (tree.var.vartype != null) { + if (!tree.var.isImplicitlyTyped()) { type = attribType(tree.var.vartype, env); } else { type = resultInfo.pt; @@ -4231,11 +4234,11 @@ public class Attr extends JCTree.Visitor { if (chk.checkUnique(tree.var.pos(), v, env.info.scope)) { chk.checkTransparentVar(tree.var.pos(), v, env.info.scope); } - chk.validate(tree.var.vartype, env, true); if (tree.var.isImplicitlyTyped()) { - setSyntheticVariableType(tree.var, type == Type.noType ? syms.errType - : type); + setupImplicitlyTypedVariable(tree.var, type == Type.noType ? syms.errType + : type); } + chk.validate(tree.var.vartype, env, true); annotate.annotateLater(tree.var.mods.annotations, env, v); if (!tree.var.isImplicitlyTyped()) { annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v); @@ -4254,7 +4257,7 @@ public class Attr extends JCTree.Visitor { public void visitRecordPattern(JCRecordPattern tree) { Type site; - if (tree.deconstructor == null) { + if (tree.deconstructor.hasTag(VARTYPE)) { log.error(tree.pos(), Errors.DeconstructionPatternVarNotAllowed); tree.record = syms.errSymbol; site = tree.type = types.createErrorType(tree.record.type); @@ -4270,6 +4273,7 @@ public class Attr extends JCTree.Visitor { } tree.type = tree.deconstructor.type = type; site = types.capture(tree.type); + chk.validate(tree.deconstructor, env, true); } List expectedRecordTypes; @@ -4315,7 +4319,6 @@ public class Attr extends JCTree.Visitor { } finally { localEnv.info.scope.leave(); } - chk.validate(tree.deconstructor, env, true); result = tree.type; matchBindings = new MatchBindings(outBindings.toList(), List.nil()); } @@ -5731,15 +5734,20 @@ public class Attr extends JCTree.Visitor { return types.capture(type); } - private void setSyntheticVariableType(JCVariableDecl tree, Type type) { - if (type.isErroneous()) { - tree.vartype = make.at(tree.pos()).Erroneous(); - } else if (tree.declaredUsingVar()) { - Assert.check(tree.typePos != Position.NOPOS); - tree.vartype = make.at(tree.typePos).Type(type); - } else { - tree.vartype = make.at(tree.pos()).Type(type); + private void setupImplicitlyTypedVariable(JCVariableDecl tree, Type type) { + Assert.check(tree.isImplicitlyTyped()); + + type.complete(); + + if (tree.vartype == null) { + return ; } + + Assert.check(tree.vartype.hasTag(VARTYPE)); + + JCVarType vartype = (JCVarType) tree.vartype; + + vartype.type = type; } public void validateTypeAnnotations(JCTree tree, boolean sigOnly) { @@ -6068,9 +6076,6 @@ public class Attr extends JCTree.Visitor { that.sym = new VarSymbol(0, that.name, that.type, syms.noSymbol); that.sym.adr = 0; } - if (that.vartype == null) { - that.vartype = make.at(Position.NOPOS).Erroneous(); - } super.visitVarDef(that); } @@ -6145,6 +6150,11 @@ public class Attr extends JCTree.Visitor { syms.noSymbol); } } + + @Override + public void visitVarType(JCVarType that) { + initTypeIfNeeded(that); + } } // diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java index 716345fa077..092035c84dd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrRecover.java @@ -117,11 +117,6 @@ public class AttrRecover { ? formals.head : ((ArrayType) formals.head).elemtype; if (arg.hasTag(JCTree.Tag.LAMBDA)) { final JCTree.JCLambda lambda = (JCLambda) arg; - if (lambda.paramKind == JCLambda.ParameterKind.IMPLICIT) { - for (JCVariableDecl var : lambda.params) { - var.vartype = null; //reset type - } - } if (types.isFunctionalInterface(formal)) { Type functionalType = types.findDescriptorType(formal); boolean voidCompatible = functionalType.getReturnType().hasTag(TypeTag.VOID); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 69161fd682c..be603ffc9ca 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -3692,11 +3692,12 @@ public class Lower extends TreeTranslator { if (tree.var.type.isPrimitive()) vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit); else - vardefinit = make.TypeCast(tree.var.type, vardefinit); + vardefinit = transTypes.coerce(attrEnv, vardefinit, tree.var.type); JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods, tree.var.name, tree.var.vartype, - vardefinit).setType(tree.var.type); + vardefinit, + tree.var.declKind).setType(tree.var.type); indexDef.sym = tree.var.sym; JCBlock body = make.Block(0, List.of(indexDef, tree.body)); body.bracePos = TreeInfo.endPos(tree.body); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java index e02fb9849c9..cb0e6d4676b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java @@ -114,7 +114,7 @@ public class MemberEnter extends JCTree.Visitor { ListBuffer argbuf = new ListBuffer<>(); for (List l = params; l.nonEmpty(); l = l.tail) { memberEnter(l.head, env); - argbuf.append(l.head.vartype.type); + argbuf.append(l.head.sym.type); } // Attribute result type, if one is given. @@ -276,9 +276,13 @@ public class MemberEnter extends JCTree.Visitor { tree.vartype.type = atype.makeVarargs(); } WriteableScope enclScope = enter.enterScope(env); - Type vartype = tree.isImplicitlyTyped() - ? env.info.scope.owner.kind == MTH ? Type.noType : syms.errType - : tree.vartype.type; + Type vartype = switch (tree.declKind) { + case IMPLICIT -> tree.type; + case EXPLICIT -> tree.vartype.type; + case VAR -> tree.type != null ? tree.type + : env.info.scope.owner.kind == MTH ? Type.noType + : syms.errType; + }; Name name = tree.name; VarSymbol v = new VarSymbol(0, name, vartype, enclScope.owner); v.flags_field = chk.checkFlags(tree.mods.flags | tree.declKind.additionalSymbolFlags, v, tree); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java index bbc12f1fe80..4890b749a90 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java @@ -93,6 +93,7 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCTypeUnion; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.JCTree.JCUses; +import com.sun.tools.javac.tree.JCTree.JCVarType; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.tree.JCTree.JCWildcard; @@ -631,6 +632,11 @@ public class TreeDiffer extends TreeScanner { result = tree.typetag == that.typetag; } + @Override + public void visitVarType(JCVarType tree) { + result = true; + } + @Override public void visitTypeIntersection(JCTypeIntersection tree) { JCTypeIntersection that = (JCTypeIntersection) parameter; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 40f91a004fd..16f26c836f8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -977,13 +977,11 @@ public class JavacParser implements Parser { pattern = toP(F.at(token.pos).AnyPattern()); } else { - int varTypePos = Position.NOPOS; if (parsedType == null) { boolean var = token.kind == IDENTIFIER && token.name() == names.var; e = unannotatedType(allowVar, TYPE | NOLAMBDA); if (var) { - varTypePos = e.pos; - e = null; + e = replaceTypeWithVar(e); } } else { e = parsedType; @@ -1021,12 +1019,9 @@ public class JavacParser implements Parser { name = names.empty; } JCVariableDecl var = toP(F.at(varPos).VarDef(mods, name, e, null, - varTypePos != Position.NOPOS ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT, - varTypePos)); - if (e == null) { - if (var.name == names.underscore && !allowVar) { - log.error(DiagnosticFlag.SYNTAX, varPos, Errors.UseOfUnderscoreNotAllowed); - } + e.hasTag(VARTYPE) ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT)); + if (var.name == names.underscore && !allowVar) { + log.error(DiagnosticFlag.SYNTAX, varPos, Errors.UseOfUnderscoreNotAllowed); } pattern = toP(F.at(pos).BindingPattern(var)); } @@ -2178,8 +2173,7 @@ public class JavacParser implements Parser { && restrictedTypeName(param.vartype, true) != null) { checkSourceLevel(param.pos, Feature.VAR_SYNTAX_IMPLICIT_LAMBDAS); param.declKind = JCVariableDecl.DeclKind.VAR; - param.typePos = TreeInfo.getStartPos(param.vartype); - param.vartype = null; + param.vartype = replaceTypeWithVar(param.vartype); } } } @@ -3792,7 +3786,6 @@ public class JavacParser implements Parser { */ JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods, JCExpression type, Name name, boolean reqInit, Comment dc, boolean localDecl, boolean compound) { - boolean declaredUsingVar = false; JCExpression init = null; type = bracketsOpt(type); @@ -3818,7 +3811,6 @@ public class JavacParser implements Parser { syntaxError(token.pos, Errors.Expected(EQ)); } - int varTypePos = Position.NOPOS; JCTree elemType = TreeInfo.innermostType(type, true); if (elemType.hasTag(IDENT)) { Name typeName = ((JCIdent) elemType).name; @@ -3829,21 +3821,27 @@ public class JavacParser implements Parser { //error - 'var' and arrays reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedArray(typeName)); } else { - declaredUsingVar = true; - varTypePos = elemType.pos; + type = replaceTypeWithVar(type); + if (compound) //error - 'var' in compound local var decl reportSyntaxError(elemType.pos, Errors.RestrictedTypeNotAllowedCompound(typeName)); - //implicit type - type = null; } } } JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, init, - declaredUsingVar ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT, varTypePos)); + type.hasTag(VARTYPE) ? JCVariableDecl.DeclKind.VAR : JCVariableDecl.DeclKind.EXPLICIT)); return attach(result, dc); } + JCExpression replaceTypeWithVar(JCExpression type) { + JCVarType varType = F.at(type).VarType(); + + varType.endpos = type.endpos; + + return varType; + } + Name restrictedTypeName(JCExpression e, boolean shouldWarn) { switch (e.getTag()) { case IDENT: @@ -3954,11 +3952,10 @@ public class JavacParser implements Parser { name = names.empty; } - boolean declaredUsingVar = type != null && type.hasTag(IDENT) && ((JCIdent)type).name == names.var; + boolean declaredUsingVar = type != null && type.hasTag(VARTYPE); JCVariableDecl.DeclKind declKind = declaredUsingVar ? JCVariableDecl.DeclKind.VAR : type != null ? JCVariableDecl.DeclKind.EXPLICIT : JCVariableDecl.DeclKind.IMPLICIT; - int typePos = type != null ? type.pos : pos; - return toP(F.at(pos).VarDef(mods, name, type, null, declKind, typePos)); + return toP(F.at(pos).VarDef(mods, name, type, null, declKind)); } /** Resources = Resource { ";" Resources } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index c54507bed3f..f0a8b6034df 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -277,6 +277,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { */ TYPEIDENT, + /** 'var' type. + */ + VARTYPE, + /** Array types, of type TypeArray. */ TYPEARRAY, @@ -1038,15 +1042,13 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public VarSymbol sym; /** how the variable's type was declared */ public DeclKind declKind; - /** a source code position to use for "vartype" when null (can happen if declKind != EXPLICIT) */ - public int typePos; protected JCVariableDecl(JCModifiers mods, Name name, JCExpression vartype, JCExpression init, VarSymbol sym) { - this(mods, name, vartype, init, sym, DeclKind.EXPLICIT, Position.NOPOS); + this(mods, name, vartype, init, sym, DeclKind.EXPLICIT); } protected JCVariableDecl(JCModifiers mods, @@ -1054,21 +1056,19 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { JCExpression vartype, JCExpression init, VarSymbol sym, - DeclKind declKind, - int typePos) { + DeclKind declKind) { this.mods = mods; this.name = name; this.vartype = vartype; this.init = init; this.sym = sym; this.declKind = declKind; - this.typePos = typePos; } protected JCVariableDecl(JCModifiers mods, JCExpression nameexpr, JCExpression vartype) { - this(mods, null, vartype, null, null, DeclKind.EXPLICIT, Position.NOPOS); + this(mods, null, vartype, null, null, DeclKind.EXPLICIT); this.nameexpr = nameexpr; if (nameexpr.hasTag(Tag.IDENT)) { this.name = ((JCIdent)nameexpr).name; @@ -1078,8 +1078,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { } } + @DefinedBy(Api.COMPILER_TREE) public boolean isImplicitlyTyped() { - return vartype == null; + return declKind != DeclKind.EXPLICIT; } public boolean declaredUsingVar() { @@ -2047,7 +2048,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { this.params = params; this.body = body; if (params.isEmpty() || - params.head.vartype != null) { + !params.head.isImplicitlyTyped()) { paramKind = ParameterKind.EXPLICIT; } else { paramKind = ParameterKind.IMPLICIT; @@ -2827,6 +2828,24 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { } } + public static class JCVarType extends JCExpression implements VarTypeTree { + public JCVarType() {} + @Override + public void accept(Visitor v) { v.visitVarType(this); } + + @DefinedBy(Api.COMPILER_TREE) + public Kind getKind() { return Kind.VAR_TYPE; } + + @Override @DefinedBy(Api.COMPILER_TREE) + public R accept(TreeVisitor v, D d) { + return v.visitVarType(this, d); + } + @Override + public Tag getTag() { + return VARTYPE; + } + } + /** * An array type, A[] */ @@ -3604,6 +3623,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public void visitIdent(JCIdent that) { visitTree(that); } public void visitLiteral(JCLiteral that) { visitTree(that); } public void visitTypeIdent(JCPrimitiveTypeTree that) { visitTree(that); } + public void visitVarType(JCVarType that) { visitTree(that); } public void visitTypeArray(JCArrayTypeTree that) { visitTree(that); } public void visitTypeApply(JCTypeApply that) { visitTree(that); } public void visitTypeUnion(JCTypeUnion that) { visitTree(that); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java index d953663a6d7..c22dfd61637 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java @@ -1529,6 +1529,15 @@ public class Pretty extends JCTree.Visitor { } } + @Override + public void visitVarType(JCVarType that) { + try { + print("var"); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public void visitTypeArray(JCArrayTypeTree tree) { try { printBaseElementType(tree); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java index 9efc6a9d895..95e2976f14e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java @@ -480,6 +480,12 @@ public class TreeCopier

implements TreeVisitor { return M.at(t.pos).TypeIdent(t.typetag); } + @DefinedBy(Api.COMPILER_TREE) + public JCTree visitVarType(VarTypeTree node, P p) { + JCVarType t = (JCVarType) node; + return M.at(t.pos).VarType(); + } + @DefinedBy(Api.COMPILER_TREE) public JCTree visitTypeParameter(TypeParameterTree node, P p) { JCTypeParameter t = (JCTypeParameter) node; @@ -551,7 +557,7 @@ public class TreeCopier

implements TreeVisitor { JCExpression vartype = copy(t.vartype, p); if (t.nameexpr == null) { JCExpression init = copy(t.init, p); - return M.at(t.pos).VarDef(mods, t.name, vartype, init, t.declKind, t.typePos); + return M.at(t.pos).VarDef(mods, t.name, vartype, init, t.declKind); } else { JCExpression nameexpr = copy(t.nameexpr, p); return M.at(t.pos).ReceiverVarDef(mods, nameexpr, vartype); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index aa616f3f580..ffb259c6a8b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -621,8 +621,6 @@ public class TreeInfo { return node.mods.pos; } else if (node.vartype != null) { return getStartPos(node.vartype); - } else if (node.typePos != Position.NOPOS) { - return node.typePos; } break; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index 0e71df99bdc..77391493ad2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -238,8 +238,8 @@ public class TreeMaker implements JCTree.Factory { } public JCVariableDecl VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init, - JCVariableDecl.DeclKind declKind, int typePos) { - JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null, declKind, typePos); + JCVariableDecl.DeclKind declKind) { + JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null, declKind); tree.pos = pos; return tree; } @@ -566,6 +566,12 @@ public class TreeMaker implements JCTree.Factory { return tree; } + public JCVarType VarType() { + JCVarType tree = new JCVarType(); + tree.pos = pos; + return tree; + } + public JCArrayTypeTree TypeArray(JCExpression elemtype) { JCArrayTypeTree tree = new JCArrayTypeTree(elemtype); tree.pos = pos; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java index b9ae35da9df..0d1216217c8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java @@ -361,6 +361,9 @@ public class TreeScanner extends Visitor { public void visitTypeIdent(JCPrimitiveTypeTree tree) { } + public void visitVarType(JCVarType tree) { + } + public void visitTypeArray(JCArrayTypeTree tree) { scan(tree.elemtype); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java index 63778fb42ff..5b06e76bf33 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java @@ -418,6 +418,10 @@ public class TreeTranslator extends JCTree.Visitor { result = tree; } + public void visitVarType(JCVarType tree) { + result = tree; + } + public void visitTypeArray(JCArrayTypeTree tree) { tree.elemtype = translate(tree.elemtype); result = tree; diff --git a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java index bc6f6d30236..70f3b70fd66 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java @@ -337,7 +337,7 @@ class Eval { Set anonymousClasses = Collections.emptySet(); StringBuilder sbBrackets = new StringBuilder(); Tree baseType = vt.getType(); - if (baseType != null) { + if (vt.getType() != null && vt.getType().getKind() != Tree.Kind.VAR_TYPE) { tds.scan(baseType); // Not dependent on initializer fullTypeName = displayType = typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false); while (baseType instanceof ArrayTypeTree) { diff --git a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java index 1cf0c85702f..35faab231af 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java @@ -816,7 +816,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis { SourcePositions sp = trees.getSourcePositions(); List tokens = new ArrayList<>(); Context ctx = new Context(); - ctx.put(DiagnosticListener.class, (DiagnosticListener) d -> {}); + ctx.put(DiagnosticListener.class, (DiagnosticListener) d -> {}); Scanner scanner = ScannerFactory.instance(ctx).newScanner(wrappedCode, false); Log.instance(ctx).useSource(cut.getSourceFile()); scanner.nextToken(); @@ -932,7 +932,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis { @Override public Void visitVariable(VariableTree node, Void p) { int pos = ((JCTree) node).pos; - if (sp.getEndPosition(cut, node.getType()) == (-1)) { + if (node.getType() != null && node.getType().getKind() == Kind.VAR_TYPE) { Token varCandidate = findTokensBefore(pos, TokenKind.IDENTIFIER); if (varCandidate != null && "var".equals(varCandidate.name().toString())) { addKeyword.accept(varCandidate); diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out index 5b16d28d053..2b447e4a842 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables-old.out @@ -4,6 +4,4 @@ VarVariables.java:24:14: compiler.err.annotation.type.not.applicable VarVariables.java:27:14: compiler.err.annotation.type.not.applicable VarVariables.java:32:14: compiler.err.annotation.type.not.applicable VarVariables.java:36:37: compiler.err.annotation.type.not.applicable -VarVariables.java:32:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.AutoCloseable -VarVariables.java:36:41: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.String -8 errors +6 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out index 2586e7883d6..7f055040855 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/target/VarVariables.out @@ -4,6 +4,4 @@ VarVariables.java:24:14: compiler.err.annotation.type.not.applicable VarVariables.java:27:14: compiler.err.annotation.type.not.applicable VarVariables.java:32:14: compiler.err.annotation.type.not.applicable VarVariables.java:36:37: compiler.err.annotation.type.not.applicable -VarVariables.java:32:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.AutoCloseable -VarVariables.java:36:41: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @VarVariables.TA), java.lang, @VarVariables.TA java.lang.String -8 errors +6 errors diff --git a/test/langtools/tools/javac/lvti/VarAccessibility.java b/test/langtools/tools/javac/lvti/VarAccessibility.java new file mode 100644 index 00000000000..d39d706faea --- /dev/null +++ b/test/langtools/tools/javac/lvti/VarAccessibility.java @@ -0,0 +1,393 @@ +/* + * 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 + * @summary Check behavior of var when the inferred type is inaccessible + * @library /tools/lib + * @modules java.logging + * java.sql + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit VarAccessibility + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Objects; + +import org.junit.jupiter.api.Test; + +import toolbox.JavacTask; +import toolbox.JavaTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class VarAccessibility { + + private static final ToolBox tb = new ToolBox(); + + @Test + public void testInaccessibleInferredTypeLocalVariable() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + public class API { + public static PackagePrivate get() { + return new PackagePrivate(); + } + } + """, + """ + package p1; + class PackagePrivate { + public String toString() { + return "pass"; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + var v = API.get(); + System.out.println(v); + } + } + """); + + Files.createDirectories(classes); + + new JavacTask(tb) + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll(); + + var out = new JavaTask(tb) + .classpath(classes.toString()) + .className("p2.Test") + .run() + .writeAll() + .getOutputLines(Task.OutputKind.STDOUT); + + var expectedOut = List.of("pass"); + + if (!Objects.equals(expectedOut, out)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + out); + + } + } + + @Test + public void testInaccessibleInferredTypeForEachIterable() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + import java.util.List; + public class API { + public static Iterable get() { + return List.of(new PackagePrivate()); + } + } + """, + """ + package p1; + class PackagePrivate { + public String toString() { + return "pass"; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + for (var v : API.get()) { + System.out.println(v); + } + } + } + """); + + Files.createDirectories(classes); + + var out = new JavacTask(tb) + .options("-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + var expectedOut = List.of( + "Test.java:5:22: compiler.err.not.def.public.cant.access: p1.PackagePrivate, p1", + "1 error"); + + if (!Objects.equals(expectedOut, out)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + out); + + } + } + + @Test + public void testInaccessibleInferredTypeForEachArray() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + import java.util.List; + public class API { + public static PackagePrivate[] get() { + return new PackagePrivate[] {new PackagePrivate()}; + } + } + """, + """ + package p1; + class PackagePrivate { + public String toString() { + return "pass"; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + for (var v : API.get()) { + System.out.println(v); + } + } + } + """); + + Files.createDirectories(classes); + + new JavacTask(tb) + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll(); + + var out = new JavaTask(tb) + .classpath(classes.toString()) + .className("p2.Test") + .run() + .writeAll() + .getOutputLines(Task.OutputKind.STDOUT); + + var expectedOut = List.of("pass"); + + if (!Objects.equals(expectedOut, out)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + out); + + } + } + + @Test + public void testInaccessibleInferredTypeLambda() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + import java.util.function.Consumer; + public class API { + public static void run(Consumer c) { + c.accept(new PackagePrivate()); + } + } + """, + """ + package p1; + class PackagePrivate { + public String toString() { + return "pass"; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + API.run(v -> System.out.println(v)); + API.run((var v) -> System.out.println(v)); + } + } + """); + + Files.createDirectories(classes); + + var out = new JavacTask(tb) + .options("-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + var expectedOut = List.of( + "Test.java:5:17: compiler.err.not.def.public.cant.access: p1.PackagePrivate, p1", + "Test.java:6:17: compiler.err.not.def.public.cant.access: p1.PackagePrivate, p1", + "2 errors"); + + if (!Objects.equals(expectedOut, out)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + out); + + } + } + + @Test + public void testInaccessibleInferredTypeCanUse() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + public class API { + public static PackagePrivate get() { + return null; + } + } + """, + """ + package p1; + class PackagePrivate { + public String toString() { + return "pass"; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + var v = API.get(); + System.out.println(v.toString()); + } + } + """); + + Files.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + var expectedOut = List.of( + "Test.java:6:29: compiler.err.not.def.access.class.intf.cant.access: toString(), p1.PackagePrivate", + "1 error" + ); + + if (!Objects.equals(expectedOut, log)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + log); + + } + } + + @Test + public void testInaccessibleInferredBindingPattern() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + public record API(PackagePrivate p) { + public static API create() { + return new API(new PackagePrivate()); + } + } + """, + """ + package p1; + class PackagePrivate { + public String toString() { + return "pass"; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + Object o = API.create(); + if (o instanceof API(var v)) { + System.out.println(v.toString()); + } + } + } + """); + + Files.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + var expectedOut = List.of( + "Test.java:7:33: compiler.err.not.def.access.class.intf.cant.access: toString(), p1.PackagePrivate", + "1 error" + ); + + if (!Objects.equals(expectedOut, log)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + log); + + } + } +} diff --git a/test/langtools/tools/javac/lvti/VarWarnings.java b/test/langtools/tools/javac/lvti/VarWarnings.java new file mode 100644 index 00000000000..a1fbadc0393 --- /dev/null +++ b/test/langtools/tools/javac/lvti/VarWarnings.java @@ -0,0 +1,193 @@ +/* + * 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 + * @summary Check behavior of warnings related to var + * @library /tools/lib + * @modules java.logging + * java.sql + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit VarWarnings + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Objects; + +import org.junit.jupiter.api.Test; + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class VarWarnings { + + private static final ToolBox tb = new ToolBox(); + + @Test + public void testDeprecationWarning() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + import java.util.List; + import java.util.function.Consumer; + @SuppressWarnings("deprecation") + public class API { + public static DeprOutter get() { + return new DeprOutter<>(); + } + public static Iterable> getIterable() { + return null; + } + public static void run(Consumer> task) { + } + } + """, + """ + package p1; + @Deprecated + public class DeprInner { + } + """, + """ + package p1; + @Deprecated + public class DeprOutter { + public T get() { + return null; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + var v1 = API.get(); + API.run(v -> v.get().toString()); + API.run((var v) -> v.get().toString()); + for (var v2 : API.getIterable()) {} + } + } + """); + + Files.createDirectories(classes); + + var out = new JavacTask(tb) + .options("-XDrawDiagnostics", + "-Werror", + "-Xlint:deprecation") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + var expectedOut = List.of(""); + + if (!Objects.equals(expectedOut, out)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + out); + + } + } + + @Test + public void testRawTypeWarning() throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + package p1; + import java.util.List; + import java.util.function.Consumer; + @SuppressWarnings("rawtypes") + public class API { + public static RawOutter get() { + return new RawOutter<>(); + } + public static Iterable> getIterable() { + return null; + } + public static void run(Consumer> task) { + } + } + """, + """ + package p1; + public class RawInner { + } + """, + """ + package p1; + public class RawOutter { + public T get() { + return null; + } + } + """, + """ + package p2; + import p1.API; + public class Test { + public static void main(String... args) { + var v1 = API.get(); + API.run(v -> v.get().toString()); + API.run((var v) -> v.get().toString()); + for (var v2 : API.getIterable()) {} + } + } + """); + + Files.createDirectories(classes); + + var out = new JavacTask(tb) + .options("-XDrawDiagnostics", + "-Werror", + "-Xlint:rawtypes") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + var expectedOut = List.of(""); + + if (!Objects.equals(expectedOut, out)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + out); + + } + } + +} diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java index c61a92e80cd..fe4ca8c1652 100644 --- a/test/langtools/tools/javac/parser/DeclarationEndPositions.java +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -86,10 +86,12 @@ public class DeclarationEndPositions { // For variable declarations using "var", verify the "var" position if (tree instanceof JCVariableDecl varDecl && varDecl.declaredUsingVar()) { - int vpos = varDecl.typePos; - if (!input.substring(vpos).startsWith("var")) { + int varStart = varDecl.vartype.getStartPosition(); + int varEnd = varDecl.vartype.getEndPosition(); + + if (!input.substring(varStart, varEnd).startsWith("var")) { throw new AssertionError(String.format( - "wrong %s pos %d for \"%s\" in \"%s\"", "var", vpos, tree, input)); + "wrong %s start pos %d end pos %d for \"%s\" in \"%s\"", "var", varStart, varEnd, tree, input)); } } } diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index 25aeb19f1bb..0bce1ef017c 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -87,7 +87,9 @@ import javax.tools.ToolProvider; import com.sun.source.tree.CaseTree; import com.sun.source.tree.DefaultCaseLabelTree; +import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.ModuleTree; +import com.sun.source.tree.VarTypeTree; import com.sun.source.util.TreePathScanner; import com.sun.tools.javac.api.JavacTaskPool; import com.sun.tools.javac.api.JavacTaskPool.Worker; @@ -1070,8 +1072,8 @@ public class JavacParserTest extends TestCase { VariableTree stmt2 = (VariableTree) method.getBody().getStatements().get(1); Tree v1Type = stmt1.getType(); Tree v2Type = stmt2.getType(); - assertEquals("Implicit type for v1 is not correct: ", Kind.PRIMITIVE_TYPE, v1Type.getKind()); - assertEquals("Implicit type for v2 is not correct: ", Kind.PRIMITIVE_TYPE, v2Type.getKind()); + assertEquals("Implicit type for v1 is not correct: ", Kind.VAR_TYPE, v1Type.getKind()); + assertEquals("Implicit type for v2 is not correct: ", Kind.VAR_TYPE, v2Type.getKind()); } @Test @@ -3151,6 +3153,37 @@ public class JavacParserTest extends TestCase { codes); } + @Test + void testVarPositions() throws IOException { + String code = """ + public class Test { + void t() { + var v = + } + } + """; + DiagnosticCollector coll = + new DiagnosticCollector<>(); + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, + null, + null, Arrays.asList(new MyFileObject(code))); + Trees trees = Trees.instance(ct); + SourcePositions sp = trees.getSourcePositions(); + CompilationUnitTree cut = ct.parse().iterator().next(); + new TreeScanner() { + @Override + public Void visitVarType(VarTypeTree node, Void p) { + long start = sp.getStartPosition(cut, node); + long end = sp.getEndPosition(cut, node); + String text = code.substring((int) start, (int) end); + assertEquals("var start pos", + "var", + text); + return null; + } + }; + } + void run(String[] args) throws Exception { int passed = 0, failed = 0; final Pattern p = (args != null && args.length > 0) diff --git a/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java b/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java index e63cbeb2008..01d742eb9e3 100644 --- a/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java +++ b/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java @@ -83,7 +83,7 @@ public class BindingPatternVarTypeModel { new TreeScanner() { @Override public Void visitBindingPattern(BindingPatternTree node, Void p) { - if (node.getVariable().getType().getKind() != Tree.Kind.PRIMITIVE_TYPE) { + if (node.getVariable().getType().getKind() != Tree.Kind.VAR_TYPE) { throw new AssertionError("Unexpected type for var: " + node.getVariable().getType().getKind() + ":" + node.getVariable().getType()); diff --git a/test/langtools/tools/javac/patterns/InstanceOfModelTest.java b/test/langtools/tools/javac/patterns/InstanceOfModelTest.java index b756c9263bd..288abec9659 100644 --- a/test/langtools/tools/javac/patterns/InstanceOfModelTest.java +++ b/test/langtools/tools/javac/patterns/InstanceOfModelTest.java @@ -79,7 +79,7 @@ public class InstanceOfModelTest { List expectedInstanceOf = List.of( "null:R", "R r:R", - "R(int v):null" + "R(var v):null" ); if (!Objects.equals(expectedInstanceOf, instanceOf)) { diff --git a/test/langtools/tools/javac/tree/VarTree.java b/test/langtools/tools/javac/tree/VarTree.java index 6efd080bd4f..70caf66862e 100644 --- a/test/langtools/tools/javac/tree/VarTree.java +++ b/test/langtools/tools/javac/tree/VarTree.java @@ -52,27 +52,27 @@ public class VarTree { public static void main(String... args) throws Exception { VarTree test = new VarTree(); test.run("|var testVar = 0;| ", - "int testVar = 0"); + "var testVar = 0"); test.run("|var testVar = 0;| undef undef;", - "int testVar = 0"); + "var testVar = 0"); test.run("|final var testVar = 0;| ", - "final int testVar = 0"); + "final var testVar = 0"); test.run("for (|var testVar| : java.util.Arrays.asList(0, 1)) {}", - "java.lang.Integer testVar"); + "var testVar"); test.run("for (|final var testVar| : java.util.Arrays.asList(0, 1)) {}", - "final java.lang.Integer testVar"); + "final var testVar"); test.run("java.util.function.Consumer c = |testVar| -> {};", - "java.lang.String testVar"); + "/*missing*/ testVar"); //TODO: is the /*missing*/ here ideal? test.run("java.util.function.Consumer c = (|testVar|) -> {};", - "java.lang.String testVar"); + "/*missing*/ testVar"); //TODO: is the /*missing*/ here ideal? test.run("java.util.function.Consumer c = (|var testVar|) -> {};", - "java.lang.String testVar"); + "var testVar"); test.run("java.util.function.Consumer c = (|final var testVar|) -> {};", - "final java.lang.String testVar"); + "final var testVar"); test.run("record Rec(int x) { }; switch (null) { case Rec(|var testVar|) -> {} default -> {} };", - "int testVar"); + "var testVar"); test.run("record Rec(int x) { }; switch (null) { case Rec(|final var testVar|) -> {} default -> {} };", - "final int testVar"); + "final var testVar"); } void run(String code, String expected) throws IOException { @@ -136,11 +136,13 @@ public class VarTree { throw new AssertionError("Unexpected span: " + snip); } - int typeStart = (int) trees.getSourcePositions().getStartPosition(cut, node.getType()); - int typeEnd = (int) trees.getSourcePositions().getEndPosition(cut, node.getType()); + if (node.getType() != null) { + int typeStart = (int) trees.getSourcePositions().getStartPosition(cut, node.getType()); + int typeEnd = (int) trees.getSourcePositions().getEndPosition(cut, node.getType()); - if (typeStart != (-1) && typeEnd != (-1)) { - throw new AssertionError("Unexpected type position: " + typeStart + ", " + typeEnd); + if (typeStart + 3 != typeEnd) { + throw new AssertionError("Unexpected type position: " + typeStart + ", " + typeEnd); + } } found[0] = true; diff --git a/test/langtools/tools/javac/tree/VarWarnPosition.java b/test/langtools/tools/javac/tree/VarWarnPosition.java index f1d5f4346d0..5cdfa41d6ef 100644 --- a/test/langtools/tools/javac/tree/VarWarnPosition.java +++ b/test/langtools/tools/javac/tree/VarWarnPosition.java @@ -25,6 +25,9 @@ public class VarWarnPosition { // Test 4 Consumer c3 = (final var d) -> { }; + + // Test 5 + var d = deprecatedList.get(0); } } diff --git a/test/langtools/tools/javac/tree/VarWarnPosition.out b/test/langtools/tools/javac/tree/VarWarnPosition.out index 87a4ca3a1fd..0bd92acd035 100644 --- a/test/langtools/tools/javac/tree/VarWarnPosition.out +++ b/test/langtools/tools/javac/tree/VarWarnPosition.out @@ -1,8 +1,4 @@ -VarWarnPosition.java:18:14: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package VarWarnPosition.java:21:18: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package -VarWarnPosition.java:21:28: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package VarWarnPosition.java:24:18: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package -VarWarnPosition.java:24:30: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package VarWarnPosition.java:27:18: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package -VarWarnPosition.java:27:36: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package -7 warnings +3 warnings \ No newline at end of file From 3d8ffabe5dc6efda10ee7c76cca47e54b5383b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 26 Feb 2026 08:21:48 +0000 Subject: [PATCH 056/636] 8364393: Allow templates to have # character without variable replacement Reviewed-by: epeter, chagedorn --- .../lib/template_framework/Renderer.java | 58 ++++++++--- .../lib/template_framework/Template.java | 6 +- .../examples/TestTutorial.java | 4 +- .../tests/TestTemplate.java | 99 ++++++++++++++----- 4 files changed, 130 insertions(+), 37 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java index 61ab9ab343c..cb83a5ea40c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Renderer.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 @@ -413,8 +413,39 @@ final class Renderer { } } + /** + * Finds the index where the next replacement pattern after {@code start} begins while skipping + * over "$$" and "##". + * + * @param s string to search for replacements + * @param start index from which to start searching + * @return the index of the beginning of the next replacement pattern or the length of {@code s} + */ + private int findNextReplacement(final String s, final int start) { + int next = start; + for (int potentialStart = start; potentialStart < s.length() && s.charAt(next) == s.charAt(potentialStart); potentialStart = next + 1) { + // If this is not the first iteration, we have found a doubled up "$" or "#" and need to skip + // over the second instance. + if (potentialStart != start) { + potentialStart += 1; + } + // Find the next "$" or "#", after the potential start. + int dollar = s.indexOf("$", potentialStart); + int hashtag = s.indexOf("#", potentialStart); + // If the character was not found, we want to have the rest of the + // String s, so instead of "-1" take the end/length of the String. + dollar = (dollar == -1) ? s.length() : dollar; + hashtag = (hashtag == -1) ? s.length() : hashtag; + // Take the first one. + next = Math.min(dollar, hashtag); + } + + return next; + } + /** * We split a {@link String} by "#" and "$", and then look at each part. + * However, we escape "##" to "#" and "$$" to "$". * Example: * * s: "abcdefghijklmnop #name abcdefgh${var_name} 12345#{name2}_con $field_name something" @@ -428,16 +459,19 @@ final class Renderer { int start = 0; boolean startIsAfterDollar = false; do { - // Find the next "$" or "#", after start. - int dollar = s.indexOf("$", start); - int hashtag = s.indexOf("#", start); - // If the character was not found, we want to have the rest of the - // String s, so instead of "-1" take the end/length of the String. - dollar = (dollar == -1) ? s.length() : dollar; - hashtag = (hashtag == -1) ? s.length() : hashtag; - // Take the first one. - int next = Math.min(dollar, hashtag); + int next = findNextReplacement(s, start); + + // Detect most zero sized replacement patterns, i.e. "$#" or "#$", for better error reporting. + if (next < s.length() - 2 && ((s.charAt(next) == '$' && s.charAt(next + 1) == '#') || + (s.charAt(next) == '#' && s.charAt(next + 1) == '$'))) { + String pattern = s.substring(next, next + 2); + throw new RendererException("Found zero sized replacement pattern '" + pattern + "'."); + } + String part = s.substring(start, next); + // Escape doubled up replacement characters. + part = part.replace("##", "#"); + part = part.replace("$$", "$"); if (count == 0) { // First part has no "#" or "$" before it. @@ -452,8 +486,8 @@ final class Renderer { // terminate now. return; } - start = next + 1; // skip over the "#" or "$" - startIsAfterDollar = next == dollar; // remember which character we just split with + start = next + 1; + startIsAfterDollar = s.charAt(next) == '$'; // remember which character we just split with count++; } while (true); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java index f245cda0501..3cd3a9097ff 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java @@ -160,7 +160,8 @@ import compiler.lib.ir_framework.TestFramework; * arguments into the strings. But since string templates are not (yet) available, the Templates provide * hashtag replacements in the {@link String}s: the Template argument names are captured, and * the argument values automatically replace any {@code "#name"} in the {@link String}s. See the different overloads - * of {@link #make} for examples. Additional hashtag replacements can be defined with {@link #let}. + * of {@link #make} for examples. Additional hashtag replacements can be defined with {@link #let}. If a "#" is needed + * in the code, hashtag replacmement can be escaped by writing two hashtags, i.e. "##" will render as "#". * We have decided to keep hashtag replacements constrained to the scope of one Template. They * do not escape to outer or inner Template uses. If one needs to pass values to inner Templates, * this can be done with Template arguments. Keeping hashtag replacements local to Templates @@ -172,7 +173,8 @@ import compiler.lib.ir_framework.TestFramework; * For this, Templates provide dollar replacements, which automatically rename any * {@code "$name"} in the {@link String} with a {@code "name_ID"}, where the {@code "ID"} is unique for every use of * a Template. The dollar replacement can also be captured with {@link #$}, and passed to nested - * Templates, which allows sharing of these identifier names between Templates. + * Templates, which allows sharing of these identifier names between Templates. Similar to hashtag replacements, + * dollars can be escaped by doubling up, i.e. "$$" renders as "$". * *

* The dollar and hashtag names must have at least one character. The first character must be a letter diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index ed542180bad..7de32d1bc10 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.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 @@ -226,6 +226,8 @@ public class TestTutorial { // we automatically rename the names that have a $ prepended with // var_1, var_2, etc. """ + // You can escape a hashtag by doubling it up. e.g. ## will render as a + // single hashtag. The same goes for $$. int $var = #con; System.out.println("T1: #x, #con, " + $var); """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index 9be74d232a7..f56a9d5b231 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8344942 + * @bug 8344942 8364393 * @summary Test some basic Template instantiations. We do not necessarily generate correct * java code, we just test that the code generation deterministically creates the * expected String. @@ -138,6 +138,7 @@ public class TestTemplate { testHookWithNestedTemplates(); testHookRecursion(); testDollar(); + testEscaping(); testLet1(); testLet2(); testDollarAndHashtagBrackets(); @@ -182,7 +183,6 @@ public class TestTemplate { expectRendererException(() -> testFailingDollarName5(), "Is not a valid '$' replacement pattern: '$' in '$'."); expectRendererException(() -> testFailingDollarName6(), "Is not a valid '$' replacement pattern: '$' in 'asdf$'."); expectRendererException(() -> testFailingDollarName7(), "Is not a valid '$' replacement pattern: '$1' in 'asdf$1'."); - expectRendererException(() -> testFailingDollarName8(), "Is not a valid '$' replacement pattern: '$' in 'abc$$abc'."); expectRendererException(() -> testFailingLetName1(), "A hashtag replacement should not be null."); expectRendererException(() -> testFailingHashtagName1(), "Is not a valid hashtag replacement name: ''."); expectRendererException(() -> testFailingHashtagName2(), "Is not a valid hashtag replacement name: 'abc#abc'."); @@ -191,11 +191,12 @@ public class TestTemplate { expectRendererException(() -> testFailingHashtagName5(), "Is not a valid '#' replacement pattern: '#' in '#'."); expectRendererException(() -> testFailingHashtagName6(), "Is not a valid '#' replacement pattern: '#' in 'asdf#'."); expectRendererException(() -> testFailingHashtagName7(), "Is not a valid '#' replacement pattern: '#1' in 'asdf#1'."); - expectRendererException(() -> testFailingHashtagName8(), "Is not a valid '#' replacement pattern: '#' in 'abc##abc'."); expectRendererException(() -> testFailingDollarHashtagName1(), "Is not a valid '#' replacement pattern: '#' in '#$'."); expectRendererException(() -> testFailingDollarHashtagName2(), "Is not a valid '$' replacement pattern: '$' in '$#'."); - expectRendererException(() -> testFailingDollarHashtagName3(), "Is not a valid '#' replacement pattern: '#' in '#$name'."); - expectRendererException(() -> testFailingDollarHashtagName4(), "Is not a valid '$' replacement pattern: '$' in '$#name'."); + expectRendererException(() -> testFailingDollarHashtagName3(), "Found zero sized replacement pattern '#$'."); + expectRendererException(() -> testFailingDollarHashtagName4(), "Found zero sized replacement pattern '$#'."); + expectRendererException(() -> testFailingDollarHashtagName5(), "Found zero sized replacement pattern '#$'."); + expectRendererException(() -> testFailingDollarHashtagName6(), "Found zero sized replacement pattern '$#'."); expectRendererException(() -> testFailingHook(), "Hook 'Hook1' was referenced but not found!"); expectRendererException(() -> testFailingSample1a(), "No Name found for DataName.FilterdSet(MUTABLE, subtypeOf(int), supertypeOf(int))"); expectRendererException(() -> testFailingSample1b(), "No Name found for StructuralName.FilteredSet( subtypeOf(StructuralA) supertypeOf(StructuralA))"); @@ -822,6 +823,60 @@ public class TestTemplate { checkEQ(code, expected); } + public static void testEscaping() { + var template1 = Template.make(() -> scope( + let("one", 1), + let("two", 2), + let("three", 3), + """ + abc##def + abc$$def + abc####def + abc$$$$def + ##abc + $$abc + abc## + abc$$ + ## + $$ + ###### + $$$$$$ + abc###one + abc$$$dollar + #one ###two #####three + ###{one}## + $dollar $$$dollar $$$$$dollar + $$${dollar}$$ + ##$dollar $$#one $$##$$## + """ + )); + + String code = template1.render(); + String expected = + """ + abc#def + abc$def + abc##def + abc$$def + #abc + $abc + abc# + abc$ + # + $ + ### + $$$ + abc#1 + abc$dollar_1 + 1 #2 ##3 + #1# + dollar_1 $dollar_1 $$dollar_1 + $dollar_1$ + #dollar_1 $1 $#$# + """; + checkEQ(code, expected); + } + public static void testLet1() { var hook1 = new Hook("Hook1"); @@ -3382,13 +3437,6 @@ public class TestTemplate { String code = template1.render(); } - public static void testFailingDollarName8() { - var template1 = Template.make(() -> scope( - "abc$$abc" // empty dollar name - )); - String code = template1.render(); - } - public static void testFailingLetName1() { var template1 = Template.make(() -> scope( let(null, $("abc")) // Null input for hashtag name @@ -3447,13 +3495,6 @@ public class TestTemplate { String code = template1.render(); } - public static void testFailingHashtagName8() { - var template1 = Template.make(() -> scope( - "abc##abc" // empty hashtag name - )); - String code = template1.render(); - } - public static void testFailingDollarHashtagName1() { var template1 = Template.make(() -> scope( "#$" // empty hashtag name @@ -3470,14 +3511,28 @@ public class TestTemplate { public static void testFailingDollarHashtagName3() { var template1 = Template.make(() -> scope( - "#$name" // empty hashtag name + "#$name" // Zero sized replacement )); String code = template1.render(); } public static void testFailingDollarHashtagName4() { var template1 = Template.make(() -> scope( - "$#name" // empty dollar name + "$#name" // Zero sized replacement + )); + String code = template1.render(); + } + + public static void testFailingDollarHashtagName5() { + var template1 = Template.make(() -> scope( + "asdf#$abc" // Zero sized replacement + )); + String code = template1.render(); + } + + public static void testFailingDollarHashtagName6() { + var template1 = Template.make(() -> scope( + "asdf$#abc" // Zero sized replacement )); String code = template1.render(); } From 4a08996147222d0d8f77655798ac4c3bb5471633 Mon Sep 17 00:00:00 2001 From: Andrew Haley Date: Thu, 26 Feb 2026 11:02:59 +0000 Subject: [PATCH 057/636] 8378107: Data cache zeroing is used even when it is prohibited Reviewed-by: shade, adinn --- src/hotspot/cpu/aarch64/vm_version_aarch64.cpp | 4 +++- src/hotspot/cpu/aarch64/vm_version_aarch64.hpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index b678921dd97..9b85733ed08 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -446,7 +446,9 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * VM_Version::zva_length()); } } else if (UseBlockZeroing) { - warning("DC ZVA is not available on this CPU"); + if (!FLAG_IS_DEFAULT(UseBlockZeroing)) { + warning("DC ZVA is not available on this CPU"); + } FLAG_SET_DEFAULT(UseBlockZeroing, false); } diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 07f6c09e18f..0213872852b 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -207,7 +207,7 @@ public: return false; } - static bool is_zva_enabled() { return 0 <= _zva_length; } + static bool is_zva_enabled() { return 0 < _zva_length; } static int zva_length() { assert(is_zva_enabled(), "ZVA not available"); return _zva_length; From 00064ee77365129074be73d519ebd3570cc38d3a Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 26 Feb 2026 11:22:43 +0000 Subject: [PATCH 058/636] 8378239: C2: Incorrect check in StoreNode::Identity Reviewed-by: epeter, rcastanedalo --- src/hotspot/share/opto/memnode.cpp | 7 +++++-- .../vectorapi/TestVectorLoadStoreOptimization.java | 12 ++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 5af0794f29c..f92d6fcfb6c 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -3567,8 +3567,11 @@ Node* StoreNode::Identity(PhaseGVN* phase) { val->in(MemNode::Address)->eqv_uncast(adr) && val->in(MemNode::Memory )->eqv_uncast(mem) && val->as_Load()->store_Opcode() == Opcode()) { - // Ensure vector type is the same - if (!is_StoreVector() || (mem->is_LoadVector() && as_StoreVector()->vect_type() == mem->as_LoadVector()->vect_type())) { + if (!is_StoreVector()) { + result = mem; + } else if (Opcode() == Op_StoreVector && val->Opcode() == Op_LoadVector && + as_StoreVector()->vect_type() == val->as_LoadVector()->vect_type()) { + // Ensure both are not masked accesses or gathers/scatters and vector types are the same result = mem; } } diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLoadStoreOptimization.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLoadStoreOptimization.java index c603f450d0c..71507bde7c0 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLoadStoreOptimization.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLoadStoreOptimization.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2025, 2026, NVIDIA CORPORATION & 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 @@ -29,7 +29,7 @@ import jdk.incubator.vector.*; import jdk.test.lib.Asserts; /** - * @test 8371603 + * @test 8371603 8378239 * @key randomness * @library /test/lib / * @summary Test the missing optimization issues for vector load/store caused by JDK-8286941 @@ -96,6 +96,14 @@ public class TestVectorLoadStoreOptimization { } } + // Test that store a value that is just loaded from the same memory location is elided + @Test + @IR(failOn = IRNode.STORE_VECTOR, + applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"}) + public static void testStoreVector2() { + IntVector.fromArray(SPECIES, a, 0).intoArray(a, 0); + } + public static void main(String[] args) { TestFramework testFramework = new TestFramework(); testFramework.setDefaultWarmup(10000) From 7065a24be669b8a6cb319124ac5b3b1667420463 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 26 Feb 2026 11:28:39 +0000 Subject: [PATCH 059/636] 8378240: C2: MemNode::can_see_stored_value assumes this can never be a StoreVector Reviewed-by: chagedorn, epeter --- src/hotspot/share/opto/memnode.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index f92d6fcfb6c..21ed15f9ec7 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1228,8 +1228,13 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { } // LoadVector/StoreVector needs additional check to ensure the types match. if (st->is_StoreVector()) { - const TypeVect* in_vt = st->as_StoreVector()->vect_type(); - const TypeVect* out_vt = as_LoadVector()->vect_type(); + if ((Opcode() != Op_LoadVector && Opcode() != Op_StoreVector) || st->Opcode() != Op_StoreVector) { + // Some kind of masked access or gather/scatter + return nullptr; + } + + const TypeVect* in_vt = st->as_StoreVector()->vect_type(); + const TypeVect* out_vt = is_Load() ? as_LoadVector()->vect_type() : as_StoreVector()->vect_type(); if (in_vt != out_vt) { return nullptr; } From 1674006047ba1e96b7b5a8baa899b7cf03e9c9b1 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Thu, 26 Feb 2026 11:47:10 +0000 Subject: [PATCH 060/636] 8378631: Update Zlib Data Compression Library to Version 1.3.2 Reviewed-by: alanb, erikj, lancea --- make/autoconf/lib-bundled.m4 | 2 +- src/java.base/share/legal/zlib.md | 4 +- .../share/native/libzip/zlib/ChangeLog | 51 +++ src/java.base/share/native/libzip/zlib/README | 28 +- .../share/native/libzip/zlib/compress.c | 46 ++- .../share/native/libzip/zlib/deflate.c | 176 ++++++---- .../share/native/libzip/zlib/deflate.h | 8 +- .../share/native/libzip/zlib/gzguts.h | 64 ++-- .../share/native/libzip/zlib/gzlib.c | 103 +++--- .../share/native/libzip/zlib/gzread.c | 314 +++++++++++------- .../share/native/libzip/zlib/gzwrite.c | 267 +++++++++------ .../share/native/libzip/zlib/infback.c | 87 ++--- .../share/native/libzip/zlib/inffast.c | 13 +- .../share/native/libzip/zlib/inffixed.h | 182 +++++----- .../share/native/libzip/zlib/inflate.c | 189 +++-------- .../share/native/libzip/zlib/inflate.h | 2 +- .../share/native/libzip/zlib/inftrees.c | 143 +++++++- .../share/native/libzip/zlib/inftrees.h | 4 +- .../native/libzip/zlib/patches/ChangeLog_java | 2 +- .../share/native/libzip/zlib/trees.c | 28 +- .../share/native/libzip/zlib/uncompr.c | 62 ++-- .../share/native/libzip/zlib/zconf.h | 46 +-- .../share/native/libzip/zlib/zcrc32.c | 166 +++------ src/java.base/share/native/libzip/zlib/zlib.h | 307 +++++++++++------ .../share/native/libzip/zlib/zutil.c | 84 +++-- .../share/native/libzip/zlib/zutil.h | 99 +++++- 26 files changed, 1452 insertions(+), 1025 deletions(-) diff --git a/make/autoconf/lib-bundled.m4 b/make/autoconf/lib-bundled.m4 index a6266bec014..bc358928af0 100644 --- a/make/autoconf/lib-bundled.m4 +++ b/make/autoconf/lib-bundled.m4 @@ -268,7 +268,7 @@ AC_DEFUN_ONCE([LIB_SETUP_ZLIB], if test "x$USE_EXTERNAL_LIBZ" = "xfalse"; then LIBZ_CFLAGS="$LIBZ_CFLAGS -I$TOPDIR/src/java.base/share/native/libzip/zlib" if test "x$OPENJDK_TARGET_OS" = xmacosx; then - LIBZ_CFLAGS="$LIBZ_CFLAGS -DHAVE_UNISTD_H" + LIBZ_CFLAGS="$LIBZ_CFLAGS -DHAVE_UNISTD_H=1 -DHAVE_STDARG_H=1" fi else LIBZ_LIBS="-lz" diff --git a/src/java.base/share/legal/zlib.md b/src/java.base/share/legal/zlib.md index fcc5457bf5b..c729c4248d4 100644 --- a/src/java.base/share/legal/zlib.md +++ b/src/java.base/share/legal/zlib.md @@ -1,9 +1,9 @@ -## zlib v1.3.1 +## zlib v1.3.2 ### zlib License

 
-Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
+Copyright (C) 1995-2026 Jean-loup Gailly and Mark Adler
 
 This software is provided 'as-is', without any express or implied
 warranty.  In no event will the authors be held liable for any damages
diff --git a/src/java.base/share/native/libzip/zlib/ChangeLog b/src/java.base/share/native/libzip/zlib/ChangeLog
index b801a1031ec..312753edade 100644
--- a/src/java.base/share/native/libzip/zlib/ChangeLog
+++ b/src/java.base/share/native/libzip/zlib/ChangeLog
@@ -1,6 +1,57 @@
 
                 ChangeLog file for zlib
 
+Changes in 1.3.2 (17 Feb 2026)
+- Continued rewrite of CMake build [Vollstrecker]
+- Various portability improvements
+- Various github workflow additions and improvements
+- Check for negative lengths in crc32_combine functions
+- Copy only the initialized window contents in inflateCopy
+- Prevent the use of insecure functions without an explicit request
+- Add compressBound_z and deflateBound_z functions for large values
+- Use atomics to build inflate fixed tables once
+- Add definition of ZLIB_INSECURE to build tests with c89 and c94
+- Add --undefined option to ./configure for UBSan checker
+- Copy only the initialized deflate state in deflateCopy
+- Zero inflate state on allocation
+- Remove untgz from contrib
+- Add _z versions of the compress and uncompress functions
+- Vectorize the CRC-32 calculation on the s390x
+- Set bit 11 of the zip header flags in minizip if UTF-8
+- Update OS/400 support
+- Add a test to configure to check for a working compiler
+- Check for invalid NULL pointer inputs to zlib operations
+- Add --mandir to ./configure to specify manual directory
+- Add LICENSE.Info-Zip to contrib/minizip
+- Remove vstudio projects in lieu of cmake-generated projects
+- Replace strcpy() with memcpy() in contrib/minizip
+
+Changes in 1.3.1.2 (8 Dec 2025)
+- Improve portability to RISC OS
+- Permit compiling contrib/minizip/unzip.c with decryption
+- Enable build of shared library on AIX
+- Make deflateBound() more conservative and handle Z_STREAM_END
+- Add zipAlreadyThere() to minizip zip.c to help avoid duplicates
+- Make z_off_t 64 bits by default
+- Add deflateUsed() function to get the used bits in the last byte
+- Avoid out-of-bounds pointer arithmetic in inflateCopy()
+- Add Haiku to configure for proper LDSHARED settings
+- Add Bazel targets
+- Complete rewrite of CMake build [Vollstrecker]
+- Clarify the use of errnum in gzerror()
+- Note that gzseek() requests are deferred until the next operation
+- Note the use of gzungetc() to run a deferred seek while reading
+- Fix bug in inflatePrime() for 16-bit ints
+- Add a "G" option to force gzip, disabling transparency in gzread()
+- Improve the discrimination between trailing garbage and bad gzip
+- Allow gzflush() to write empty gzip members
+- Remove redundant frees of point list on error in examples/zran.c
+- Clarify the use of inflateGetHeader()
+- Update links to the RFCs
+- Return all available uncompressed data on error in gzread.c
+- Support non-blocking devices in the gz* routines
+- Various other small improvements
+
 Changes in 1.3.1 (22 Jan 2024)
 - Reject overflows of zip header fields in minizip
 - Fix bug in inflateSync() for data held in bit buffer
diff --git a/src/java.base/share/native/libzip/zlib/README b/src/java.base/share/native/libzip/zlib/README
index c5f917540b6..2b1e6f36fe3 100644
--- a/src/java.base/share/native/libzip/zlib/README
+++ b/src/java.base/share/native/libzip/zlib/README
@@ -1,10 +1,10 @@
 ZLIB DATA COMPRESSION LIBRARY
 
-zlib 1.3.1 is a general purpose data compression library.  All the code is
-thread safe.  The data format used by the zlib library is described by RFCs
-(Request for Comments) 1950 to 1952 in the files
-http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and
-rfc1952 (gzip format).
+zlib 1.3.2 is a general purpose data compression library.  All the code is
+thread safe (though see the FAQ for caveats).  The data format used by the zlib
+library is described by RFCs (Request for Comments) 1950 to 1952 at
+https://datatracker.ietf.org/doc/html/rfc1950 (zlib format), rfc1951 (deflate
+format) and rfc1952 (gzip format).
 
 All functions of the compression library are documented in the file zlib.h
 (volunteer to write man pages welcome, contact zlib@gzip.org).  A usage example
@@ -21,17 +21,17 @@ make_vms.com.
 
 Questions about zlib should be sent to , or to Gilles Vollant
  for the Windows DLL version.  The zlib home page is
-http://zlib.net/ .  Before reporting a problem, please check this site to
+https://zlib.net/ .  Before reporting a problem, please check this site to
 verify that you have the latest version of zlib; otherwise get the latest
 version and check whether the problem still exists or not.
 
-PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help.
+PLEASE read the zlib FAQ https://zlib.net/zlib_faq.html before asking for help.
 
 Mark Nelson  wrote an article about zlib for the Jan.  1997
 issue of Dr.  Dobb's Journal; a copy of the article is available at
-https://marknelson.us/posts/1997/01/01/zlib-engine.html .
+https://zlib.net/nelson/ .
 
-The changes made in version 1.3.1 are documented in the file ChangeLog.
+The changes made in version 1.3.2 are documented in the file ChangeLog.
 
 Unsupported third party contributions are provided in directory contrib/ .
 
@@ -43,9 +43,9 @@ can be found at https://github.com/pmqs/IO-Compress .
 
 A Python interface to zlib written by A.M. Kuchling  is
 available in Python 1.5 and later versions, see
-http://docs.python.org/library/zlib.html .
+https://docs.python.org/3/library/zlib.html .
 
-zlib is built into tcl: http://wiki.tcl.tk/4610 .
+zlib is built into tcl: https://wiki.tcl-lang.org/page/zlib .
 
 An experimental package to read and write files in .zip format, written on top
 of zlib by Gilles Vollant , is available in the
@@ -69,9 +69,7 @@ Notes for some targets:
 - zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with
   other compilers. Use "make test" to check your compiler.
 
-- gzdopen is not supported on RISCOS or BEOS.
-
-- For PalmOs, see http://palmzlib.sourceforge.net/
+- For PalmOs, see https://palmzlib.sourceforge.net/
 
 
 Acknowledgments:
@@ -83,7 +81,7 @@ Acknowledgments:
 
 Copyright notice:
 
- (C) 1995-2024 Jean-loup Gailly and Mark Adler
+ (C) 1995-2026 Jean-loup Gailly and Mark Adler
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
diff --git a/src/java.base/share/native/libzip/zlib/compress.c b/src/java.base/share/native/libzip/zlib/compress.c
index d7421379673..54346ad2a2f 100644
--- a/src/java.base/share/native/libzip/zlib/compress.c
+++ b/src/java.base/share/native/libzip/zlib/compress.c
@@ -23,7 +23,7 @@
  */
 
 /* compress.c -- compress a memory buffer
- * Copyright (C) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler
+ * Copyright (C) 1995-2026 Jean-loup Gailly, Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -42,13 +42,19 @@
      compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
    memory, Z_BUF_ERROR if there was not enough room in the output buffer,
    Z_STREAM_ERROR if the level parameter is invalid.
+
+     The _z versions of the functions take size_t length arguments.
 */
-int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source,
-                      uLong sourceLen, int level) {
+int ZEXPORT compress2_z(Bytef *dest, z_size_t *destLen, const Bytef *source,
+                        z_size_t sourceLen, int level) {
     z_stream stream;
     int err;
     const uInt max = (uInt)-1;
-    uLong left;
+    z_size_t left;
+
+    if ((sourceLen > 0 && source == NULL) ||
+        destLen == NULL || (*destLen > 0 && dest == NULL))
+        return Z_STREAM_ERROR;
 
     left = *destLen;
     *destLen = 0;
@@ -67,23 +73,36 @@ int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source,
 
     do {
         if (stream.avail_out == 0) {
-            stream.avail_out = left > (uLong)max ? max : (uInt)left;
+            stream.avail_out = left > (z_size_t)max ? max : (uInt)left;
             left -= stream.avail_out;
         }
         if (stream.avail_in == 0) {
-            stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen;
+            stream.avail_in = sourceLen > (z_size_t)max ? max :
+                                                          (uInt)sourceLen;
             sourceLen -= stream.avail_in;
         }
         err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH);
     } while (err == Z_OK);
 
-    *destLen = stream.total_out;
+    *destLen = (z_size_t)(stream.next_out - dest);
     deflateEnd(&stream);
     return err == Z_STREAM_END ? Z_OK : err;
 }
-
+int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source,
+                      uLong sourceLen, int level) {
+    int ret;
+    z_size_t got = *destLen;
+    ret = compress2_z(dest, &got, source, sourceLen, level);
+    *destLen = (uLong)got;
+    return ret;
+}
 /* ===========================================================================
  */
+int ZEXPORT compress_z(Bytef *dest, z_size_t *destLen, const Bytef *source,
+                       z_size_t sourceLen) {
+    return compress2_z(dest, destLen, source, sourceLen,
+                       Z_DEFAULT_COMPRESSION);
+}
 int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source,
                      uLong sourceLen) {
     return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
@@ -93,7 +112,12 @@ int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source,
      If the default memLevel or windowBits for deflateInit() is changed, then
    this function needs to be updated.
  */
-uLong ZEXPORT compressBound(uLong sourceLen) {
-    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
-           (sourceLen >> 25) + 13;
+z_size_t ZEXPORT compressBound_z(z_size_t sourceLen) {
+    z_size_t bound = sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+                     (sourceLen >> 25) + 13;
+    return bound < sourceLen ? (z_size_t)-1 : bound;
+}
+uLong ZEXPORT compressBound(uLong sourceLen) {
+    z_size_t bound = compressBound_z(sourceLen);
+    return (uLong)bound != bound ? (uLong)-1 : (uLong)bound;
 }
diff --git a/src/java.base/share/native/libzip/zlib/deflate.c b/src/java.base/share/native/libzip/zlib/deflate.c
index 57fc6802bb8..0ec56ec5691 100644
--- a/src/java.base/share/native/libzip/zlib/deflate.c
+++ b/src/java.base/share/native/libzip/zlib/deflate.c
@@ -23,7 +23,7 @@
  */
 
 /* deflate.c -- compress data using the deflation algorithm
- * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
+ * Copyright (C) 1995-2026 Jean-loup Gailly and Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -61,7 +61,7 @@
  *  REFERENCES
  *
  *      Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
- *      Available in http://tools.ietf.org/html/rfc1951
+ *      Available at https://datatracker.ietf.org/doc/html/rfc1951
  *
  *      A description of the Rabin and Karp algorithm is given in the book
  *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
@@ -76,7 +76,7 @@
 #include "deflate.h"
 
 const char deflate_copyright[] =
-   " deflate 1.3.1 Copyright 1995-2024 Jean-loup Gailly and Mark Adler ";
+   " deflate 1.3.2 Copyright 1995-2026 Jean-loup Gailly and Mark Adler ";
 /*
   If you use the zlib library in a product, an acknowledgment is welcome
   in the documentation of your product. If for some reason you cannot
@@ -194,8 +194,8 @@ local const config configuration_table[10] = {
 #define CLEAR_HASH(s) \
     do { \
         s->head[s->hash_size - 1] = NIL; \
-        zmemzero((Bytef *)s->head, \
-                 (unsigned)(s->hash_size - 1)*sizeof(*s->head)); \
+        zmemzero(s->head, (unsigned)(s->hash_size - 1)*sizeof(*s->head)); \
+        s->slid = 0; \
     } while (0)
 
 /* ===========================================================================
@@ -219,8 +219,8 @@ local void slide_hash(deflate_state *s) {
         m = *--p;
         *p = (Pos)(m >= wsize ? m - wsize : NIL);
     } while (--n);
-    n = wsize;
 #ifndef FASTEST
+    n = wsize;
     p = &s->prev[n];
     do {
         m = *--p;
@@ -230,6 +230,7 @@ local void slide_hash(deflate_state *s) {
          */
     } while (--n);
 #endif
+    s->slid = 1;
 }
 
 /* ===========================================================================
@@ -283,7 +284,14 @@ local void fill_window(deflate_state *s) {
         more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
 
         /* Deal with !@#$% 64K limit: */
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4127)
+#endif
         if (sizeof(int) <= 2) {
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
             if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
                 more = wsize;
 
@@ -455,6 +463,7 @@ int ZEXPORT deflateInit2_(z_streamp strm, int level, int method,
     if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */
     s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
     if (s == Z_NULL) return Z_MEM_ERROR;
+    zmemzero(s, sizeof(deflate_state));
     strm->state = (struct internal_state FAR *)s;
     s->strm = strm;
     s->status = INIT_STATE;     /* to pass state test in deflateReset() */
@@ -736,10 +745,23 @@ int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) {
 /* ========================================================================= */
 int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) {
     if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
-    if (pending != Z_NULL)
-        *pending = strm->state->pending;
     if (bits != Z_NULL)
         *bits = strm->state->bi_valid;
+    if (pending != Z_NULL) {
+        *pending = (unsigned)strm->state->pending;
+        if (*pending != strm->state->pending) {
+            *pending = (unsigned)-1;
+            return Z_BUF_ERROR;
+        }
+    }
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateUsed(z_streamp strm, int *bits) {
+    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
+    if (bits != Z_NULL)
+        *bits = strm->state->bi_used;
     return Z_OK;
 }
 
@@ -855,28 +877,34 @@ int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy,
  *
  * Shifts are used to approximate divisions, for speed.
  */
-uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) {
+z_size_t ZEXPORT deflateBound_z(z_streamp strm, z_size_t sourceLen) {
     deflate_state *s;
-    uLong fixedlen, storelen, wraplen;
+    z_size_t fixedlen, storelen, wraplen, bound;
 
     /* upper bound for fixed blocks with 9-bit literals and length 255
        (memLevel == 2, which is the lowest that may not use stored blocks) --
        ~13% overhead plus a small constant */
     fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) +
                (sourceLen >> 9) + 4;
+    if (fixedlen < sourceLen)
+        fixedlen = (z_size_t)-1;
 
     /* upper bound for stored blocks with length 127 (memLevel == 1) --
        ~4% overhead plus a small constant */
     storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) +
                (sourceLen >> 11) + 7;
+    if (storelen < sourceLen)
+        storelen = (z_size_t)-1;
 
-    /* if can't get parameters, return larger bound plus a zlib wrapper */
-    if (deflateStateCheck(strm))
-        return (fixedlen > storelen ? fixedlen : storelen) + 6;
+    /* if can't get parameters, return larger bound plus a wrapper */
+    if (deflateStateCheck(strm)) {
+        bound = fixedlen > storelen ? fixedlen : storelen;
+        return bound + 18 < bound ? (z_size_t)-1 : bound + 18;
+    }
 
     /* compute wrapper length */
     s = strm->state;
-    switch (s->wrap) {
+    switch (s->wrap < 0 ? -s->wrap : s->wrap) {
     case 0:                                 /* raw deflate */
         wraplen = 0;
         break;
@@ -906,18 +934,25 @@ uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) {
         break;
 #endif
     default:                                /* for compiler happiness */
-        wraplen = 6;
+        wraplen = 18;
     }
 
     /* if not default parameters, return one of the conservative bounds */
-    if (s->w_bits != 15 || s->hash_bits != 8 + 7)
-        return (s->w_bits <= s->hash_bits && s->level ? fixedlen : storelen) +
-               wraplen;
+    if (s->w_bits != 15 || s->hash_bits != 8 + 7) {
+        bound = s->w_bits <= s->hash_bits && s->level ? fixedlen :
+                                                        storelen;
+        return bound + wraplen < bound ? (z_size_t)-1 : bound + wraplen;
+    }
 
     /* default settings: return tight bound for that case -- ~0.03% overhead
        plus a small constant */
-    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
-           (sourceLen >> 25) + 13 - 6 + wraplen;
+    bound = sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+            (sourceLen >> 25) + 13 - 6 + wraplen;
+    return bound < sourceLen ? (z_size_t)-1 : bound;
+}
+uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) {
+    z_size_t bound = deflateBound_z(strm, sourceLen);
+    return (uLong)bound != bound ? (uLong)-1 : (uLong)bound;
 }
 
 /* =========================================================================
@@ -941,8 +976,8 @@ local void flush_pending(z_streamp strm) {
     deflate_state *s = strm->state;
 
     _tr_flush_bits(s);
-    len = s->pending;
-    if (len > strm->avail_out) len = strm->avail_out;
+    len = s->pending > strm->avail_out ? strm->avail_out :
+                                         (unsigned)s->pending;
     if (len == 0) return;
 
     zmemcpy(strm->next_out, s->pending_out, len);
@@ -962,8 +997,8 @@ local void flush_pending(z_streamp strm) {
 #define HCRC_UPDATE(beg) \
     do { \
         if (s->gzhead->hcrc && s->pending > (beg)) \
-            strm->adler = crc32(strm->adler, s->pending_buf + (beg), \
-                                s->pending - (beg)); \
+            strm->adler = crc32_z(strm->adler, s->pending_buf + (beg), \
+                                  s->pending - (beg)); \
     } while (0)
 
 /* ========================================================================= */
@@ -1097,8 +1132,8 @@ int ZEXPORT deflate(z_streamp strm, int flush) {
                 put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
             }
             if (s->gzhead->hcrc)
-                strm->adler = crc32(strm->adler, s->pending_buf,
-                                    s->pending);
+                strm->adler = crc32_z(strm->adler, s->pending_buf,
+                                      s->pending);
             s->gzindex = 0;
             s->status = EXTRA_STATE;
         }
@@ -1106,9 +1141,9 @@ int ZEXPORT deflate(z_streamp strm, int flush) {
     if (s->status == EXTRA_STATE) {
         if (s->gzhead->extra != Z_NULL) {
             ulg beg = s->pending;   /* start of bytes to update crc */
-            uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex;
+            ulg left = (s->gzhead->extra_len & 0xffff) - s->gzindex;
             while (s->pending + left > s->pending_buf_size) {
-                uInt copy = s->pending_buf_size - s->pending;
+                ulg copy = s->pending_buf_size - s->pending;
                 zmemcpy(s->pending_buf + s->pending,
                         s->gzhead->extra + s->gzindex, copy);
                 s->pending = s->pending_buf_size;
@@ -1319,12 +1354,13 @@ int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) {
 
     ss = source->state;
 
-    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
+    zmemcpy(dest, source, sizeof(z_stream));
 
     ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
     if (ds == Z_NULL) return Z_MEM_ERROR;
+    zmemzero(ds, sizeof(deflate_state));
     dest->state = (struct internal_state FAR *) ds;
-    zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
+    zmemcpy(ds, ss, sizeof(deflate_state));
     ds->strm = dest;
 
     ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
@@ -1337,18 +1373,23 @@ int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) {
         deflateEnd (dest);
         return Z_MEM_ERROR;
     }
-    /* following zmemcpy do not work for 16-bit MSDOS */
-    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
-    zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
-    zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
-    zmemcpy(ds->pending_buf, ss->pending_buf, ds->lit_bufsize * LIT_BUFS);
+    /* following zmemcpy's do not work for 16-bit MSDOS */
+    zmemcpy(ds->window, ss->window, ss->high_water);
+    zmemcpy(ds->prev, ss->prev,
+            (ss->slid || ss->strstart - ss->insert > ds->w_size ? ds->w_size :
+                ss->strstart - ss->insert) * sizeof(Pos));
+    zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
 
     ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+    zmemcpy(ds->pending_out, ss->pending_out, ss->pending);
 #ifdef LIT_MEM
     ds->d_buf = (ushf *)(ds->pending_buf + (ds->lit_bufsize << 1));
     ds->l_buf = ds->pending_buf + (ds->lit_bufsize << 2);
+    zmemcpy(ds->d_buf, ss->d_buf, ss->sym_next * sizeof(ush));
+    zmemcpy(ds->l_buf, ss->l_buf, ss->sym_next);
 #else
     ds->sym_buf = ds->pending_buf + ds->lit_bufsize;
+    zmemcpy(ds->sym_buf, ss->sym_buf, ss->sym_next);
 #endif
 
     ds->l_desc.dyn_tree = ds->dyn_ltree;
@@ -1371,9 +1412,9 @@ int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) {
  */
 local uInt longest_match(deflate_state *s, IPos cur_match) {
     unsigned chain_length = s->max_chain_length;/* max hash chain length */
-    register Bytef *scan = s->window + s->strstart; /* current string */
-    register Bytef *match;                      /* matched string */
-    register int len;                           /* length of current match */
+    Bytef *scan = s->window + s->strstart;      /* current string */
+    Bytef *match;                               /* matched string */
+    int len;                                    /* length of current match */
     int best_len = (int)s->prev_length;         /* best match length so far */
     int nice_match = s->nice_match;             /* stop if match long enough */
     IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
@@ -1388,13 +1429,13 @@ local uInt longest_match(deflate_state *s, IPos cur_match) {
     /* Compare two bytes at a time. Note: this is not always beneficial.
      * Try with and without -DUNALIGNED_OK to check.
      */
-    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
-    register ush scan_start = *(ushf*)scan;
-    register ush scan_end   = *(ushf*)(scan + best_len - 1);
+    Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+    ush scan_start = *(ushf*)scan;
+    ush scan_end   = *(ushf*)(scan + best_len - 1);
 #else
-    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
-    register Byte scan_end1  = scan[best_len - 1];
-    register Byte scan_end   = scan[best_len];
+    Bytef *strend = s->window + s->strstart + MAX_MATCH;
+    Byte scan_end1  = scan[best_len - 1];
+    Byte scan_end   = scan[best_len];
 #endif
 
     /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
@@ -1518,10 +1559,10 @@ local uInt longest_match(deflate_state *s, IPos cur_match) {
  * Optimized version for FASTEST only
  */
 local uInt longest_match(deflate_state *s, IPos cur_match) {
-    register Bytef *scan = s->window + s->strstart; /* current string */
-    register Bytef *match;                       /* matched string */
-    register int len;                           /* length of current match */
-    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+    Bytef *scan = s->window + s->strstart;      /* current string */
+    Bytef *match;                               /* matched string */
+    int len;                                    /* length of current match */
+    Bytef *strend = s->window + s->strstart + MAX_MATCH;
 
     /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
      * It is easy to get rid of this optimization if necessary.
@@ -1581,7 +1622,7 @@ local uInt longest_match(deflate_state *s, IPos cur_match) {
 local void check_match(deflate_state *s, IPos start, IPos match, int length) {
     /* check that the match is indeed a match */
     Bytef *back = s->window + (int)match, *here = s->window + start;
-    IPos len = length;
+    IPos len = (IPos)length;
     if (match == (IPos)-1) {
         /* match starts one byte before the current window -- just compare the
            subsequent length-1 bytes */
@@ -1653,13 +1694,14 @@ local block_state deflate_stored(deflate_state *s, int flush) {
      * this is 32K. This can be as small as 507 bytes for memLevel == 1. For
      * large input and output buffers, the stored block size will be larger.
      */
-    unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size);
+    unsigned min_block = (unsigned)(MIN(s->pending_buf_size - 5, s->w_size));
 
     /* Copy as many min_block or larger stored blocks directly to next_out as
      * possible. If flushing, copy the remaining available input to next_out as
      * stored blocks, if there is enough space.
      */
-    unsigned len, left, have, last = 0;
+    int last = 0;
+    unsigned len, left, have;
     unsigned used = s->strm->avail_in;
     do {
         /* Set len to the maximum size block that we can copy directly with the
@@ -1667,12 +1709,12 @@ local block_state deflate_stored(deflate_state *s, int flush) {
          * would be copied from what's left in the window.
          */
         len = MAX_STORED;       /* maximum deflate stored block length */
-        have = (s->bi_valid + 42) >> 3;         /* number of header bytes */
+        have = ((unsigned)s->bi_valid + 42) >> 3;   /* bytes in header */
         if (s->strm->avail_out < have)          /* need room for header */
             break;
             /* maximum stored block length that will fit in avail_out: */
         have = s->strm->avail_out - have;
-        left = s->strstart - s->block_start;    /* bytes left in window */
+        left = (unsigned)(s->strstart - s->block_start);    /* window bytes */
         if (len > (ulg)left + s->strm->avail_in)
             len = left + s->strm->avail_in;     /* limit len to the input */
         if (len > have)
@@ -1695,10 +1737,10 @@ local block_state deflate_stored(deflate_state *s, int flush) {
         _tr_stored_block(s, (char *)0, 0L, last);
 
         /* Replace the lengths in the dummy stored block with len. */
-        s->pending_buf[s->pending - 4] = len;
-        s->pending_buf[s->pending - 3] = len >> 8;
-        s->pending_buf[s->pending - 2] = ~len;
-        s->pending_buf[s->pending - 1] = ~len >> 8;
+        s->pending_buf[s->pending - 4] = (Bytef)len;
+        s->pending_buf[s->pending - 3] = (Bytef)(len >> 8);
+        s->pending_buf[s->pending - 2] = (Bytef)~len;
+        s->pending_buf[s->pending - 1] = (Bytef)(~len >> 8);
 
         /* Write the stored block header bytes. */
         flush_pending(s->strm);
@@ -1769,8 +1811,10 @@ local block_state deflate_stored(deflate_state *s, int flush) {
         s->high_water = s->strstart;
 
     /* If the last block was written to next_out, then done. */
-    if (last)
+    if (last) {
+        s->bi_used = 8;
         return finish_done;
+    }
 
     /* If flushing and all input has been consumed, then done. */
     if (flush != Z_NO_FLUSH && flush != Z_FINISH &&
@@ -1778,7 +1822,7 @@ local block_state deflate_stored(deflate_state *s, int flush) {
         return block_done;
 
     /* Fill the window with any remaining input. */
-    have = s->window_size - s->strstart;
+    have = (unsigned)(s->window_size - s->strstart);
     if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) {
         /* Slide the window down. */
         s->block_start -= s->w_size;
@@ -1805,11 +1849,11 @@ local block_state deflate_stored(deflate_state *s, int flush) {
      * have enough input for a worthy block, or if flushing and there is enough
      * room for the remaining input as a stored block in the pending buffer.
      */
-    have = (s->bi_valid + 42) >> 3;         /* number of header bytes */
+    have = ((unsigned)s->bi_valid + 42) >> 3;   /* bytes in header */
         /* maximum stored block length that will fit in pending: */
-    have = MIN(s->pending_buf_size - have, MAX_STORED);
+    have = (unsigned)MIN(s->pending_buf_size - have, MAX_STORED);
     min_block = MIN(have, s->w_size);
-    left = s->strstart - s->block_start;
+    left = (unsigned)(s->strstart - s->block_start);
     if (left >= min_block ||
         ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH &&
          s->strm->avail_in == 0 && left <= have)) {
@@ -1822,6 +1866,8 @@ local block_state deflate_stored(deflate_state *s, int flush) {
     }
 
     /* We've done all we can with the available input and output. */
+    if (last)
+        s->bi_used = 8;
     return last ? finish_started : need_more;
 }
 
@@ -1870,7 +1916,7 @@ local block_state deflate_fast(deflate_state *s, int flush) {
             /* longest_match() sets match_start */
         }
         if (s->match_length >= MIN_MATCH) {
-            check_match(s, s->strstart, s->match_start, s->match_length);
+            check_match(s, s->strstart, s->match_start, (int)s->match_length);
 
             _tr_tally_dist(s, s->strstart - s->match_start,
                            s->match_length - MIN_MATCH, bflush);
@@ -1992,7 +2038,7 @@ local block_state deflate_slow(deflate_state *s, int flush) {
             uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
             /* Do not insert strings in hash table beyond this. */
 
-            check_match(s, s->strstart - 1, s->prev_match, s->prev_length);
+            check_match(s, s->strstart - 1, s->prev_match, (int)s->prev_length);
 
             _tr_tally_dist(s, s->strstart - 1 - s->prev_match,
                            s->prev_length - MIN_MATCH, bflush);
@@ -2100,7 +2146,7 @@ local block_state deflate_rle(deflate_state *s, int flush) {
 
         /* Emit match if have run of MIN_MATCH or longer, else emit literal */
         if (s->match_length >= MIN_MATCH) {
-            check_match(s, s->strstart, s->strstart - 1, s->match_length);
+            check_match(s, s->strstart, s->strstart - 1, (int)s->match_length);
 
             _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush);
 
diff --git a/src/java.base/share/native/libzip/zlib/deflate.h b/src/java.base/share/native/libzip/zlib/deflate.h
index 830d46b8894..5b6246ee3c4 100644
--- a/src/java.base/share/native/libzip/zlib/deflate.h
+++ b/src/java.base/share/native/libzip/zlib/deflate.h
@@ -23,7 +23,7 @@
  */
 
 /* deflate.h -- internal compression state
- * Copyright (C) 1995-2024 Jean-loup Gailly
+ * Copyright (C) 1995-2026 Jean-loup Gailly
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -295,6 +295,9 @@ typedef struct internal_state {
     /* Number of valid bits in bi_buf.  All bits above the last valid bit
      * are always zero.
      */
+    int bi_used;
+    /* Last number of used bits when going to a byte boundary.
+     */
 
     ulg high_water;
     /* High water mark offset in window for initialized bytes -- bytes above
@@ -303,6 +306,9 @@ typedef struct internal_state {
      * updated to the new high water mark.
      */
 
+    int slid;
+    /* True if the hash table has been slid since it was cleared. */
+
 } FAR deflate_state;
 
 /* Output a byte on the stream.
diff --git a/src/java.base/share/native/libzip/zlib/gzguts.h b/src/java.base/share/native/libzip/zlib/gzguts.h
index 8cce2c69d24..0be646016ed 100644
--- a/src/java.base/share/native/libzip/zlib/gzguts.h
+++ b/src/java.base/share/native/libzip/zlib/gzguts.h
@@ -23,7 +23,7 @@
  */
 
 /* gzguts.h -- zlib internal header definitions for gz* operations
- * Copyright (C) 2004-2024 Mark Adler
+ * Copyright (C) 2004-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -41,6 +41,18 @@
 #  define ZLIB_INTERNAL
 #endif
 
+#if defined(_WIN32)
+#  ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN
+#  endif
+#  ifndef _CRT_SECURE_NO_WARNINGS
+#    define _CRT_SECURE_NO_WARNINGS
+#  endif
+#  ifndef _CRT_NONSTDC_NO_DEPRECATE
+#    define _CRT_NONSTDC_NO_DEPRECATE
+#  endif
+#endif
+
 #include 
 #include "zlib.h"
 #ifdef STDC
@@ -49,8 +61,8 @@
 #  include 
 #endif
 
-#ifndef _POSIX_SOURCE
-#  define _POSIX_SOURCE
+#ifndef _POSIX_C_SOURCE
+#  define _POSIX_C_SOURCE 200112L
 #endif
 #include 
 
@@ -60,19 +72,13 @@
 
 #if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32)
 #  include 
+#  include 
 #endif
 
-#if defined(_WIN32)
+#if defined(_WIN32) && !defined(WIDECHAR)
 #  define WIDECHAR
 #endif
 
-#ifdef WINAPI_FAMILY
-#  define open _open
-#  define read _read
-#  define write _write
-#  define close _close
-#endif
-
 #ifdef NO_DEFLATE       /* for compatibility with old definition */
 #  define NO_GZCOMPRESS
 #endif
@@ -96,33 +102,28 @@
 #endif
 
 #ifndef HAVE_VSNPRINTF
-#  ifdef MSDOS
+#  if !defined(NO_vsnprintf) && \
+      (defined(MSDOS) || defined(__TURBOC__) || defined(__SASC) || \
+       defined(VMS) || defined(__OS400) || defined(__MVS__))
 /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
    but for now we just assume it doesn't. */
 #    define NO_vsnprintf
 #  endif
-#  ifdef __TURBOC__
-#    define NO_vsnprintf
-#  endif
 #  ifdef WIN32
 /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
-#    if !defined(vsnprintf) && !defined(NO_vsnprintf)
-#      if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
-#         define vsnprintf _vsnprintf
+#    if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
+#      ifndef vsnprintf
+#        define vsnprintf _vsnprintf
 #      endif
 #    endif
-#  endif
-#  ifdef __SASC
-#    define NO_vsnprintf
-#  endif
-#  ifdef VMS
-#    define NO_vsnprintf
-#  endif
-#  ifdef __OS400__
-#    define NO_vsnprintf
-#  endif
-#  ifdef __MVS__
-#    define NO_vsnprintf
+#  elif !defined(__STDC_VERSION__) || __STDC_VERSION__-0 < 199901L
+/* Otherwise if C89/90, assume no C99 snprintf() or vsnprintf() */
+#    ifndef NO_snprintf
+#      define NO_snprintf
+#    endif
+#    ifndef NO_vsnprintf
+#      define NO_vsnprintf
+#    endif
 #  endif
 #endif
 
@@ -206,7 +207,9 @@ typedef struct {
     unsigned char *out;     /* output buffer (double-sized when reading) */
     int direct;             /* 0 if processing gzip, 1 if transparent */
         /* just for reading */
+    int junk;               /* -1 = start, 1 = junk candidate, 0 = in gzip */
     int how;                /* 0: get header, 1: copy, 2: decompress */
+    int again;              /* true if EAGAIN or EWOULDBLOCK on last i/o */
     z_off64_t start;        /* where the gzip data started, for rewinding */
     int eof;                /* true if end of input file reached */
     int past;               /* true if read requested past end */
@@ -216,7 +219,6 @@ typedef struct {
     int reset;              /* true if a reset is pending after a Z_FINISH */
         /* seek request */
     z_off64_t skip;         /* amount to skip (already rewound if backwards) */
-    int seek;               /* true if seek request pending */
         /* error information */
     int err;                /* error code */
     char *msg;              /* error message */
diff --git a/src/java.base/share/native/libzip/zlib/gzlib.c b/src/java.base/share/native/libzip/zlib/gzlib.c
index 0f4dfae64a0..9489bcc1f12 100644
--- a/src/java.base/share/native/libzip/zlib/gzlib.c
+++ b/src/java.base/share/native/libzip/zlib/gzlib.c
@@ -23,21 +23,21 @@
  */
 
 /* gzlib.c -- zlib functions common to reading and writing gzip files
- * Copyright (C) 2004-2024 Mark Adler
+ * Copyright (C) 2004-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
 #include "gzguts.h"
 
-#if defined(_WIN32) && !defined(__BORLANDC__)
+#if defined(__DJGPP__)
+#  define LSEEK llseek
+#elif defined(_WIN32) && !defined(__BORLANDC__) && !defined(UNDER_CE)
 #  define LSEEK _lseeki64
-#else
-#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
+#elif defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
 #  define LSEEK lseek64
 #else
 #  define LSEEK lseek
 #endif
-#endif
 
 #if defined UNDER_CE
 
@@ -76,7 +76,7 @@ char ZLIB_INTERNAL *gz_strwinerror(DWORD error) {
             msgbuf[chars] = 0;
         }
 
-        wcstombs(buf, msgbuf, chars + 1);
+        wcstombs(buf, msgbuf, chars + 1);       /* assumes buf is big enough */
         LocalFree(msgbuf);
     }
     else {
@@ -96,10 +96,12 @@ local void gz_reset(gz_statep state) {
         state->eof = 0;             /* not at end of file */
         state->past = 0;            /* have not read past end yet */
         state->how = LOOK;          /* look for gzip header */
+        state->junk = -1;           /* mark first member */
     }
     else                            /* for writing ... */
         state->reset = 0;           /* no deflateReset pending */
-    state->seek = 0;                /* no seek request pending */
+    state->again = 0;               /* no stalled i/o yet */
+    state->skip = 0;                /* no seek request pending */
     gz_error(state, Z_OK, NULL);    /* clear error */
     state->x.pos = 0;               /* no uncompressed data yet */
     state->strm.avail_in = 0;       /* no input data yet */
@@ -109,16 +111,13 @@ local void gz_reset(gz_statep state) {
 local gzFile gz_open(const void *path, int fd, const char *mode) {
     gz_statep state;
     z_size_t len;
-    int oflag;
-#ifdef O_CLOEXEC
-    int cloexec = 0;
-#endif
+    int oflag = 0;
 #ifdef O_EXCL
     int exclusive = 0;
 #endif
 
     /* check input */
-    if (path == NULL)
+    if (path == NULL || mode == NULL)
         return NULL;
 
     /* allocate gzFile structure to return */
@@ -127,6 +126,7 @@ local gzFile gz_open(const void *path, int fd, const char *mode) {
         return NULL;
     state->size = 0;            /* no buffers allocated yet */
     state->want = GZBUFSIZE;    /* requested buffer size */
+    state->err = Z_OK;          /* no error yet */
     state->msg = NULL;          /* no error message yet */
 
     /* interpret mode */
@@ -157,7 +157,7 @@ local gzFile gz_open(const void *path, int fd, const char *mode) {
                 break;
 #ifdef O_CLOEXEC
             case 'e':
-                cloexec = 1;
+                oflag |= O_CLOEXEC;
                 break;
 #endif
 #ifdef O_EXCL
@@ -177,6 +177,14 @@ local gzFile gz_open(const void *path, int fd, const char *mode) {
             case 'F':
                 state->strategy = Z_FIXED;
                 break;
+            case 'G':
+                state->direct = -1;
+                break;
+#ifdef O_NONBLOCK
+            case 'N':
+                oflag |= O_NONBLOCK;
+                break;
+#endif
             case 'T':
                 state->direct = 1;
                 break;
@@ -192,22 +200,30 @@ local gzFile gz_open(const void *path, int fd, const char *mode) {
         return NULL;
     }
 
-    /* can't force transparent read */
+    /* direct is 0, 1 if "T", or -1 if "G" (last "G" or "T" wins) */
     if (state->mode == GZ_READ) {
-        if (state->direct) {
+        if (state->direct == 1) {
+            /* can't force a transparent read */
             free(state);
             return NULL;
         }
-        state->direct = 1;      /* for empty file */
+        if (state->direct == 0)
+            /* default when reading is auto-detect of gzip vs. transparent --
+               start with a transparent assumption in case of an empty file */
+            state->direct = 1;
     }
+    else if (state->direct == -1) {
+        /* "G" has no meaning when writing -- disallow it */
+        free(state);
+        return NULL;
+    }
+    /* if reading, direct == 1 for auto-detect, -1 for gzip only; if writing or
+       appending, direct == 0 for gzip, 1 for transparent (copy in to out) */
 
     /* save the path name for error messages */
 #ifdef WIDECHAR
-    if (fd == -2) {
+    if (fd == -2)
         len = wcstombs(NULL, path, 0);
-        if (len == (z_size_t)-1)
-            len = 0;
-    }
     else
 #endif
         len = strlen((const char *)path);
@@ -217,29 +233,29 @@ local gzFile gz_open(const void *path, int fd, const char *mode) {
         return NULL;
     }
 #ifdef WIDECHAR
-    if (fd == -2)
+    if (fd == -2) {
         if (len)
             wcstombs(state->path, path, len + 1);
         else
             *(state->path) = 0;
+    }
     else
 #endif
+    {
 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
         (void)snprintf(state->path, len + 1, "%s", (const char *)path);
 #else
         strcpy(state->path, path);
 #endif
+    }
 
     /* compute the flags for open() */
-    oflag =
+    oflag |=
 #ifdef O_LARGEFILE
         O_LARGEFILE |
 #endif
 #ifdef O_BINARY
         O_BINARY |
-#endif
-#ifdef O_CLOEXEC
-        (cloexec ? O_CLOEXEC : 0) |
 #endif
         (state->mode == GZ_READ ?
          O_RDONLY :
@@ -252,11 +268,23 @@ local gzFile gz_open(const void *path, int fd, const char *mode) {
            O_APPEND)));
 
     /* open the file with the appropriate flags (or just use fd) */
-    state->fd = fd > -1 ? fd : (
+    if (fd == -1)
+        state->fd = open((const char *)path, oflag, 0666);
 #ifdef WIDECHAR
-        fd == -2 ? _wopen(path, oflag, 0666) :
+    else if (fd == -2)
+        state->fd = _wopen(path, oflag, _S_IREAD | _S_IWRITE);
 #endif
-        open((const char *)path, oflag, 0666));
+    else {
+#ifdef O_NONBLOCK
+        if (oflag & O_NONBLOCK)
+            fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+#endif
+#ifdef O_CLOEXEC
+        if (oflag & O_CLOEXEC)
+            fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | O_CLOEXEC);
+#endif
+        state->fd = fd;
+    }
     if (state->fd == -1) {
         free(state->path);
         free(state);
@@ -383,9 +411,10 @@ z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) {
     /* normalize offset to a SEEK_CUR specification */
     if (whence == SEEK_SET)
         offset -= state->x.pos;
-    else if (state->seek)
-        offset += state->skip;
-    state->seek = 0;
+    else {
+        offset += state->past ? 0 : state->skip;
+        state->skip = 0;
+    }
 
     /* if within raw area while reading, just go there */
     if (state->mode == GZ_READ && state->how == COPY &&
@@ -396,7 +425,7 @@ z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) {
         state->x.have = 0;
         state->eof = 0;
         state->past = 0;
-        state->seek = 0;
+        state->skip = 0;
         gz_error(state, Z_OK, NULL);
         state->strm.avail_in = 0;
         state->x.pos += offset;
@@ -425,10 +454,7 @@ z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) {
     }
 
     /* request skip (if not zero) */
-    if (offset) {
-        state->seek = 1;
-        state->skip = offset;
-    }
+    state->skip = offset;
     return state->x.pos + offset;
 }
 
@@ -452,7 +478,7 @@ z_off64_t ZEXPORT gztell64(gzFile file) {
         return -1;
 
     /* return position */
-    return state->x.pos + (state->seek ? state->skip : 0);
+    return state->x.pos + (state->past ? 0 : state->skip);
 }
 
 /* -- see zlib.h -- */
@@ -559,7 +585,7 @@ void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) {
     }
 
     /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
-    if (err != Z_OK && err != Z_BUF_ERROR)
+    if (err != Z_OK && err != Z_BUF_ERROR && !state->again)
         state->x.have = 0;
 
     /* set error code, and if no message, then done */
@@ -596,6 +622,7 @@ unsigned ZLIB_INTERNAL gz_intmax(void) {
     return INT_MAX;
 #else
     unsigned p = 1, q;
+
     do {
         q = p;
         p <<= 1;
diff --git a/src/java.base/share/native/libzip/zlib/gzread.c b/src/java.base/share/native/libzip/zlib/gzread.c
index 7b9c9df5fa1..89144d2e56f 100644
--- a/src/java.base/share/native/libzip/zlib/gzread.c
+++ b/src/java.base/share/native/libzip/zlib/gzread.c
@@ -23,7 +23,7 @@
  */
 
 /* gzread.c -- zlib functions for reading gzip files
- * Copyright (C) 2004-2017 Mark Adler
+ * Copyright (C) 2004-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -32,23 +32,36 @@
 /* Use read() to load a buffer -- return -1 on error, otherwise 0.  Read from
    state->fd, and update state->eof, state->err, and state->msg as appropriate.
    This function needs to loop on read(), since read() is not guaranteed to
-   read the number of bytes requested, depending on the type of descriptor. */
+   read the number of bytes requested, depending on the type of descriptor. It
+   also needs to loop to manage the fact that read() returns an int. If the
+   descriptor is non-blocking and read() returns with no data in order to avoid
+   blocking, then gz_load() will return 0 if some data has been read, or -1 if
+   no data has been read. Either way, state->again is set true to indicate a
+   non-blocking event. If errno is non-zero on return, then there was an error
+   signaled from read().  *have is set to the number of bytes read. */
 local int gz_load(gz_statep state, unsigned char *buf, unsigned len,
                   unsigned *have) {
     int ret;
     unsigned get, max = ((unsigned)-1 >> 2) + 1;
 
+    state->again = 0;
+    errno = 0;
     *have = 0;
     do {
         get = len - *have;
         if (get > max)
             get = max;
-        ret = read(state->fd, buf + *have, get);
+        ret = (int)read(state->fd, buf + *have, get);
         if (ret <= 0)
             break;
         *have += (unsigned)ret;
     } while (*have < len);
     if (ret < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            state->again = 1;
+            if (*have != 0)
+                return 0;
+        }
         gz_error(state, Z_ERRNO, zstrerror());
         return -1;
     }
@@ -74,10 +87,14 @@ local int gz_avail(gz_statep state) {
         if (strm->avail_in) {       /* copy what's there to the start */
             unsigned char *p = state->in;
             unsigned const char *q = strm->next_in;
-            unsigned n = strm->avail_in;
-            do {
-                *p++ = *q++;
-            } while (--n);
+
+            if (q != p) {
+                unsigned n = strm->avail_in;
+
+                do {
+                    *p++ = *q++;
+                } while (--n);
+            }
         }
         if (gz_load(state, state->in + strm->avail_in,
                     state->size - strm->avail_in, &got) == -1)
@@ -128,39 +145,44 @@ local int gz_look(gz_statep state) {
         }
     }
 
-    /* get at least the magic bytes in the input buffer */
-    if (strm->avail_in < 2) {
-        if (gz_avail(state) == -1)
-            return -1;
-        if (strm->avail_in == 0)
-            return 0;
-    }
-
-    /* look for gzip magic bytes -- if there, do gzip decoding (note: there is
-       a logical dilemma here when considering the case of a partially written
-       gzip file, to wit, if a single 31 byte is written, then we cannot tell
-       whether this is a single-byte file, or just a partially written gzip
-       file -- for here we assume that if a gzip file is being written, then
-       the header will be written in a single operation, so that reading a
-       single byte is sufficient indication that it is not a gzip file) */
-    if (strm->avail_in > 1 &&
-            strm->next_in[0] == 31 && strm->next_in[1] == 139) {
+    /* if transparent reading is disabled, which would only be at the start, or
+       if we're looking for a gzip member after the first one, which is not at
+       the start, then proceed directly to look for a gzip member next */
+    if (state->direct == -1 || state->junk == 0) {
         inflateReset(strm);
         state->how = GZIP;
+        state->junk = state->junk != -1;
         state->direct = 0;
         return 0;
     }
 
-    /* no gzip header -- if we were decoding gzip before, then this is trailing
-       garbage.  Ignore the trailing garbage and finish. */
-    if (state->direct == 0) {
-        strm->avail_in = 0;
-        state->eof = 1;
-        state->x.have = 0;
+    /* otherwise we're at the start with auto-detect -- we check to see if the
+       first four bytes could be gzip header in order to decide whether or not
+       this will be a transparent read */
+
+    /* load any header bytes into the input buffer -- if the input is empty,
+       then it's not an error as this is a transparent read of zero bytes */
+    if (gz_avail(state) == -1)
+        return -1;
+    if (strm->avail_in == 0 || (state->again && strm->avail_in < 4))
+        /* if non-blocking input stalled before getting four bytes, then
+           return and wait until a later call has accumulated enough */
+        return 0;
+
+    /* see if this is (likely) gzip input -- if the first four bytes are
+       consistent with a gzip header, then go look for the first gzip member,
+       otherwise proceed to copy the input transparently */
+    if (strm->avail_in > 3 &&
+            strm->next_in[0] == 31 && strm->next_in[1] == 139 &&
+            strm->next_in[2] == 8 && strm->next_in[3] < 32) {
+        inflateReset(strm);
+        state->how = GZIP;
+        state->junk = 1;
+        state->direct = 0;
         return 0;
     }
 
-    /* doing raw i/o, copy any leftover input to output -- this assumes that
+    /* doing raw i/o: copy any leftover input to output -- this assumes that
        the output buffer is larger than the input buffer, which also assures
        space for gzungetc() */
     state->x.next = state->out;
@@ -168,15 +190,17 @@ local int gz_look(gz_statep state) {
     state->x.have = strm->avail_in;
     strm->avail_in = 0;
     state->how = COPY;
-    state->direct = 1;
     return 0;
 }
 
 /* Decompress from input to the provided next_out and avail_out in the state.
    On return, state->x.have and state->x.next point to the just decompressed
-   data.  If the gzip stream completes, state->how is reset to LOOK to look for
-   the next gzip stream or raw data, once state->x.have is depleted.  Returns 0
-   on success, -1 on failure. */
+   data. If the gzip stream completes, state->how is reset to LOOK to look for
+   the next gzip stream or raw data, once state->x.have is depleted. Returns 0
+   on success, -1 on failure. If EOF is reached when looking for more input to
+   complete the gzip member, then an unexpected end of file error is raised.
+   If there is no more input, but state->again is true, then EOF has not been
+   reached, and no error is raised. */
 local int gz_decomp(gz_statep state) {
     int ret = Z_OK;
     unsigned had;
@@ -186,28 +210,41 @@ local int gz_decomp(gz_statep state) {
     had = strm->avail_out;
     do {
         /* get more input for inflate() */
-        if (strm->avail_in == 0 && gz_avail(state) == -1)
-            return -1;
+        if (strm->avail_in == 0 && gz_avail(state) == -1) {
+            ret = state->err;
+            break;
+        }
         if (strm->avail_in == 0) {
-            gz_error(state, Z_BUF_ERROR, "unexpected end of file");
+            if (!state->again)
+                gz_error(state, Z_BUF_ERROR, "unexpected end of file");
             break;
         }
 
         /* decompress and handle errors */
         ret = inflate(strm, Z_NO_FLUSH);
+        if (strm->avail_out < had)
+            /* any decompressed data marks this as a real gzip stream */
+            state->junk = 0;
         if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
             gz_error(state, Z_STREAM_ERROR,
                      "internal error: inflate stream corrupt");
-            return -1;
+            break;
         }
         if (ret == Z_MEM_ERROR) {
             gz_error(state, Z_MEM_ERROR, "out of memory");
-            return -1;
+            break;
         }
         if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */
+            if (state->junk == 1) {             /* trailing garbage is ok */
+                strm->avail_in = 0;
+                state->eof = 1;
+                state->how = LOOK;
+                ret = Z_OK;
+                break;
+            }
             gz_error(state, Z_DATA_ERROR,
                      strm->msg == NULL ? "compressed data error" : strm->msg);
-            return -1;
+            break;
         }
     } while (strm->avail_out && ret != Z_STREAM_END);
 
@@ -216,11 +253,14 @@ local int gz_decomp(gz_statep state) {
     state->x.next = strm->next_out - state->x.have;
 
     /* if the gzip stream completed successfully, look for another */
-    if (ret == Z_STREAM_END)
+    if (ret == Z_STREAM_END) {
+        state->junk = 0;
         state->how = LOOK;
+        return 0;
+    }
 
-    /* good decompression */
-    return 0;
+    /* return decompression status */
+    return ret != Z_OK ? -1 : 0;
 }
 
 /* Fetch data and put it in the output buffer.  Assumes state->x.have is 0.
@@ -251,25 +291,31 @@ local int gz_fetch(gz_statep state) {
             strm->next_out = state->out;
             if (gz_decomp(state) == -1)
                 return -1;
+            break;
+        default:
+            gz_error(state, Z_STREAM_ERROR, "state corrupt");
+            return -1;
         }
     } while (state->x.have == 0 && (!state->eof || strm->avail_in));
     return 0;
 }
 
-/* Skip len uncompressed bytes of output.  Return -1 on error, 0 on success. */
-local int gz_skip(gz_statep state, z_off64_t len) {
+/* Skip state->skip (> 0) uncompressed bytes of output.  Return -1 on error, 0
+   on success. */
+local int gz_skip(gz_statep state) {
     unsigned n;
 
     /* skip over len bytes or reach end-of-file, whichever comes first */
-    while (len)
+    do {
         /* skip over whatever is in output buffer */
         if (state->x.have) {
-            n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ?
-                (unsigned)len : state->x.have;
+            n = GT_OFF(state->x.have) ||
+                (z_off64_t)state->x.have > state->skip ?
+                (unsigned)state->skip : state->x.have;
             state->x.have -= n;
             state->x.next += n;
             state->x.pos += n;
-            len -= n;
+            state->skip -= n;
         }
 
         /* output buffer empty -- return if we're at the end of the input */
@@ -282,30 +328,32 @@ local int gz_skip(gz_statep state, z_off64_t len) {
             if (gz_fetch(state) == -1)
                 return -1;
         }
+    } while (state->skip);
     return 0;
 }
 
 /* Read len bytes into buf from file, or less than len up to the end of the
-   input.  Return the number of bytes read.  If zero is returned, either the
-   end of file was reached, or there was an error.  state->err must be
-   consulted in that case to determine which. */
+   input. Return the number of bytes read. If zero is returned, either the end
+   of file was reached, or there was an error. state->err must be consulted in
+   that case to determine which. If there was an error, but some uncompressed
+   bytes were read before the error, then that count is returned. The error is
+   still recorded, and so is deferred until the next call. */
 local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) {
     z_size_t got;
     unsigned n;
+    int err;
 
     /* if len is zero, avoid unnecessary operations */
     if (len == 0)
         return 0;
 
     /* process a skip request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_skip(state, state->skip) == -1)
-            return 0;
-    }
+    if (state->skip && gz_skip(state) == -1)
+        return 0;
 
     /* get len bytes to buf, or less than len if at the end */
     got = 0;
+    err = 0;
     do {
         /* set n to the maximum amount of len that fits in an unsigned int */
         n = (unsigned)-1;
@@ -319,37 +367,36 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) {
             memcpy(buf, state->x.next, n);
             state->x.next += n;
             state->x.have -= n;
+            if (state->err != Z_OK)
+                /* caught deferred error from gz_fetch() */
+                err = -1;
         }
 
         /* output buffer empty -- return if we're at the end of the input */
-        else if (state->eof && state->strm.avail_in == 0) {
-            state->past = 1;        /* tried to read past end */
+        else if (state->eof && state->strm.avail_in == 0)
             break;
-        }
 
         /* need output data -- for small len or new stream load up our output
-           buffer */
+           buffer, so that gzgetc() can be fast */
         else if (state->how == LOOK || n < (state->size << 1)) {
             /* get more output, looking for header if required */
-            if (gz_fetch(state) == -1)
-                return 0;
+            if (gz_fetch(state) == -1 && state->x.have == 0)
+                /* if state->x.have != 0, error will be caught after copy */
+                err = -1;
             continue;       /* no progress yet -- go back to copy above */
             /* the copy above assures that we will leave with space in the
                output buffer, allowing at least one gzungetc() to succeed */
         }
 
         /* large len -- read directly into user buffer */
-        else if (state->how == COPY) {      /* read directly */
-            if (gz_load(state, (unsigned char *)buf, n, &n) == -1)
-                return 0;
-        }
+        else if (state->how == COPY)        /* read directly */
+            err = gz_load(state, (unsigned char *)buf, n, &n);
 
         /* large len -- decompress directly into user buffer */
         else {  /* state->how == GZIP */
             state->strm.avail_out = n;
             state->strm.next_out = (unsigned char *)buf;
-            if (gz_decomp(state) == -1)
-                return 0;
+            err = gz_decomp(state);
             n = state->x.have;
             state->x.have = 0;
         }
@@ -359,7 +406,11 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) {
         buf = (char *)buf + n;
         got += n;
         state->x.pos += n;
-    } while (len);
+    } while (len && !err);
+
+    /* note read past eof */
+    if (len && state->eof)
+        state->past = 1;
 
     /* return number of bytes read into user buffer */
     return got;
@@ -369,16 +420,18 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) {
 int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) {
     gz_statep state;
 
-    /* get internal structure */
+    /* get internal structure and check that it's for reading */
     if (file == NULL)
         return -1;
     state = (gz_statep)file;
-
-    /* check that we're reading and that there's no (serious) error */
-    if (state->mode != GZ_READ ||
-            (state->err != Z_OK && state->err != Z_BUF_ERROR))
+    if (state->mode != GZ_READ)
         return -1;
 
+    /* check that there was no (serious) error */
+    if (state->err != Z_OK && state->err != Z_BUF_ERROR && !state->again)
+        return -1;
+    gz_error(state, Z_OK, NULL);
+
     /* since an int is returned, make sure len fits in one, otherwise return
        with an error (this avoids a flaw in the interface) */
     if ((int)len < 0) {
@@ -390,28 +443,40 @@ int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) {
     len = (unsigned)gz_read(state, buf, len);
 
     /* check for an error */
-    if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)
-        return -1;
+    if (len == 0) {
+        if (state->err != Z_OK && state->err != Z_BUF_ERROR)
+            return -1;
+        if (state->again) {
+            /* non-blocking input stalled after some input was read, but no
+               uncompressed bytes were produced -- let the application know
+               this isn't EOF */
+            gz_error(state, Z_ERRNO, zstrerror());
+            return -1;
+        }
+    }
 
-    /* return the number of bytes read (this is assured to fit in an int) */
+    /* return the number of bytes read */
     return (int)len;
 }
 
 /* -- see zlib.h -- */
-z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file) {
+z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems,
+                         gzFile file) {
     z_size_t len;
     gz_statep state;
 
-    /* get internal structure */
+    /* get internal structure and check that it's for reading */
     if (file == NULL)
         return 0;
     state = (gz_statep)file;
-
-    /* check that we're reading and that there's no (serious) error */
-    if (state->mode != GZ_READ ||
-            (state->err != Z_OK && state->err != Z_BUF_ERROR))
+    if (state->mode != GZ_READ)
         return 0;
 
+    /* check that there was no (serious) error */
+    if (state->err != Z_OK && state->err != Z_BUF_ERROR && !state->again)
+        return 0;
+    gz_error(state, Z_OK, NULL);
+
     /* compute bytes to read -- error on overflow */
     len = nitems * size;
     if (size && len / size != nitems) {
@@ -433,16 +498,18 @@ int ZEXPORT gzgetc(gzFile file) {
     unsigned char buf[1];
     gz_statep state;
 
-    /* get internal structure */
+    /* get internal structure and check that it's for reading */
     if (file == NULL)
         return -1;
     state = (gz_statep)file;
-
-    /* check that we're reading and that there's no (serious) error */
-    if (state->mode != GZ_READ ||
-        (state->err != Z_OK && state->err != Z_BUF_ERROR))
+    if (state->mode != GZ_READ)
         return -1;
 
+    /* check that there was no (serious) error */
+    if (state->err != Z_OK && state->err != Z_BUF_ERROR && !state->again)
+        return -1;
+    gz_error(state, Z_OK, NULL);
+
     /* try output buffer (no need to check for skip request) */
     if (state->x.have) {
         state->x.have--;
@@ -462,26 +529,25 @@ int ZEXPORT gzgetc_(gzFile file) {
 int ZEXPORT gzungetc(int c, gzFile file) {
     gz_statep state;
 
-    /* get internal structure */
+    /* get internal structure and check that it's for reading */
     if (file == NULL)
         return -1;
     state = (gz_statep)file;
-
-    /* in case this was just opened, set up the input buffer */
-    if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
-        (void)gz_look(state);
-
-    /* check that we're reading and that there's no (serious) error */
-    if (state->mode != GZ_READ ||
-        (state->err != Z_OK && state->err != Z_BUF_ERROR))
+    if (state->mode != GZ_READ)
         return -1;
 
+    /* in case this was just opened, set up the input buffer */
+    if (state->how == LOOK && state->x.have == 0)
+        (void)gz_look(state);
+
+    /* check that there was no (serious) error */
+    if (state->err != Z_OK && state->err != Z_BUF_ERROR && !state->again)
+        return -1;
+    gz_error(state, Z_OK, NULL);
+
     /* process a skip request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_skip(state, state->skip) == -1)
-            return -1;
-    }
+    if (state->skip && gz_skip(state) == -1)
+        return -1;
 
     /* can't push EOF */
     if (c < 0)
@@ -507,6 +573,7 @@ int ZEXPORT gzungetc(int c, gzFile file) {
     if (state->x.next == state->out) {
         unsigned char *src = state->out + state->x.have;
         unsigned char *dest = state->out + (state->size << 1);
+
         while (src > state->out)
             *--dest = *--src;
         state->x.next = dest;
@@ -526,32 +593,31 @@ char * ZEXPORT gzgets(gzFile file, char *buf, int len) {
     unsigned char *eol;
     gz_statep state;
 
-    /* check parameters and get internal structure */
+    /* check parameters, get internal structure, and check that it's for
+       reading */
     if (file == NULL || buf == NULL || len < 1)
         return NULL;
     state = (gz_statep)file;
-
-    /* check that we're reading and that there's no (serious) error */
-    if (state->mode != GZ_READ ||
-        (state->err != Z_OK && state->err != Z_BUF_ERROR))
+    if (state->mode != GZ_READ)
         return NULL;
 
-    /* process a skip request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_skip(state, state->skip) == -1)
-            return NULL;
-    }
+    /* check that there was no (serious) error */
+    if (state->err != Z_OK && state->err != Z_BUF_ERROR && !state->again)
+        return NULL;
+    gz_error(state, Z_OK, NULL);
 
-    /* copy output bytes up to new line or len - 1, whichever comes first --
-       append a terminating zero to the string (we don't check for a zero in
-       the contents, let the user worry about that) */
+    /* process a skip request */
+    if (state->skip && gz_skip(state) == -1)
+        return NULL;
+
+    /* copy output up to a new line, len-1 bytes, or there is no more output,
+       whichever comes first */
     str = buf;
     left = (unsigned)len - 1;
     if (left) do {
         /* assure that something is in the output buffer */
         if (state->x.have == 0 && gz_fetch(state) == -1)
-            return NULL;                /* error */
+            break;                      /* error */
         if (state->x.have == 0) {       /* end of file */
             state->past = 1;            /* read past end */
             break;                      /* return what we have */
@@ -572,7 +638,9 @@ char * ZEXPORT gzgets(gzFile file, char *buf, int len) {
         buf += n;
     } while (left && eol == NULL);
 
-    /* return terminated string, or if nothing, end of file */
+    /* append a terminating zero to the string (we don't check for a zero in
+       the contents, let the user worry about that) -- return the terminated
+       string, or if nothing was read, NULL */
     if (buf == str)
         return NULL;
     buf[0] = 0;
@@ -594,7 +662,7 @@ int ZEXPORT gzdirect(gzFile file) {
         (void)gz_look(state);
 
     /* return 1 if transparent, 0 if processing a gzip stream */
-    return state->direct;
+    return state->direct == 1;
 }
 
 /* -- see zlib.h -- */
@@ -602,12 +670,10 @@ int ZEXPORT gzclose_r(gzFile file) {
     int ret, err;
     gz_statep state;
 
-    /* get internal structure */
+    /* get internal structure and check that it's for reading */
     if (file == NULL)
         return Z_STREAM_ERROR;
     state = (gz_statep)file;
-
-    /* check that we're reading */
     if (state->mode != GZ_READ)
         return Z_STREAM_ERROR;
 
diff --git a/src/java.base/share/native/libzip/zlib/gzwrite.c b/src/java.base/share/native/libzip/zlib/gzwrite.c
index 008b03e7021..b11c318d543 100644
--- a/src/java.base/share/native/libzip/zlib/gzwrite.c
+++ b/src/java.base/share/native/libzip/zlib/gzwrite.c
@@ -23,7 +23,7 @@
  */
 
 /* gzwrite.c -- zlib functions for writing gzip files
- * Copyright (C) 2004-2019 Mark Adler
+ * Copyright (C) 2004-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -98,9 +98,13 @@ local int gz_comp(gz_statep state, int flush) {
     /* write directly if requested */
     if (state->direct) {
         while (strm->avail_in) {
+            errno = 0;
+            state->again = 0;
             put = strm->avail_in > max ? max : strm->avail_in;
-            writ = write(state->fd, strm->next_in, put);
+            writ = (int)write(state->fd, strm->next_in, put);
             if (writ < 0) {
+                if (errno == EAGAIN || errno == EWOULDBLOCK)
+                    state->again = 1;
                 gz_error(state, Z_ERRNO, zstrerror());
                 return -1;
             }
@@ -112,8 +116,9 @@ local int gz_comp(gz_statep state, int flush) {
 
     /* check for a pending reset */
     if (state->reset) {
-        /* don't start a new gzip member unless there is data to write */
-        if (strm->avail_in == 0)
+        /* don't start a new gzip member unless there is data to write and
+           we're not flushing */
+        if (strm->avail_in == 0 && flush == Z_NO_FLUSH)
             return 0;
         deflateReset(strm);
         state->reset = 0;
@@ -127,10 +132,14 @@ local int gz_comp(gz_statep state, int flush) {
         if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
             (flush != Z_FINISH || ret == Z_STREAM_END))) {
             while (strm->next_out > state->x.next) {
+                errno = 0;
+                state->again = 0;
                 put = strm->next_out - state->x.next > (int)max ? max :
                       (unsigned)(strm->next_out - state->x.next);
-                writ = write(state->fd, state->x.next, put);
+                writ = (int)write(state->fd, state->x.next, put);
                 if (writ < 0) {
+                    if (errno == EAGAIN || errno == EWOULDBLOCK)
+                        state->again = 1;
                     gz_error(state, Z_ERRNO, zstrerror());
                     return -1;
                 }
@@ -162,10 +171,12 @@ local int gz_comp(gz_statep state, int flush) {
     return 0;
 }
 
-/* Compress len zeros to output.  Return -1 on a write error or memory
-   allocation failure by gz_comp(), or 0 on success. */
-local int gz_zero(gz_statep state, z_off64_t len) {
-    int first;
+/* Compress state->skip (> 0) zeros to output.  Return -1 on a write error or
+   memory allocation failure by gz_comp(), or 0 on success. state->skip is
+   updated with the number of successfully written zeros, in case there is a
+   stall on a non-blocking write destination. */
+local int gz_zero(gz_statep state) {
+    int first, ret;
     unsigned n;
     z_streamp strm = &(state->strm);
 
@@ -173,29 +184,34 @@ local int gz_zero(gz_statep state, z_off64_t len) {
     if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
         return -1;
 
-    /* compress len zeros (len guaranteed > 0) */
+    /* compress state->skip zeros */
     first = 1;
-    while (len) {
-        n = GT_OFF(state->size) || (z_off64_t)state->size > len ?
-            (unsigned)len : state->size;
+    do {
+        n = GT_OFF(state->size) || (z_off64_t)state->size > state->skip ?
+            (unsigned)state->skip : state->size;
         if (first) {
             memset(state->in, 0, n);
             first = 0;
         }
         strm->avail_in = n;
         strm->next_in = state->in;
+        ret = gz_comp(state, Z_NO_FLUSH);
+        n -= strm->avail_in;
         state->x.pos += n;
-        if (gz_comp(state, Z_NO_FLUSH) == -1)
+        state->skip -= n;
+        if (ret == -1)
             return -1;
-        len -= n;
-    }
+    } while (state->skip);
     return 0;
 }
 
 /* Write len bytes from buf to file.  Return the number of bytes written.  If
-   the returned value is less than len, then there was an error. */
+   the returned value is less than len, then there was an error. If the error
+   was a non-blocking stall, then the number of bytes consumed is returned.
+   For any other error, 0 is returned. */
 local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
     z_size_t put = len;
+    int ret;
 
     /* if len is zero, avoid unnecessary operations */
     if (len == 0)
@@ -206,16 +222,13 @@ local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
         return 0;
 
     /* check for seek request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_zero(state, state->skip) == -1)
-            return 0;
-    }
+    if (state->skip && gz_zero(state) == -1)
+        return 0;
 
     /* for small len, copy to input buffer, otherwise compress directly */
     if (len < state->size) {
         /* copy to input buffer, compress when full */
-        do {
+        for (;;) {
             unsigned have, copy;
 
             if (state->strm.avail_in == 0)
@@ -230,9 +243,11 @@ local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
             state->x.pos += copy;
             buf = (const char *)buf + copy;
             len -= copy;
-            if (len && gz_comp(state, Z_NO_FLUSH) == -1)
-                return 0;
-        } while (len);
+            if (len == 0)
+                break;
+            if (gz_comp(state, Z_NO_FLUSH) == -1)
+                return state->again ? put - len : 0;
+        }
     }
     else {
         /* consume whatever's left in the input buffer */
@@ -243,13 +258,16 @@ local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
         state->strm.next_in = (z_const Bytef *)buf;
         do {
             unsigned n = (unsigned)-1;
+
             if (n > len)
                 n = (unsigned)len;
             state->strm.avail_in = n;
+            ret = gz_comp(state, Z_NO_FLUSH);
+            n -= state->strm.avail_in;
             state->x.pos += n;
-            if (gz_comp(state, Z_NO_FLUSH) == -1)
-                return 0;
             len -= n;
+            if (ret == -1)
+                return state->again ? put - len : 0;
         } while (len);
     }
 
@@ -266,9 +284,10 @@ int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) {
         return 0;
     state = (gz_statep)file;
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK)
+    /* check that we're writing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
         return 0;
+    gz_error(state, Z_OK, NULL);
 
     /* since an int is returned, make sure len fits in one, otherwise return
        with an error (this avoids a flaw in the interface) */
@@ -292,9 +311,10 @@ z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems,
         return 0;
     state = (gz_statep)file;
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK)
+    /* check that we're writing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
         return 0;
+    gz_error(state, Z_OK, NULL);
 
     /* compute bytes to read -- error on overflow */
     len = nitems * size;
@@ -320,16 +340,14 @@ int ZEXPORT gzputc(gzFile file, int c) {
     state = (gz_statep)file;
     strm = &(state->strm);
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK)
+    /* check that we're writing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
         return -1;
+    gz_error(state, Z_OK, NULL);
 
     /* check for seek request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_zero(state, state->skip) == -1)
-            return -1;
-    }
+    if (state->skip && gz_zero(state) == -1)
+        return -1;
 
     /* try writing to input buffer for speed (state->size == 0 if buffer not
        initialized) */
@@ -362,9 +380,10 @@ int ZEXPORT gzputs(gzFile file, const char *s) {
         return -1;
     state = (gz_statep)file;
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK)
+    /* check that we're writing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
         return -1;
+    gz_error(state, Z_OK, NULL);
 
     /* write string */
     len = strlen(s);
@@ -373,16 +392,47 @@ int ZEXPORT gzputs(gzFile file, const char *s) {
         return -1;
     }
     put = gz_write(state, s, len);
-    return put < len ? -1 : (int)len;
+    return len && put == 0 ? -1 : (int)put;
 }
 
+#if (((!defined(STDC) && !defined(Z_HAVE_STDARG_H)) || !defined(NO_vsnprintf)) && \
+     (defined(STDC) || defined(Z_HAVE_STDARG_H) || !defined(NO_snprintf))) || \
+    defined(ZLIB_INSECURE)
+/* If the second half of the input buffer is occupied, write out the contents.
+   If there is any input remaining due to a non-blocking stall on write, move
+   it to the start of the buffer. Return true if this did not open up the
+   second half of the buffer.  state->err should be checked after this to
+   handle a gz_comp() error. */
+local int gz_vacate(gz_statep state) {
+    z_streamp strm;
+
+    strm = &(state->strm);
+    if (strm->next_in + strm->avail_in <= state->in + state->size)
+        return 0;
+    (void)gz_comp(state, Z_NO_FLUSH);
+    if (strm->avail_in == 0) {
+        strm->next_in = state->in;
+        return 0;
+    }
+    memmove(state->in, strm->next_in, strm->avail_in);
+    strm->next_in = state->in;
+    return strm->avail_in > state->size;
+}
+#endif
+
 #if defined(STDC) || defined(Z_HAVE_STDARG_H)
 #include 
 
 /* -- see zlib.h -- */
 int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) {
-    int len;
-    unsigned left;
+#if defined(NO_vsnprintf) && !defined(ZLIB_INSECURE)
+#warning "vsnprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
+#warning "you can recompile with ZLIB_INSECURE defined to use vsprintf()"
+    /* prevent use of insecure vsprintf(), unless purposefully requested */
+    (void)file, (void)format, (void)va;
+    return Z_STREAM_ERROR;
+#else
+    int len, ret;
     char *next;
     gz_statep state;
     z_streamp strm;
@@ -393,24 +443,34 @@ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) {
     state = (gz_statep)file;
     strm = &(state->strm);
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK)
+    /* check that we're writing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
         return Z_STREAM_ERROR;
+    gz_error(state, Z_OK, NULL);
 
     /* make sure we have some buffer space */
     if (state->size == 0 && gz_init(state) == -1)
         return state->err;
 
     /* check for seek request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_zero(state, state->skip) == -1)
-            return state->err;
-    }
+    if (state->skip && gz_zero(state) == -1)
+        return state->err;
 
     /* do the printf() into the input buffer, put length in len -- the input
-       buffer is double-sized just for this function, so there is guaranteed to
-       be state->size bytes available after the current contents */
+       buffer is double-sized just for this function, so there should be
+       state->size bytes available after the current contents */
+    ret = gz_vacate(state);
+    if (state->err) {
+        if (ret && state->again) {
+            /* There was a non-blocking stall on write, resulting in the part
+               of the second half of the output buffer being occupied.  Return
+               a Z_BUF_ERROR to let the application know that this gzprintf()
+               needs to be retried. */
+            gz_error(state, Z_BUF_ERROR, "stalled write on gzprintf");
+        }
+        if (!state->again)
+            return state->err;
+    }
     if (strm->avail_in == 0)
         strm->next_in = state->in;
     next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
@@ -436,19 +496,16 @@ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) {
     if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
         return 0;
 
-    /* update buffer and position, compress first half if past that */
+    /* update buffer and position */
     strm->avail_in += (unsigned)len;
     state->x.pos += len;
-    if (strm->avail_in >= state->size) {
-        left = strm->avail_in - state->size;
-        strm->avail_in = state->size;
-        if (gz_comp(state, Z_NO_FLUSH) == -1)
-            return state->err;
-        memmove(state->in, state->in + state->size, left);
-        strm->next_in = state->in;
-        strm->avail_in = left;
-    }
+
+    /* write out buffer if more than half is occupied */
+    ret = gz_vacate(state);
+    if (state->err && !state->again)
+        return state->err;
     return len;
+#endif
 }
 
 int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) {
@@ -468,6 +525,17 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
                        int a4, int a5, int a6, int a7, int a8, int a9, int a10,
                        int a11, int a12, int a13, int a14, int a15, int a16,
                        int a17, int a18, int a19, int a20) {
+#if defined(NO_snprintf) && !defined(ZLIB_INSECURE)
+#warning "snprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
+#warning "you can recompile with ZLIB_INSECURE defined to use sprintf()"
+    /* prevent use of insecure sprintf(), unless purposefully requested */
+    (void)file, (void)format, (void)a1, (void)a2, (void)a3, (void)a4, (void)a5,
+    (void)a6, (void)a7, (void)a8, (void)a9, (void)a10, (void)a11, (void)a12,
+    (void)a13, (void)a14, (void)a15, (void)a16, (void)a17, (void)a18,
+    (void)a19, (void)a20;
+    return Z_STREAM_ERROR;
+#else
+    int ret;
     unsigned len, left;
     char *next;
     gz_statep state;
@@ -483,24 +551,34 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
     if (sizeof(int) != sizeof(void *))
         return Z_STREAM_ERROR;
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK)
+    /* check that we're writing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
         return Z_STREAM_ERROR;
+    gz_error(state, Z_OK, NULL);
 
     /* make sure we have some buffer space */
     if (state->size == 0 && gz_init(state) == -1)
-        return state->error;
+        return state->err;
 
     /* check for seek request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_zero(state, state->skip) == -1)
-            return state->error;
-    }
+    if (state->skip && gz_zero(state) == -1)
+        return state->err;
 
     /* do the printf() into the input buffer, put length in len -- the input
        buffer is double-sized just for this function, so there is guaranteed to
        be state->size bytes available after the current contents */
+    ret = gz_vacate(state);
+    if (state->err) {
+        if (ret && state->again) {
+            /* There was a non-blocking stall on write, resulting in the part
+               of the second half of the output buffer being occupied.  Return
+               a Z_BUF_ERROR to let the application know that this gzprintf()
+               needs to be retried. */
+            gz_error(state, Z_BUF_ERROR, "stalled write on gzprintf");
+        }
+        if (!state->again)
+            return state->err;
+    }
     if (strm->avail_in == 0)
         strm->next_in = state->in;
     next = (char *)(strm->next_in + strm->avail_in);
@@ -534,16 +612,13 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
     /* update buffer and position, compress first half if past that */
     strm->avail_in += len;
     state->x.pos += len;
-    if (strm->avail_in >= state->size) {
-        left = strm->avail_in - state->size;
-        strm->avail_in = state->size;
-        if (gz_comp(state, Z_NO_FLUSH) == -1)
-            return state->err;
-        memmove(state->in, state->in + state->size, left);
-        strm->next_in = state->in;
-        strm->avail_in = left;
-    }
+
+    /* write out buffer if more than half is occupied */
+    ret = gz_vacate(state);
+    if (state->err && !state->again)
+        return state->err;
     return (int)len;
+#endif
 }
 
 #endif
@@ -557,20 +632,18 @@ int ZEXPORT gzflush(gzFile file, int flush) {
         return Z_STREAM_ERROR;
     state = (gz_statep)file;
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK)
+    /* check that we're writing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again))
         return Z_STREAM_ERROR;
+    gz_error(state, Z_OK, NULL);
 
     /* check flush parameter */
     if (flush < 0 || flush > Z_FINISH)
         return Z_STREAM_ERROR;
 
     /* check for seek request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_zero(state, state->skip) == -1)
-            return state->err;
-    }
+    if (state->skip && gz_zero(state) == -1)
+        return state->err;
 
     /* compress remaining data with requested flush */
     (void)gz_comp(state, flush);
@@ -588,20 +661,19 @@ int ZEXPORT gzsetparams(gzFile file, int level, int strategy) {
     state = (gz_statep)file;
     strm = &(state->strm);
 
-    /* check that we're writing and that there's no error */
-    if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct)
+    /* check that we're compressing and that there's no (serious) error */
+    if (state->mode != GZ_WRITE || (state->err != Z_OK && !state->again) ||
+            state->direct)
         return Z_STREAM_ERROR;
+    gz_error(state, Z_OK, NULL);
 
     /* if no change is requested, then do nothing */
     if (level == state->level && strategy == state->strategy)
         return Z_OK;
 
     /* check for seek request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_zero(state, state->skip) == -1)
-            return state->err;
-    }
+    if (state->skip && gz_zero(state) == -1)
+        return state->err;
 
     /* change compression parameters for subsequent input */
     if (state->size) {
@@ -630,11 +702,8 @@ int ZEXPORT gzclose_w(gzFile file) {
         return Z_STREAM_ERROR;
 
     /* check for seek request */
-    if (state->seek) {
-        state->seek = 0;
-        if (gz_zero(state, state->skip) == -1)
-            ret = state->err;
-    }
+    if (state->skip && gz_zero(state) == -1)
+        ret = state->err;
 
     /* flush, free memory, and close file */
     if (gz_comp(state, Z_FINISH) == -1)
diff --git a/src/java.base/share/native/libzip/zlib/infback.c b/src/java.base/share/native/libzip/zlib/infback.c
index f680e2cdbdc..0becbb9eb4f 100644
--- a/src/java.base/share/native/libzip/zlib/infback.c
+++ b/src/java.base/share/native/libzip/zlib/infback.c
@@ -23,7 +23,7 @@
  */
 
 /* infback.c -- inflate using a call-back interface
- * Copyright (C) 1995-2022 Mark Adler
+ * Copyright (C) 1995-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -70,7 +70,7 @@ int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits,
 #ifdef Z_SOLO
         return Z_STREAM_ERROR;
 #else
-    strm->zfree = zcfree;
+        strm->zfree = zcfree;
 #endif
     state = (struct inflate_state FAR *)ZALLOC(strm, 1,
                                                sizeof(struct inflate_state));
@@ -87,57 +87,6 @@ int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits,
     return Z_OK;
 }
 
-/*
-   Return state with length and distance decoding tables and index sizes set to
-   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
-   If BUILDFIXED is defined, then instead this routine builds the tables the
-   first time it's called, and returns those tables the first time and
-   thereafter.  This reduces the size of the code by about 2K bytes, in
-   exchange for a little execution time.  However, BUILDFIXED should not be
-   used for threaded applications, since the rewriting of the tables and virgin
-   may not be thread-safe.
- */
-local void fixedtables(struct inflate_state FAR *state) {
-#ifdef BUILDFIXED
-    static int virgin = 1;
-    static code *lenfix, *distfix;
-    static code fixed[544];
-
-    /* build fixed huffman tables if first call (may not be thread safe) */
-    if (virgin) {
-        unsigned sym, bits;
-        static code *next;
-
-        /* literal/length table */
-        sym = 0;
-        while (sym < 144) state->lens[sym++] = 8;
-        while (sym < 256) state->lens[sym++] = 9;
-        while (sym < 280) state->lens[sym++] = 7;
-        while (sym < 288) state->lens[sym++] = 8;
-        next = fixed;
-        lenfix = next;
-        bits = 9;
-        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
-
-        /* distance table */
-        sym = 0;
-        while (sym < 32) state->lens[sym++] = 5;
-        distfix = next;
-        bits = 5;
-        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
-
-        /* do this just once */
-        virgin = 0;
-    }
-#else /* !BUILDFIXED */
-#   include "inffixed.h"
-#endif /* BUILDFIXED */
-    state->lencode = lenfix;
-    state->lenbits = 9;
-    state->distcode = distfix;
-    state->distbits = 5;
-}
-
 /* Macros for inflateBack(): */
 
 /* Load returned state from inflate_fast() */
@@ -317,7 +266,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
                 state->mode = STORED;
                 break;
             case 1:                             /* fixed block */
-                fixedtables(state);
+                inflate_fixed(state);
                 Tracev((stderr, "inflate:     fixed codes block%s\n",
                         state->last ? " (last)" : ""));
                 state->mode = LEN;              /* decode codes */
@@ -327,8 +276,8 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
                         state->last ? " (last)" : ""));
                 state->mode = TABLE;
                 break;
-            case 3:
-                strm->msg = (char *)"invalid block type";
+            default:
+                strm->msg = (z_const char *)"invalid block type";
                 state->mode = BAD;
             }
             DROPBITS(2);
@@ -339,7 +288,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
             BYTEBITS();                         /* go to byte boundary */
             NEEDBITS(32);
             if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
-                strm->msg = (char *)"invalid stored block lengths";
+                strm->msg = (z_const char *)"invalid stored block lengths";
                 state->mode = BAD;
                 break;
             }
@@ -377,7 +326,8 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
             DROPBITS(4);
 #ifndef PKZIP_BUG_WORKAROUND
             if (state->nlen > 286 || state->ndist > 30) {
-                strm->msg = (char *)"too many length or distance symbols";
+                strm->msg = (z_const char *)
+                    "too many length or distance symbols";
                 state->mode = BAD;
                 break;
             }
@@ -399,7 +349,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
             ret = inflate_table(CODES, state->lens, 19, &(state->next),
                                 &(state->lenbits), state->work);
             if (ret) {
-                strm->msg = (char *)"invalid code lengths set";
+                strm->msg = (z_const char *)"invalid code lengths set";
                 state->mode = BAD;
                 break;
             }
@@ -422,7 +372,8 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
                         NEEDBITS(here.bits + 2);
                         DROPBITS(here.bits);
                         if (state->have == 0) {
-                            strm->msg = (char *)"invalid bit length repeat";
+                            strm->msg = (z_const char *)
+                                "invalid bit length repeat";
                             state->mode = BAD;
                             break;
                         }
@@ -445,7 +396,8 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
                         DROPBITS(7);
                     }
                     if (state->have + copy > state->nlen + state->ndist) {
-                        strm->msg = (char *)"invalid bit length repeat";
+                        strm->msg = (z_const char *)
+                            "invalid bit length repeat";
                         state->mode = BAD;
                         break;
                     }
@@ -459,7 +411,8 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
 
             /* check for end-of-block code (better have one) */
             if (state->lens[256] == 0) {
-                strm->msg = (char *)"invalid code -- missing end-of-block";
+                strm->msg = (z_const char *)
+                    "invalid code -- missing end-of-block";
                 state->mode = BAD;
                 break;
             }
@@ -473,7 +426,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
             ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
                                 &(state->lenbits), state->work);
             if (ret) {
-                strm->msg = (char *)"invalid literal/lengths set";
+                strm->msg = (z_const char *)"invalid literal/lengths set";
                 state->mode = BAD;
                 break;
             }
@@ -482,7 +435,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
             ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
                             &(state->next), &(state->distbits), state->work);
             if (ret) {
-                strm->msg = (char *)"invalid distances set";
+                strm->msg = (z_const char *)"invalid distances set";
                 state->mode = BAD;
                 break;
             }
@@ -541,7 +494,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
 
             /* invalid code */
             if (here.op & 64) {
-                strm->msg = (char *)"invalid literal/length code";
+                strm->msg = (z_const char *)"invalid literal/length code";
                 state->mode = BAD;
                 break;
             }
@@ -573,7 +526,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
             }
             DROPBITS(here.bits);
             if (here.op & 64) {
-                strm->msg = (char *)"invalid distance code";
+                strm->msg = (z_const char *)"invalid distance code";
                 state->mode = BAD;
                 break;
             }
@@ -588,7 +541,7 @@ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
             }
             if (state->offset > state->wsize - (state->whave < state->wsize ?
                                                 left : 0)) {
-                strm->msg = (char *)"invalid distance too far back";
+                strm->msg = (z_const char *)"invalid distance too far back";
                 state->mode = BAD;
                 break;
             }
diff --git a/src/java.base/share/native/libzip/zlib/inffast.c b/src/java.base/share/native/libzip/zlib/inffast.c
index e86dd78d801..1ce89512ec4 100644
--- a/src/java.base/share/native/libzip/zlib/inffast.c
+++ b/src/java.base/share/native/libzip/zlib/inffast.c
@@ -23,7 +23,7 @@
  */
 
 /* inffast.c -- fast decoding
- * Copyright (C) 1995-2017 Mark Adler
+ * Copyright (C) 1995-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -179,7 +179,8 @@ void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) {
                 dist += (unsigned)hold & ((1U << op) - 1);
 #ifdef INFLATE_STRICT
                 if (dist > dmax) {
-                    strm->msg = (char *)"invalid distance too far back";
+                    strm->msg = (z_const char *)
+                        "invalid distance too far back";
                     state->mode = BAD;
                     break;
                 }
@@ -192,8 +193,8 @@ void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) {
                     op = dist - op;             /* distance back in window */
                     if (op > whave) {
                         if (state->sane) {
-                            strm->msg =
-                                (char *)"invalid distance too far back";
+                            strm->msg = (z_const char *)
+                                "invalid distance too far back";
                             state->mode = BAD;
                             break;
                         }
@@ -289,7 +290,7 @@ void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) {
                 goto dodist;
             }
             else {
-                strm->msg = (char *)"invalid distance code";
+                strm->msg = (z_const char *)"invalid distance code";
                 state->mode = BAD;
                 break;
             }
@@ -304,7 +305,7 @@ void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) {
             break;
         }
         else {
-            strm->msg = (char *)"invalid literal/length code";
+            strm->msg = (z_const char *)"invalid literal/length code";
             state->mode = BAD;
             break;
         }
diff --git a/src/java.base/share/native/libzip/zlib/inffixed.h b/src/java.base/share/native/libzip/zlib/inffixed.h
index f0a4ef1c4e8..d234b018c87 100644
--- a/src/java.base/share/native/libzip/zlib/inffixed.h
+++ b/src/java.base/share/native/libzip/zlib/inffixed.h
@@ -22,97 +22,97 @@
  * questions.
  */
 
-    /* inffixed.h -- table for decoding fixed codes
-     * Generated automatically by makefixed().
-     */
+/* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by makefixed().
+ */
 
-    /* WARNING: this file should *not* be used by applications.
-       It is part of the implementation of this library and is
-       subject to change. Applications should only use zlib.h.
-     */
+/* WARNING: this file should *not* be used by applications.
+   It is part of the implementation of this library and is
+   subject to change. Applications should only use zlib.h.
+ */
 
-    static const code lenfix[512] = {
-        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
-        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
-        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
-        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
-        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
-        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
-        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
-        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
-        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
-        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
-        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
-        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
-        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
-        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
-        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
-        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
-        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
-        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
-        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
-        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
-        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
-        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
-        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
-        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
-        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
-        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
-        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
-        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
-        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
-        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
-        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
-        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
-        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
-        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
-        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
-        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
-        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
-        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
-        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
-        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
-        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
-        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
-        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
-        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
-        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
-        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
-        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
-        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
-        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
-        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
-        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
-        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
-        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
-        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
-        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
-        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
-        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
-        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
-        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
-        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
-        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
-        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
-        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
-        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
-        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
-        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
-        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
-        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
-        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
-        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
-        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
-        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
-        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
-        {0,9,255}
-    };
+static const code lenfix[512] = {
+    {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+    {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+    {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+    {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+    {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+    {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+    {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+    {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+    {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+    {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+    {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+    {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+    {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+    {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+    {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+    {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+    {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+    {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+    {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+    {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+    {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+    {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+    {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+    {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+    {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+    {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+    {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+    {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+    {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+    {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+    {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+    {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+    {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+    {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+    {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+    {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+    {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+    {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+    {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+    {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+    {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+    {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+    {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+    {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+    {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+    {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+    {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+    {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+    {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+    {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+    {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+    {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+    {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+    {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+    {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+    {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+    {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+    {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+    {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+    {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+    {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+    {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+    {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+    {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+    {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+    {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+    {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+    {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+    {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+    {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+    {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+    {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+    {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+    {0,9,255}
+};
 
-    static const code distfix[32] = {
-        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
-        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
-        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
-        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
-        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
-        {22,5,193},{64,5,0}
-    };
+static const code distfix[32] = {
+    {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+    {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+    {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+    {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+    {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+    {22,5,193},{64,5,0}
+};
diff --git a/src/java.base/share/native/libzip/zlib/inflate.c b/src/java.base/share/native/libzip/zlib/inflate.c
index 3370cfe9565..c548f98f6de 100644
--- a/src/java.base/share/native/libzip/zlib/inflate.c
+++ b/src/java.base/share/native/libzip/zlib/inflate.c
@@ -23,7 +23,7 @@
  */
 
 /* inflate.c -- zlib decompression
- * Copyright (C) 1995-2022 Mark Adler
+ * Copyright (C) 1995-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -109,12 +109,6 @@
 #include "inflate.h"
 #include "inffast.h"
 
-#ifdef MAKEFIXED
-#  ifndef BUILDFIXED
-#    define BUILDFIXED
-#  endif
-#endif
-
 local int inflateStateCheck(z_streamp strm) {
     struct inflate_state FAR *state;
     if (strm == Z_NULL ||
@@ -134,6 +128,7 @@ int ZEXPORT inflateResetKeep(z_streamp strm) {
     state = (struct inflate_state FAR *)strm->state;
     strm->total_in = strm->total_out = state->total = 0;
     strm->msg = Z_NULL;
+    strm->data_type = 0;
     if (state->wrap)        /* to support ill-conceived Java test suite */
         strm->adler = state->wrap & 1;
     state->mode = HEAD;
@@ -226,6 +221,7 @@ int ZEXPORT inflateInit2_(z_streamp strm, int windowBits,
     state = (struct inflate_state FAR *)
             ZALLOC(strm, 1, sizeof(struct inflate_state));
     if (state == Z_NULL) return Z_MEM_ERROR;
+    zmemzero(state, sizeof(struct inflate_state));
     Tracev((stderr, "inflate: allocated\n"));
     strm->state = (struct internal_state FAR *)state;
     state->strm = strm;
@@ -258,123 +254,11 @@ int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) {
     }
     if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR;
     value &= (1L << bits) - 1;
-    state->hold += (unsigned)value << state->bits;
+    state->hold += (unsigned long)value << state->bits;
     state->bits += (uInt)bits;
     return Z_OK;
 }
 
-/*
-   Return state with length and distance decoding tables and index sizes set to
-   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
-   If BUILDFIXED is defined, then instead this routine builds the tables the
-   first time it's called, and returns those tables the first time and
-   thereafter.  This reduces the size of the code by about 2K bytes, in
-   exchange for a little execution time.  However, BUILDFIXED should not be
-   used for threaded applications, since the rewriting of the tables and virgin
-   may not be thread-safe.
- */
-local void fixedtables(struct inflate_state FAR *state) {
-#ifdef BUILDFIXED
-    static int virgin = 1;
-    static code *lenfix, *distfix;
-    static code fixed[544];
-
-    /* build fixed huffman tables if first call (may not be thread safe) */
-    if (virgin) {
-        unsigned sym, bits;
-        static code *next;
-
-        /* literal/length table */
-        sym = 0;
-        while (sym < 144) state->lens[sym++] = 8;
-        while (sym < 256) state->lens[sym++] = 9;
-        while (sym < 280) state->lens[sym++] = 7;
-        while (sym < 288) state->lens[sym++] = 8;
-        next = fixed;
-        lenfix = next;
-        bits = 9;
-        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
-
-        /* distance table */
-        sym = 0;
-        while (sym < 32) state->lens[sym++] = 5;
-        distfix = next;
-        bits = 5;
-        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
-
-        /* do this just once */
-        virgin = 0;
-    }
-#else /* !BUILDFIXED */
-#   include "inffixed.h"
-#endif /* BUILDFIXED */
-    state->lencode = lenfix;
-    state->lenbits = 9;
-    state->distcode = distfix;
-    state->distbits = 5;
-}
-
-#ifdef MAKEFIXED
-#include 
-
-/*
-   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also
-   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes
-   those tables to stdout, which would be piped to inffixed.h.  A small program
-   can simply call makefixed to do this:
-
-    void makefixed(void);
-
-    int main(void)
-    {
-        makefixed();
-        return 0;
-    }
-
-   Then that can be linked with zlib built with MAKEFIXED defined and run:
-
-    a.out > inffixed.h
- */
-void makefixed(void)
-{
-    unsigned low, size;
-    struct inflate_state state;
-
-    fixedtables(&state);
-    puts("    /* inffixed.h -- table for decoding fixed codes");
-    puts("     * Generated automatically by makefixed().");
-    puts("     */");
-    puts("");
-    puts("    /* WARNING: this file should *not* be used by applications.");
-    puts("       It is part of the implementation of this library and is");
-    puts("       subject to change. Applications should only use zlib.h.");
-    puts("     */");
-    puts("");
-    size = 1U << 9;
-    printf("    static const code lenfix[%u] = {", size);
-    low = 0;
-    for (;;) {
-        if ((low % 7) == 0) printf("\n        ");
-        printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
-               state.lencode[low].bits, state.lencode[low].val);
-        if (++low == size) break;
-        putchar(',');
-    }
-    puts("\n    };");
-    size = 1U << 5;
-    printf("\n    static const code distfix[%u] = {", size);
-    low = 0;
-    for (;;) {
-        if ((low % 6) == 0) printf("\n        ");
-        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
-               state.distcode[low].val);
-        if (++low == size) break;
-        putchar(',');
-    }
-    puts("\n    };");
-}
-#endif /* MAKEFIXED */
-
 /*
    Update the window with the last wsize (normally 32K) bytes written before
    returning.  If window does not exist yet, create it.  This is only called
@@ -666,12 +550,12 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             if (
 #endif
                 ((BITS(8) << 8) + (hold >> 8)) % 31) {
-                strm->msg = (char *)"incorrect header check";
+                strm->msg = (z_const char *)"incorrect header check";
                 state->mode = BAD;
                 break;
             }
             if (BITS(4) != Z_DEFLATED) {
-                strm->msg = (char *)"unknown compression method";
+                strm->msg = (z_const char *)"unknown compression method";
                 state->mode = BAD;
                 break;
             }
@@ -680,7 +564,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             if (state->wbits == 0)
                 state->wbits = len;
             if (len > 15 || len > state->wbits) {
-                strm->msg = (char *)"invalid window size";
+                strm->msg = (z_const char *)"invalid window size";
                 state->mode = BAD;
                 break;
             }
@@ -696,12 +580,12 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             NEEDBITS(16);
             state->flags = (int)(hold);
             if ((state->flags & 0xff) != Z_DEFLATED) {
-                strm->msg = (char *)"unknown compression method";
+                strm->msg = (z_const char *)"unknown compression method";
                 state->mode = BAD;
                 break;
             }
             if (state->flags & 0xe000) {
-                strm->msg = (char *)"unknown header flags set";
+                strm->msg = (z_const char *)"unknown header flags set";
                 state->mode = BAD;
                 break;
             }
@@ -817,7 +701,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             if (state->flags & 0x0200) {
                 NEEDBITS(16);
                 if ((state->wrap & 4) && hold != (state->check & 0xffff)) {
-                    strm->msg = (char *)"header crc mismatch";
+                    strm->msg = (z_const char *)"header crc mismatch";
                     state->mode = BAD;
                     break;
                 }
@@ -864,7 +748,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
                 state->mode = STORED;
                 break;
             case 1:                             /* fixed block */
-                fixedtables(state);
+                inflate_fixed(state);
                 Tracev((stderr, "inflate:     fixed codes block%s\n",
                         state->last ? " (last)" : ""));
                 state->mode = LEN_;             /* decode codes */
@@ -878,8 +762,8 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
                         state->last ? " (last)" : ""));
                 state->mode = TABLE;
                 break;
-            case 3:
-                strm->msg = (char *)"invalid block type";
+            default:
+                strm->msg = (z_const char *)"invalid block type";
                 state->mode = BAD;
             }
             DROPBITS(2);
@@ -888,7 +772,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             BYTEBITS();                         /* go to byte boundary */
             NEEDBITS(32);
             if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
-                strm->msg = (char *)"invalid stored block lengths";
+                strm->msg = (z_const char *)"invalid stored block lengths";
                 state->mode = BAD;
                 break;
             }
@@ -929,7 +813,8 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             DROPBITS(4);
 #ifndef PKZIP_BUG_WORKAROUND
             if (state->nlen > 286 || state->ndist > 30) {
-                strm->msg = (char *)"too many length or distance symbols";
+                strm->msg = (z_const char *)
+                    "too many length or distance symbols";
                 state->mode = BAD;
                 break;
             }
@@ -947,12 +832,12 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             while (state->have < 19)
                 state->lens[order[state->have++]] = 0;
             state->next = state->codes;
-            state->lencode = (const code FAR *)(state->next);
+            state->lencode = state->distcode = (const code FAR *)(state->next);
             state->lenbits = 7;
             ret = inflate_table(CODES, state->lens, 19, &(state->next),
                                 &(state->lenbits), state->work);
             if (ret) {
-                strm->msg = (char *)"invalid code lengths set";
+                strm->msg = (z_const char *)"invalid code lengths set";
                 state->mode = BAD;
                 break;
             }
@@ -976,7 +861,8 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
                         NEEDBITS(here.bits + 2);
                         DROPBITS(here.bits);
                         if (state->have == 0) {
-                            strm->msg = (char *)"invalid bit length repeat";
+                            strm->msg = (z_const char *)
+                                "invalid bit length repeat";
                             state->mode = BAD;
                             break;
                         }
@@ -999,7 +885,8 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
                         DROPBITS(7);
                     }
                     if (state->have + copy > state->nlen + state->ndist) {
-                        strm->msg = (char *)"invalid bit length repeat";
+                        strm->msg = (z_const char *)
+                            "invalid bit length repeat";
                         state->mode = BAD;
                         break;
                     }
@@ -1013,7 +900,8 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
 
             /* check for end-of-block code (better have one) */
             if (state->lens[256] == 0) {
-                strm->msg = (char *)"invalid code -- missing end-of-block";
+                strm->msg = (z_const char *)
+                    "invalid code -- missing end-of-block";
                 state->mode = BAD;
                 break;
             }
@@ -1027,7 +915,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
                                 &(state->lenbits), state->work);
             if (ret) {
-                strm->msg = (char *)"invalid literal/lengths set";
+                strm->msg = (z_const char *)"invalid literal/lengths set";
                 state->mode = BAD;
                 break;
             }
@@ -1036,7 +924,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
                             &(state->next), &(state->distbits), state->work);
             if (ret) {
-                strm->msg = (char *)"invalid distances set";
+                strm->msg = (z_const char *)"invalid distances set";
                 state->mode = BAD;
                 break;
             }
@@ -1090,7 +978,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
                 break;
             }
             if (here.op & 64) {
-                strm->msg = (char *)"invalid literal/length code";
+                strm->msg = (z_const char *)"invalid literal/length code";
                 state->mode = BAD;
                 break;
             }
@@ -1128,7 +1016,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             DROPBITS(here.bits);
             state->back += here.bits;
             if (here.op & 64) {
-                strm->msg = (char *)"invalid distance code";
+                strm->msg = (z_const char *)"invalid distance code";
                 state->mode = BAD;
                 break;
             }
@@ -1145,7 +1033,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             }
 #ifdef INFLATE_STRICT
             if (state->offset > state->dmax) {
-                strm->msg = (char *)"invalid distance too far back";
+                strm->msg = (z_const char *)"invalid distance too far back";
                 state->mode = BAD;
                 break;
             }
@@ -1160,7 +1048,8 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
                 copy = state->offset - copy;
                 if (copy > state->whave) {
                     if (state->sane) {
-                        strm->msg = (char *)"invalid distance too far back";
+                        strm->msg = (z_const char *)
+                            "invalid distance too far back";
                         state->mode = BAD;
                         break;
                     }
@@ -1219,7 +1108,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
                      state->flags ? hold :
 #endif
                      ZSWAP32(hold)) != state->check) {
-                    strm->msg = (char *)"incorrect data check";
+                    strm->msg = (z_const char *)"incorrect data check";
                     state->mode = BAD;
                     break;
                 }
@@ -1233,7 +1122,7 @@ int ZEXPORT inflate(z_streamp strm, int flush) {
             if (state->wrap && state->flags) {
                 NEEDBITS(32);
                 if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) {
-                    strm->msg = (char *)"incorrect length check";
+                    strm->msg = (z_const char *)"incorrect length check";
                     state->mode = BAD;
                     break;
                 }
@@ -1464,7 +1353,6 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) {
     struct inflate_state FAR *state;
     struct inflate_state FAR *copy;
     unsigned char FAR *window;
-    unsigned wsize;
 
     /* check input */
     if (inflateStateCheck(source) || dest == Z_NULL)
@@ -1475,6 +1363,7 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) {
     copy = (struct inflate_state FAR *)
            ZALLOC(source, 1, sizeof(struct inflate_state));
     if (copy == Z_NULL) return Z_MEM_ERROR;
+    zmemzero(copy, sizeof(struct inflate_state));
     window = Z_NULL;
     if (state->window != Z_NULL) {
         window = (unsigned char FAR *)
@@ -1486,8 +1375,8 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) {
     }
 
     /* copy state */
-    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
-    zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
+    zmemcpy(dest, source, sizeof(z_stream));
+    zmemcpy(copy, state, sizeof(struct inflate_state));
     copy->strm = dest;
     if (state->lencode >= state->codes &&
         state->lencode <= state->codes + ENOUGH - 1) {
@@ -1495,10 +1384,8 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) {
         copy->distcode = copy->codes + (state->distcode - state->codes);
     }
     copy->next = copy->codes + (state->next - state->codes);
-    if (window != Z_NULL) {
-        wsize = 1U << state->wbits;
-        zmemcpy(window, state->window, wsize);
-    }
+    if (window != Z_NULL)
+        zmemcpy(window, state->window, state->whave);
     copy->window = window;
     dest->state = (struct internal_state FAR *)copy;
     return Z_OK;
diff --git a/src/java.base/share/native/libzip/zlib/inflate.h b/src/java.base/share/native/libzip/zlib/inflate.h
index 2cc56dd7fd4..7828fb5db38 100644
--- a/src/java.base/share/native/libzip/zlib/inflate.h
+++ b/src/java.base/share/native/libzip/zlib/inflate.h
@@ -124,7 +124,7 @@ struct inflate_state {
     unsigned char FAR *window;  /* allocated sliding window, if needed */
         /* bit accumulator */
     unsigned long hold;         /* input bit accumulator */
-    unsigned bits;              /* number of bits in "in" */
+    unsigned bits;              /* number of bits in hold */
         /* for string and stored block copying */
     unsigned length;            /* literal or length of data to copy */
     unsigned offset;            /* distance back to copy string from */
diff --git a/src/java.base/share/native/libzip/zlib/inftrees.c b/src/java.base/share/native/libzip/zlib/inftrees.c
index c4913bc4359..5f5d6b4baef 100644
--- a/src/java.base/share/native/libzip/zlib/inftrees.c
+++ b/src/java.base/share/native/libzip/zlib/inftrees.c
@@ -23,17 +23,31 @@
  */
 
 /* inftrees.c -- generate Huffman trees for efficient decoding
- * Copyright (C) 1995-2024 Mark Adler
+ * Copyright (C) 1995-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
+#ifdef MAKEFIXED
+#  ifndef BUILDFIXED
+#    define BUILDFIXED
+#  endif
+#endif
+#ifdef BUILDFIXED
+#  define Z_ONCE
+#endif
+
 #include "zutil.h"
 #include "inftrees.h"
+#include "inflate.h"
+
+#ifndef NULL
+#  define NULL 0
+#endif
 
 #define MAXBITS 15
 
 const char inflate_copyright[] =
-   " inflate 1.3.1 Copyright 1995-2024 Mark Adler ";
+   " inflate 1.3.2 Copyright 1995-2026 Mark Adler ";
 /*
   If you use the zlib library in a product, an acknowledgment is welcome
   in the documentation of your product. If for some reason you cannot
@@ -71,9 +85,9 @@ int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
     unsigned mask;              /* mask for low root bits */
     code here;                  /* table entry for duplication */
     code FAR *next;             /* next available space in table */
-    const unsigned short FAR *base;     /* base value table to use */
-    const unsigned short FAR *extra;    /* extra bits table to use */
-    unsigned match;             /* use base and extra for symbol >= match */
+    const unsigned short FAR *base = NULL;  /* base value table to use */
+    const unsigned short FAR *extra = NULL; /* extra bits table to use */
+    unsigned match = 0;         /* use base and extra for symbol >= match */
     unsigned short count[MAXBITS+1];    /* number of codes of each length */
     unsigned short offs[MAXBITS+1];     /* offsets in table for each length */
     static const unsigned short lbase[31] = { /* Length codes 257..285 base */
@@ -81,7 +95,7 @@ int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
         35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
     static const unsigned short lext[31] = { /* Length codes 257..285 extra */
         16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
-        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 77};
+        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 199, 75};
     static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
         1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
         257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
@@ -199,7 +213,6 @@ int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
     /* set up for code type */
     switch (type) {
     case CODES:
-        base = extra = work;    /* dummy value--not used */
         match = 20;
         break;
     case LENS:
@@ -207,10 +220,9 @@ int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
         extra = lext;
         match = 257;
         break;
-    default:    /* DISTS */
+    case DISTS:
         base = dbase;
         extra = dext;
-        match = 0;
     }
 
     /* initialize state for loop */
@@ -321,3 +333,116 @@ int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
     *bits = root;
     return 0;
 }
+
+#ifdef BUILDFIXED
+/*
+  If this is compiled with BUILDFIXED defined, and if inflate will be used in
+  multiple threads, and if atomics are not available, then inflate() must be
+  called with a fixed block (e.g. 0x03 0x00) to initialize the tables and must
+  return before any other threads are allowed to call inflate.
+ */
+
+static code *lenfix, *distfix;
+static code fixed[544];
+
+/* State for z_once(). */
+local z_once_t built = Z_ONCE_INIT;
+
+local void buildtables(void) {
+    unsigned sym, bits;
+    static code *next;
+    unsigned short lens[288], work[288];
+
+    /* literal/length table */
+    sym = 0;
+    while (sym < 144) lens[sym++] = 8;
+    while (sym < 256) lens[sym++] = 9;
+    while (sym < 280) lens[sym++] = 7;
+    while (sym < 288) lens[sym++] = 8;
+    next = fixed;
+    lenfix = next;
+    bits = 9;
+    inflate_table(LENS, lens, 288, &(next), &(bits), work);
+
+    /* distance table */
+    sym = 0;
+    while (sym < 32) lens[sym++] = 5;
+    distfix = next;
+    bits = 5;
+    inflate_table(DISTS, lens, 32, &(next), &(bits), work);
+}
+#else /* !BUILDFIXED */
+#  include "inffixed.h"
+#endif /* BUILDFIXED */
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications if atomics are not available, as it will
+   not be thread-safe.
+ */
+void inflate_fixed(struct inflate_state FAR *state) {
+#ifdef BUILDFIXED
+    z_once(&built, buildtables);
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include 
+
+/*
+   Write out the inffixed.h that will be #include'd above.  Defining MAKEFIXED
+   also defines BUILDFIXED, so the tables are built on the fly.  main() writes
+   those tables to stdout, which would directed to inffixed.h. Compile this
+   along with zutil.c:
+
+       cc -DMAKEFIXED -o fix inftrees.c zutil.c
+       ./fix > inffixed.h
+ */
+int main(void) {
+    unsigned low, size;
+    struct inflate_state state;
+
+    inflate_fixed(&state);
+    puts("/* inffixed.h -- table for decoding fixed codes");
+    puts(" * Generated automatically by makefixed().");
+    puts(" */");
+    puts("");
+    puts("/* WARNING: this file should *not* be used by applications.");
+    puts("   It is part of the implementation of this library and is");
+    puts("   subject to change. Applications should only use zlib.h.");
+    puts(" */");
+    puts("");
+    size = 1U << 9;
+    printf("static const code lenfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 7) == 0) printf("\n    ");
+        printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
+               state.lencode[low].bits, state.lencode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n};");
+    size = 1U << 5;
+    printf("\nstatic const code distfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 6) == 0) printf("\n    ");
+        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+               state.distcode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n};");
+    return 0;
+}
+#endif /* MAKEFIXED */
diff --git a/src/java.base/share/native/libzip/zlib/inftrees.h b/src/java.base/share/native/libzip/zlib/inftrees.h
index 3e2e889301d..b9b4fc2fb2b 100644
--- a/src/java.base/share/native/libzip/zlib/inftrees.h
+++ b/src/java.base/share/native/libzip/zlib/inftrees.h
@@ -23,7 +23,7 @@
  */
 
 /* inftrees.h -- header to use inftrees.c
- * Copyright (C) 1995-2005, 2010 Mark Adler
+ * Copyright (C) 1995-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -84,3 +84,5 @@ typedef enum {
 int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
                                 unsigned codes, code FAR * FAR *table,
                                 unsigned FAR *bits, unsigned short FAR *work);
+struct inflate_state;
+void ZLIB_INTERNAL inflate_fixed(struct inflate_state FAR *state);
diff --git a/src/java.base/share/native/libzip/zlib/patches/ChangeLog_java b/src/java.base/share/native/libzip/zlib/patches/ChangeLog_java
index 3296c5f2fad..dfde58f0122 100644
--- a/src/java.base/share/native/libzip/zlib/patches/ChangeLog_java
+++ b/src/java.base/share/native/libzip/zlib/patches/ChangeLog_java
@@ -1,4 +1,4 @@
-Changes from zlib 1.3.1
+Changes in JDK's in-tree zlib compared to upstream zlib 1.3.2
 
 (1) renamed adler32.c -> zadler32.c, crc32c -> zcrc32.c
 
diff --git a/src/java.base/share/native/libzip/zlib/trees.c b/src/java.base/share/native/libzip/zlib/trees.c
index bbfa9deee5b..cc7b136bcf6 100644
--- a/src/java.base/share/native/libzip/zlib/trees.c
+++ b/src/java.base/share/native/libzip/zlib/trees.c
@@ -23,7 +23,7 @@
  */
 
 /* trees.c -- output deflated data using Huffman coding
- * Copyright (C) 1995-2024 Jean-loup Gailly
+ * Copyright (C) 1995-2026 Jean-loup Gailly
  * detect_data_type() function provided freely by Cosmin Truta, 2006
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
@@ -136,7 +136,7 @@ local int base_dist[D_CODES];
 
 #else
 #  include "trees.h"
-#endif /* GEN_TREES_H */
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
 
 struct static_tree_desc_s {
     const ct_data *static_tree;  /* static tree or NULL */
@@ -176,7 +176,7 @@ local TCONST static_tree_desc static_bl_desc =
  * IN assertion: 1 <= len <= 15
  */
 local unsigned bi_reverse(unsigned code, int len) {
-    register unsigned res = 0;
+    unsigned res = 0;
     do {
         res |= code & 1;
         code >>= 1, res <<= 1;
@@ -208,10 +208,11 @@ local void bi_windup(deflate_state *s) {
     } else if (s->bi_valid > 0) {
         put_byte(s, (Byte)s->bi_buf);
     }
+    s->bi_used = ((s->bi_valid - 1) & 7) + 1;
     s->bi_buf = 0;
     s->bi_valid = 0;
 #ifdef ZLIB_DEBUG
-    s->bits_sent = (s->bits_sent + 7) & ~7;
+    s->bits_sent = (s->bits_sent + 7) & ~(ulg)7;
 #endif
 }
 
@@ -490,6 +491,7 @@ void ZLIB_INTERNAL _tr_init(deflate_state *s) {
 
     s->bi_buf = 0;
     s->bi_valid = 0;
+    s->bi_used = 0;
 #ifdef ZLIB_DEBUG
     s->compressed_len = 0L;
     s->bits_sent = 0L;
@@ -748,7 +750,7 @@ local void scan_tree(deflate_state *s, ct_data *tree, int max_code) {
         if (++count < max_count && curlen == nextlen) {
             continue;
         } else if (count < min_count) {
-            s->bl_tree[curlen].Freq += count;
+            s->bl_tree[curlen].Freq += (ush)count;
         } else if (curlen != 0) {
             if (curlen != prevlen) s->bl_tree[curlen].Freq++;
             s->bl_tree[REP_3_6].Freq++;
@@ -841,7 +843,7 @@ local int build_bl_tree(deflate_state *s) {
     }
     /* Update opt_len to include the bit length tree and counts */
     s->opt_len += 3*((ulg)max_blindex + 1) + 5 + 5 + 4;
-    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+    Tracev((stderr, "\ndyn trees: dyn %lu, stat %lu",
             s->opt_len, s->static_len));
 
     return max_blindex;
@@ -867,13 +869,13 @@ local void send_all_trees(deflate_state *s, int lcodes, int dcodes,
         Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
         send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
     }
-    Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+    Tracev((stderr, "\nbl tree: sent %lu", s->bits_sent));
 
     send_tree(s, (ct_data *)s->dyn_ltree, lcodes - 1);  /* literal tree */
-    Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+    Tracev((stderr, "\nlit tree: sent %lu", s->bits_sent));
 
     send_tree(s, (ct_data *)s->dyn_dtree, dcodes - 1);  /* distance tree */
-    Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+    Tracev((stderr, "\ndist tree: sent %lu", s->bits_sent));
 }
 
 /* ===========================================================================
@@ -956,7 +958,7 @@ local void compress_block(deflate_state *s, const ct_data *ltree,
             extra = extra_dbits[code];
             if (extra != 0) {
                 dist -= (unsigned)base_dist[code];
-                send_bits(s, dist, extra);   /* send the extra distance bits */
+                send_bits(s, (int)dist, extra); /* send the extra bits */
             }
         } /* literal or match pair ? */
 
@@ -1030,11 +1032,11 @@ void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf,
 
         /* Construct the literal and distance trees */
         build_tree(s, (tree_desc *)(&(s->l_desc)));
-        Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+        Tracev((stderr, "\nlit data: dyn %lu, stat %lu", s->opt_len,
                 s->static_len));
 
         build_tree(s, (tree_desc *)(&(s->d_desc)));
-        Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+        Tracev((stderr, "\ndist data: dyn %lu, stat %lu", s->opt_len,
                 s->static_len));
         /* At this point, opt_len and static_len are the total bit lengths of
          * the compressed block data, excluding the tree representations.
@@ -1107,7 +1109,7 @@ void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf,
 #endif
     }
     Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len >> 3,
-           s->compressed_len - 7*last));
+           s->compressed_len - 7*(ulg)last));
 }
 
 /* ===========================================================================
diff --git a/src/java.base/share/native/libzip/zlib/uncompr.c b/src/java.base/share/native/libzip/zlib/uncompr.c
index 219c1d264d5..25da59461c3 100644
--- a/src/java.base/share/native/libzip/zlib/uncompr.c
+++ b/src/java.base/share/native/libzip/zlib/uncompr.c
@@ -23,7 +23,7 @@
  */
 
 /* uncompr.c -- decompress a memory buffer
- * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler
+ * Copyright (C) 1995-2026 Jean-loup Gailly, Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -47,24 +47,24 @@
    memory, Z_BUF_ERROR if there was not enough room in the output buffer, or
    Z_DATA_ERROR if the input data was corrupted, including if the input data is
    an incomplete zlib stream.
+
+     The _z versions of the functions take size_t length arguments.
 */
-int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source,
-                        uLong *sourceLen) {
+int ZEXPORT uncompress2_z(Bytef *dest, z_size_t *destLen, const Bytef *source,
+                          z_size_t *sourceLen) {
     z_stream stream;
     int err;
     const uInt max = (uInt)-1;
-    uLong len, left;
-    Byte buf[1];    /* for detection of incomplete stream when *destLen == 0 */
+    z_size_t len, left;
+
+    if (sourceLen == NULL || (*sourceLen > 0 && source == NULL) ||
+        destLen == NULL || (*destLen > 0 && dest == NULL))
+        return Z_STREAM_ERROR;
 
     len = *sourceLen;
-    if (*destLen) {
-        left = *destLen;
-        *destLen = 0;
-    }
-    else {
-        left = 1;
-        dest = buf;
-    }
+    left = *destLen;
+    if (left == 0 && dest == Z_NULL)
+        dest = (Bytef *)&stream.reserved;       /* next_out cannot be NULL */
 
     stream.next_in = (z_const Bytef *)source;
     stream.avail_in = 0;
@@ -80,30 +80,46 @@ int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source,
 
     do {
         if (stream.avail_out == 0) {
-            stream.avail_out = left > (uLong)max ? max : (uInt)left;
+            stream.avail_out = left > (z_size_t)max ? max : (uInt)left;
             left -= stream.avail_out;
         }
         if (stream.avail_in == 0) {
-            stream.avail_in = len > (uLong)max ? max : (uInt)len;
+            stream.avail_in = len > (z_size_t)max ? max : (uInt)len;
             len -= stream.avail_in;
         }
         err = inflate(&stream, Z_NO_FLUSH);
     } while (err == Z_OK);
 
-    *sourceLen -= len + stream.avail_in;
-    if (dest != buf)
-        *destLen = stream.total_out;
-    else if (stream.total_out && err == Z_BUF_ERROR)
-        left = 1;
+    /* Set len and left to the unused input data and unused output space. Set
+       *sourceLen to the amount of input consumed. Set *destLen to the amount
+       of data produced. */
+    len += stream.avail_in;
+    left += stream.avail_out;
+    *sourceLen -= len;
+    *destLen -= left;
 
     inflateEnd(&stream);
     return err == Z_STREAM_END ? Z_OK :
            err == Z_NEED_DICT ? Z_DATA_ERROR  :
-           err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR :
+           err == Z_BUF_ERROR && len == 0 ? Z_DATA_ERROR :
            err;
 }
-
+int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source,
+                        uLong *sourceLen) {
+    int ret;
+    z_size_t got = *destLen, used = *sourceLen;
+    ret = uncompress2_z(dest, &got, source, &used);
+    *sourceLen = (uLong)used;
+    *destLen = (uLong)got;
+    return ret;
+}
+int ZEXPORT uncompress_z(Bytef *dest, z_size_t *destLen, const Bytef *source,
+                         z_size_t sourceLen) {
+    z_size_t used = sourceLen;
+    return uncompress2_z(dest, destLen, source, &used);
+}
 int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source,
                        uLong sourceLen) {
-    return uncompress2(dest, destLen, source, &sourceLen);
+    uLong used = sourceLen;
+    return uncompress2(dest, destLen, source, &used);
 }
diff --git a/src/java.base/share/native/libzip/zlib/zconf.h b/src/java.base/share/native/libzip/zlib/zconf.h
index 46204222f5d..d4589739a0a 100644
--- a/src/java.base/share/native/libzip/zlib/zconf.h
+++ b/src/java.base/share/native/libzip/zlib/zconf.h
@@ -23,7 +23,7 @@
  */
 
 /* zconf.h -- configuration of the zlib compression library
- * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler
+ * Copyright (C) 1995-2026 Jean-loup Gailly, Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -57,7 +57,10 @@
 #  ifndef Z_SOLO
 #    define compress              z_compress
 #    define compress2             z_compress2
+#    define compress_z            z_compress_z
+#    define compress2_z           z_compress2_z
 #    define compressBound         z_compressBound
+#    define compressBound_z       z_compressBound_z
 #  endif
 #  define crc32                 z_crc32
 #  define crc32_combine         z_crc32_combine
@@ -68,6 +71,7 @@
 #  define crc32_z               z_crc32_z
 #  define deflate               z_deflate
 #  define deflateBound          z_deflateBound
+#  define deflateBound_z        z_deflateBound_z
 #  define deflateCopy           z_deflateCopy
 #  define deflateEnd            z_deflateEnd
 #  define deflateGetDictionary  z_deflateGetDictionary
@@ -83,6 +87,7 @@
 #  define deflateSetDictionary  z_deflateSetDictionary
 #  define deflateSetHeader      z_deflateSetHeader
 #  define deflateTune           z_deflateTune
+#  define deflateUsed           z_deflateUsed
 #  define deflate_copyright     z_deflate_copyright
 #  define get_crc_table         z_get_crc_table
 #  ifndef Z_SOLO
@@ -152,9 +157,12 @@
 #  define inflate_copyright     z_inflate_copyright
 #  define inflate_fast          z_inflate_fast
 #  define inflate_table         z_inflate_table
+#  define inflate_fixed         z_inflate_fixed
 #  ifndef Z_SOLO
 #    define uncompress            z_uncompress
 #    define uncompress2           z_uncompress2
+#    define uncompress_z          z_uncompress_z
+#    define uncompress2_z         z_uncompress2_z
 #  endif
 #  define zError                z_zError
 #  ifndef Z_SOLO
@@ -258,10 +266,12 @@
 #  endif
 #endif
 
-#if defined(ZLIB_CONST) && !defined(z_const)
-#  define z_const const
-#else
-#  define z_const
+#ifndef z_const
+#  ifdef ZLIB_CONST
+#    define z_const const
+#  else
+#    define z_const
+#  endif
 #endif
 
 #ifdef Z_SOLO
@@ -457,11 +467,11 @@ typedef uLong FAR uLongf;
    typedef unsigned long z_crc_t;
 #endif
 
-#ifdef HAVE_UNISTD_H    /* may be set to #if 1 by ./configure */
+#if HAVE_UNISTD_H-0     /* may be set to #if 1 by ./configure */
 #  define Z_HAVE_UNISTD_H
 #endif
 
-#ifdef HAVE_STDARG_H    /* may be set to #if 1 by ./configure */
+#if HAVE_STDARG_H-0     /* may be set to #if 1 by ./configure */
 #  define Z_HAVE_STDARG_H
 #endif
 
@@ -494,12 +504,8 @@ typedef uLong FAR uLongf;
 #endif
 
 #ifndef Z_HAVE_UNISTD_H
-#  ifdef __WATCOMC__
-#    define Z_HAVE_UNISTD_H
-#  endif
-#endif
-#ifndef Z_HAVE_UNISTD_H
-#  if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32)
+#  if defined(__WATCOMC__) || defined(__GO32__) || \
+      (defined(_LARGEFILE64_SOURCE) && !defined(_WIN32))
 #    define Z_HAVE_UNISTD_H
 #  endif
 #endif
@@ -534,17 +540,19 @@ typedef uLong FAR uLongf;
 #endif
 
 #ifndef z_off_t
-#  define z_off_t long
+#  define z_off_t long long
 #endif
 
 #if !defined(_WIN32) && defined(Z_LARGE64)
 #  define z_off64_t off64_t
+#elif defined(__MINGW32__)
+#  define z_off64_t long long
+#elif defined(_WIN32) && !defined(__GNUC__)
+#  define z_off64_t __int64
+#elif defined(__GO32__)
+#  define z_off64_t offset_t
 #else
-#  if defined(_WIN32) && !defined(__GNUC__)
-#    define z_off64_t __int64
-#  else
-#    define z_off64_t z_off_t
-#  endif
+#  define z_off64_t z_off_t
 #endif
 
 /* MVS linker does not support external names larger than 8 bytes */
diff --git a/src/java.base/share/native/libzip/zlib/zcrc32.c b/src/java.base/share/native/libzip/zlib/zcrc32.c
index 3f918f76b7c..133cbe158aa 100644
--- a/src/java.base/share/native/libzip/zlib/zcrc32.c
+++ b/src/java.base/share/native/libzip/zlib/zcrc32.c
@@ -23,7 +23,7 @@
  */
 
 /* crc32.c -- compute the CRC-32 of a data stream
- * Copyright (C) 1995-2022 Mark Adler
+ * Copyright (C) 1995-2026 Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  *
  * This interleaved implementation of a CRC makes use of pipelined multiple
@@ -48,11 +48,18 @@
 #  include 
 #  ifndef DYNAMIC_CRC_TABLE
 #    define DYNAMIC_CRC_TABLE
-#  endif /* !DYNAMIC_CRC_TABLE */
-#endif /* MAKECRCH */
+#  endif
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+#  define Z_ONCE
+#endif
 
 #include "zutil.h"      /* for Z_U4, Z_U8, z_crc_t, and FAR definitions */
 
+#ifdef HAVE_S390X_VX
+#  include "contrib/crc32vx/crc32_vx_hooks.h"
+#endif
+
  /*
   A CRC of a message is computed on N braids of words in the message, where
   each word consists of W bytes (4 or 8). If N is 3, for example, then three
@@ -123,7 +130,8 @@
 #endif
 
 /* If available, use the ARM processor CRC32 instruction. */
-#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8
+#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && \
+    defined(W) && W == 8
 #  define ARMCRC32
 #endif
 
@@ -176,10 +184,10 @@ local z_word_t byte_swap(z_word_t word) {
   Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial,
   reflected. For speed, this requires that a not be zero.
  */
-local z_crc_t multmodp(z_crc_t a, z_crc_t b) {
-    z_crc_t m, p;
+local uLong multmodp(uLong a, uLong b) {
+    uLong m, p;
 
-    m = (z_crc_t)1 << 31;
+    m = (uLong)1 << 31;
     p = 0;
     for (;;) {
         if (a & m) {
@@ -195,12 +203,12 @@ local z_crc_t multmodp(z_crc_t a, z_crc_t b) {
 
 /*
   Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been
-  initialized.
+  initialized. n must not be negative.
  */
-local z_crc_t x2nmodp(z_off64_t n, unsigned k) {
-    z_crc_t p;
+local uLong x2nmodp(z_off64_t n, unsigned k) {
+    uLong p;
 
-    p = (z_crc_t)1 << 31;           /* x^0 == 1 */
+    p = (uLong)1 << 31;             /* x^0 == 1 */
     while (n) {
         if (n & 1)
             p = multmodp(x2n_table[k & 31], p);
@@ -228,83 +236,8 @@ local z_crc_t FAR crc_table[256];
    local void write_table64(FILE *, const z_word_t FAR *, int);
 #endif /* MAKECRCH */
 
-/*
-  Define a once() function depending on the availability of atomics. If this is
-  compiled with DYNAMIC_CRC_TABLE defined, and if CRCs will be computed in
-  multiple threads, and if atomics are not available, then get_crc_table() must
-  be called to initialize the tables and must return before any threads are
-  allowed to compute or combine CRCs.
- */
-
-/* Definition of once functionality. */
-typedef struct once_s once_t;
-
-/* Check for the availability of atomics. */
-#if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \
-    !defined(__STDC_NO_ATOMICS__)
-
-#include 
-
-/* Structure for once(), which must be initialized with ONCE_INIT. */
-struct once_s {
-    atomic_flag begun;
-    atomic_int done;
-};
-#define ONCE_INIT {ATOMIC_FLAG_INIT, 0}
-
-/*
-  Run the provided init() function exactly once, even if multiple threads
-  invoke once() at the same time. The state must be a once_t initialized with
-  ONCE_INIT.
- */
-local void once(once_t *state, void (*init)(void)) {
-    if (!atomic_load(&state->done)) {
-        if (atomic_flag_test_and_set(&state->begun))
-            while (!atomic_load(&state->done))
-                ;
-        else {
-            init();
-            atomic_store(&state->done, 1);
-        }
-    }
-}
-
-#else   /* no atomics */
-
-/* Structure for once(), which must be initialized with ONCE_INIT. */
-struct once_s {
-    volatile int begun;
-    volatile int done;
-};
-#define ONCE_INIT {0, 0}
-
-/* Test and set. Alas, not atomic, but tries to minimize the period of
-   vulnerability. */
-local int test_and_set(int volatile *flag) {
-    int was;
-
-    was = *flag;
-    *flag = 1;
-    return was;
-}
-
-/* Run the provided init() function once. This is not thread-safe. */
-local void once(once_t *state, void (*init)(void)) {
-    if (!state->done) {
-        if (test_and_set(&state->begun))
-            while (!state->done)
-                ;
-        else {
-            init();
-            state->done = 1;
-        }
-    }
-}
-
-#endif
-
 /* State for once(). */
-local once_t made = ONCE_INIT;
+local z_once_t made = Z_ONCE_INIT;
 
 /*
   Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
@@ -350,7 +283,7 @@ local void make_crc_table(void) {
     p = (z_crc_t)1 << 30;         /* x^1 */
     x2n_table[0] = p;
     for (n = 1; n < 32; n++)
-        x2n_table[n] = p = multmodp(p, p);
+        x2n_table[n] = p = (z_crc_t)multmodp(p, p);
 
 #ifdef W
     /* initialize the braiding tables -- needs x2n_table[] */
@@ -553,11 +486,11 @@ local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) {
     int k;
     z_crc_t i, p, q;
     for (k = 0; k < w; k++) {
-        p = x2nmodp((n * w + 3 - k) << 3, 0);
+        p = (z_crc_t)x2nmodp((n * w + 3 - k) << 3, 0);
         ltl[k][0] = 0;
         big[w - 1 - k][0] = 0;
         for (i = 1; i < 256; i++) {
-            ltl[k][i] = q = multmodp(i << 24, p);
+            ltl[k][i] = q = (z_crc_t)multmodp(i << 24, p);
             big[w - 1 - k][i] = byte_swap(q);
         }
     }
@@ -572,7 +505,7 @@ local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) {
  */
 const z_crc_t FAR * ZEXPORT get_crc_table(void) {
 #ifdef DYNAMIC_CRC_TABLE
-    once(&made, make_crc_table);
+    z_once(&made, make_crc_table);
 #endif /* DYNAMIC_CRC_TABLE */
     return (const z_crc_t FAR *)crc_table;
 }
@@ -596,9 +529,8 @@ const z_crc_t FAR * ZEXPORT get_crc_table(void) {
 #define Z_BATCH_ZEROS 0xa10d3d0c    /* computed from Z_BATCH = 3990 */
 #define Z_BATCH_MIN 800             /* fewest words in a final batch */
 
-unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf,
-                              z_size_t len) {
-    z_crc_t val;
+uLong ZEXPORT crc32_z(uLong crc, const unsigned char FAR *buf, z_size_t len) {
+    uLong val;
     z_word_t crc1, crc2;
     const z_word_t *word;
     z_word_t val0, val1, val2;
@@ -609,7 +541,7 @@ unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf,
     if (buf == Z_NULL) return 0;
 
 #ifdef DYNAMIC_CRC_TABLE
-    once(&made, make_crc_table);
+    z_once(&made, make_crc_table);
 #endif /* DYNAMIC_CRC_TABLE */
 
     /* Pre-condition the CRC */
@@ -664,7 +596,7 @@ unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf,
         }
         word += 3 * last;
         num -= 3 * last;
-        val = x2nmodp(last, 6);
+        val = x2nmodp((int)last, 6);
         crc = multmodp(val, crc) ^ crc1;
         crc = multmodp(val, crc) ^ crc2;
     }
@@ -715,13 +647,12 @@ local z_word_t crc_word_big(z_word_t data) {
 #endif
 
 /* ========================================================================= */
-unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf,
-                              z_size_t len) {
+uLong ZEXPORT crc32_z(uLong crc, const unsigned char FAR *buf, z_size_t len) {
     /* Return initial CRC, if requested. */
     if (buf == Z_NULL) return 0;
 
 #ifdef DYNAMIC_CRC_TABLE
-    once(&made, make_crc_table);
+    z_once(&made, make_crc_table);
 #endif /* DYNAMIC_CRC_TABLE */
 
     /* Pre-condition the CRC */
@@ -1036,28 +967,19 @@ unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf,
 #endif
 
 /* ========================================================================= */
-unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf,
-                            uInt len) {
+uLong ZEXPORT crc32(uLong crc, const unsigned char FAR *buf, uInt len) {
+    #ifdef HAVE_S390X_VX
+    return crc32_z_hook(crc, buf, len);
+    #endif
     return crc32_z(crc, buf, len);
 }
 
-/* ========================================================================= */
-uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) {
-#ifdef DYNAMIC_CRC_TABLE
-    once(&made, make_crc_table);
-#endif /* DYNAMIC_CRC_TABLE */
-    return multmodp(x2nmodp(len2, 3), crc1) ^ (crc2 & 0xffffffff);
-}
-
-/* ========================================================================= */
-uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) {
-    return crc32_combine64(crc1, crc2, (z_off64_t)len2);
-}
-
 /* ========================================================================= */
 uLong ZEXPORT crc32_combine_gen64(z_off64_t len2) {
+    if (len2 < 0)
+        return 0;
 #ifdef DYNAMIC_CRC_TABLE
-    once(&made, make_crc_table);
+    z_once(&made, make_crc_table);
 #endif /* DYNAMIC_CRC_TABLE */
     return x2nmodp(len2, 3);
 }
@@ -1069,5 +991,17 @@ uLong ZEXPORT crc32_combine_gen(z_off_t len2) {
 
 /* ========================================================================= */
 uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op) {
-    return multmodp(op, crc1) ^ (crc2 & 0xffffffff);
+    if (op == 0)
+        return 0;
+    return multmodp(op, crc1 & 0xffffffff) ^ (crc2 & 0xffffffff);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) {
+    return crc32_combine_op(crc1, crc2, crc32_combine_gen64(len2));
+}
+
+/* ========================================================================= */
+uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) {
+    return crc32_combine64(crc1, crc2, (z_off64_t)len2);
 }
diff --git a/src/java.base/share/native/libzip/zlib/zlib.h b/src/java.base/share/native/libzip/zlib/zlib.h
index 07496b5f981..90230f4f23f 100644
--- a/src/java.base/share/native/libzip/zlib/zlib.h
+++ b/src/java.base/share/native/libzip/zlib/zlib.h
@@ -23,9 +23,9 @@
  */
 
 /* zlib.h -- interface of the 'zlib' general purpose compression library
-  version 1.3.1, January 22nd, 2024
+  version 1.3.2, February 17th, 2026
 
-  Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
+  Copyright (C) 1995-2026 Jean-loup Gailly and Mark Adler
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -48,24 +48,28 @@
 
 
   The data format used by the zlib library is described by RFCs (Request for
-  Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+  Comments) 1950 to 1952 at https://datatracker.ietf.org/doc/html/rfc1950
   (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
 */
 
 #ifndef ZLIB_H
 #define ZLIB_H
 
-#include "zconf.h"
+#ifdef ZLIB_BUILD
+#  include 
+#else
+# include "zconf.h"
+#endif
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#define ZLIB_VERSION "1.3.1"
-#define ZLIB_VERNUM 0x1310
+#define ZLIB_VERSION "1.3.2"
+#define ZLIB_VERNUM 0x1320
 #define ZLIB_VER_MAJOR 1
 #define ZLIB_VER_MINOR 3
-#define ZLIB_VER_REVISION 1
+#define ZLIB_VER_REVISION 2
 #define ZLIB_VER_SUBREVISION 0
 
 /*
@@ -465,7 +469,7 @@ ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush);
 
     The Z_BLOCK option assists in appending to or combining deflate streams.
   To assist in this, on return inflate() always sets strm->data_type to the
-  number of unused bits in the last byte taken from strm->next_in, plus 64 if
+  number of unused bits in the input taken from strm->next_in, plus 64 if
   inflate() is currently decoding the last block in the deflate stream, plus
   128 if inflate() returned immediately after decoding an end-of-block code or
   decoding the complete header up to just before the first byte of the deflate
@@ -611,18 +615,21 @@ ZEXTERN int ZEXPORT deflateInit2(z_streamp strm,
 
      The strategy parameter is used to tune the compression algorithm.  Use the
    value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
-   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
-   string match), or Z_RLE to limit match distances to one (run-length
-   encoding).  Filtered data consists mostly of small values with a somewhat
-   random distribution.  In this case, the compression algorithm is tuned to
-   compress them better.  The effect of Z_FILTERED is to force more Huffman
-   coding and less string matching; it is somewhat intermediate between
-   Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY.  Z_RLE is designed to be almost as
-   fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data.  The
-   strategy parameter only affects the compression ratio but not the
-   correctness of the compressed output even if it is not set appropriately.
-   Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
-   decoder for special applications.
+   filter (or predictor), Z_RLE to limit match distances to one (run-length
+   encoding), or Z_HUFFMAN_ONLY to force Huffman encoding only (no string
+   matching).  Filtered data consists mostly of small values with a somewhat
+   random distribution, as produced by the PNG filters.  In this case, the
+   compression algorithm is tuned to compress them better.  The effect of
+   Z_FILTERED is to force more Huffman coding and less string matching than the
+   default; it is intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY.
+   Z_RLE is almost as fast as Z_HUFFMAN_ONLY, but should give better
+   compression for PNG image data than Huffman only.  The degree of string
+   matching from most to none is: Z_DEFAULT_STRATEGY, Z_FILTERED, Z_RLE, then
+   Z_HUFFMAN_ONLY. The strategy parameter affects the compression ratio but
+   never the correctness of the compressed output, even if it is not set
+   optimally for the given data.  Z_FIXED uses the default string matching, but
+   prevents the use of dynamic Huffman codes, allowing for a simpler decoder
+   for special applications.
 
      deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
    memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
@@ -782,8 +789,8 @@ ZEXTERN int ZEXPORT deflateTune(z_streamp strm,
    returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
  */
 
-ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm,
-                                   uLong sourceLen);
+ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen);
+ZEXTERN z_size_t ZEXPORT deflateBound_z(z_streamp strm, z_size_t sourceLen);
 /*
      deflateBound() returns an upper bound on the compressed size after
    deflation of sourceLen bytes.  It must be called after deflateInit() or
@@ -795,6 +802,9 @@ ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm,
    to return Z_STREAM_END.  Note that it is possible for the compressed size to
    be larger than the value returned by deflateBound() if flush options other
    than Z_FINISH or Z_NO_FLUSH are used.
+
+     delfateBound_z() is the same, but takes and returns a size_t length.  Note
+   that a long is 32 bits on Windows.
 */
 
 ZEXTERN int ZEXPORT deflatePending(z_streamp strm,
@@ -809,6 +819,21 @@ ZEXTERN int ZEXPORT deflatePending(z_streamp strm,
    or bits are Z_NULL, then those values are not set.
 
      deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.  If an int is 16 bits and memLevel is 9, then
+   it is possible for the number of pending bytes to not fit in an unsigned. In
+   that case Z_BUF_ERROR is returned and *pending is set to the maximum value
+   of an unsigned.
+ */
+
+ZEXTERN int ZEXPORT deflateUsed(z_streamp strm,
+                                int *bits);
+/*
+     deflateUsed() returns in *bits the most recent number of deflate bits used
+   in the last byte when flushing to a byte boundary. The result is in 1..8, or
+   0 if there has not yet been a flush. This helps determine the location of
+   the last bit of a deflate stream.
+
+     deflateUsed returns Z_OK if success, or Z_STREAM_ERROR if the source
    stream state was inconsistent.
  */
 
@@ -1011,13 +1036,15 @@ ZEXTERN int ZEXPORT inflatePrime(z_streamp strm,
                                  int bits,
                                  int value);
 /*
-     This function inserts bits in the inflate input stream.  The intent is
-   that this function is used to start inflating at a bit position in the
-   middle of a byte.  The provided bits will be used before any bytes are used
-   from next_in.  This function should only be used with raw inflate, and
-   should be used before the first inflate() call after inflateInit2() or
-   inflateReset().  bits must be less than or equal to 16, and that many of the
-   least significant bits of value will be inserted in the input.
+     This function inserts bits in the inflate input stream.  The intent is to
+   use inflatePrime() to start inflating at a bit position in the middle of a
+   byte.  The provided bits will be used before any bytes are used from
+   next_in.  This function should be used with raw inflate, before the first
+   inflate() call, after inflateInit2() or inflateReset().  It can also be used
+   after an inflate() return indicates the end of a deflate block or header
+   when using Z_BLOCK.  bits must be less than or equal to 16, and that many of
+   the least significant bits of value will be inserted in the input.  The
+   other bits in value can be non-zero, and will be ignored.
 
      If bits is negative, then the input stream bit buffer is emptied.  Then
    inflatePrime() can be called again to put bits in the buffer.  This is used
@@ -1025,7 +1052,15 @@ ZEXTERN int ZEXPORT inflatePrime(z_streamp strm,
    to feeding inflate codes.
 
      inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
-   stream state was inconsistent.
+   stream state was inconsistent, or if bits is out of range.  If inflate was
+   in the middle of processing a header, trailer, or stored block lengths, then
+   it is possible for there to be only eight bits available in the bit buffer.
+   In that case, bits > 8 is considered out of range.  However, when used as
+   outlined above, there will always be 16 bits available in the buffer for
+   insertion.  As noted in its documentation above, inflate records the number
+   of bits in the bit buffer on return in data_type. 32 minus that is the
+   number of bits available for insertion.  inflatePrime does not update
+   data_type with the new number of bits in buffer.
 */
 
 ZEXTERN long ZEXPORT inflateMark(z_streamp strm);
@@ -1071,20 +1106,22 @@ ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm,
 
      The text, time, xflags, and os fields are filled in with the gzip header
    contents.  hcrc is set to true if there is a header CRC.  (The header CRC
-   was valid if done is set to one.) If extra is not Z_NULL, then extra_max
-   contains the maximum number of bytes to write to extra.  Once done is true,
-   extra_len contains the actual extra field length, and extra contains the
-   extra field, or that field truncated if extra_max is less than extra_len.
-   If name is not Z_NULL, then up to name_max characters are written there,
-   terminated with a zero unless the length is greater than name_max.  If
-   comment is not Z_NULL, then up to comm_max characters are written there,
-   terminated with a zero unless the length is greater than comm_max.  When any
-   of extra, name, or comment are not Z_NULL and the respective field is not
-   present in the header, then that field is set to Z_NULL to signal its
-   absence.  This allows the use of deflateSetHeader() with the returned
-   structure to duplicate the header.  However if those fields are set to
-   allocated memory, then the application will need to save those pointers
-   elsewhere so that they can be eventually freed.
+   was valid if done is set to one.)  The extra, name, and comment pointers
+   much each be either Z_NULL or point to space to store that information from
+   the header.  If extra is not Z_NULL, then extra_max contains the maximum
+   number of bytes that can be written to extra.  Once done is true, extra_len
+   contains the actual extra field length, and extra contains the extra field,
+   or that field truncated if extra_max is less than extra_len.  If name is not
+   Z_NULL, then up to name_max characters, including the terminating zero, are
+   written there.  If comment is not Z_NULL, then up to comm_max characters,
+   including the terminating zero, are written there.  The application can tell
+   that the name or comment did not fit in the provided space by the absence of
+   a terminating zero.  If any of extra, name, or comment are not present in
+   the header, then that field's pointer is set to Z_NULL.  This allows the use
+   of deflateSetHeader() with the returned structure to duplicate the header.
+   Note that if those fields initially pointed to allocated memory, then the
+   application will need to save them elsewhere so that they can be eventually
+   freed.
 
      If inflateGetHeader is not used, then the header information is simply
    discarded.  The header is always checked for validity, including the header
@@ -1232,13 +1269,14 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags(void);
      21: FASTEST -- deflate algorithm with only one, lowest compression level
      22,23: 0 (reserved)
 
-    The sprintf variant used by gzprintf (zero is best):
+    The sprintf variant used by gzprintf (all zeros is best):
      24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
-     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() is not secure!
      26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+     27: 0 = gzprintf() present, 1 = not -- 1 means gzprintf() returns an error
 
     Remainder:
-     27-31: 0 (reserved)
+     28-31: 0 (reserved)
  */
 
 #ifndef Z_SOLO
@@ -1250,11 +1288,14 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags(void);
    stream-oriented functions.  To simplify the interface, some default options
    are assumed (compression level and memory usage, standard memory allocation
    functions).  The source code of these utility functions can be modified if
-   you need special options.
+   you need special options.  The _z versions of the functions use the size_t
+   type for lengths.  Note that a long is 32 bits on Windows.
 */
 
-ZEXTERN int ZEXPORT compress(Bytef *dest,   uLongf *destLen,
+ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen,
                              const Bytef *source, uLong sourceLen);
+ZEXTERN int ZEXPORT compress_z(Bytef *dest, z_size_t *destLen,
+                               const Bytef *source, z_size_t sourceLen);
 /*
      Compresses the source buffer into the destination buffer.  sourceLen is
    the byte length of the source buffer.  Upon entry, destLen is the total size
@@ -1268,9 +1309,12 @@ ZEXTERN int ZEXPORT compress(Bytef *dest,   uLongf *destLen,
    buffer.
 */
 
-ZEXTERN int ZEXPORT compress2(Bytef *dest,   uLongf *destLen,
+ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen,
                               const Bytef *source, uLong sourceLen,
                               int level);
+ZEXTERN int ZEXPORT compress2_z(Bytef *dest, z_size_t *destLen,
+                                const Bytef *source, z_size_t sourceLen,
+                                int level);
 /*
      Compresses the source buffer into the destination buffer.  The level
    parameter has the same meaning as in deflateInit.  sourceLen is the byte
@@ -1285,21 +1329,24 @@ ZEXTERN int ZEXPORT compress2(Bytef *dest,   uLongf *destLen,
 */
 
 ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen);
+ZEXTERN z_size_t ZEXPORT compressBound_z(z_size_t sourceLen);
 /*
      compressBound() returns an upper bound on the compressed size after
    compress() or compress2() on sourceLen bytes.  It would be used before a
    compress() or compress2() call to allocate the destination buffer.
 */
 
-ZEXTERN int ZEXPORT uncompress(Bytef *dest,   uLongf *destLen,
+ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen,
                                const Bytef *source, uLong sourceLen);
+ZEXTERN int ZEXPORT uncompress_z(Bytef *dest, z_size_t *destLen,
+                                 const Bytef *source, z_size_t sourceLen);
 /*
      Decompresses the source buffer into the destination buffer.  sourceLen is
-   the byte length of the source buffer.  Upon entry, destLen is the total size
+   the byte length of the source buffer.  On entry, *destLen is the total size
    of the destination buffer, which must be large enough to hold the entire
    uncompressed data.  (The size of the uncompressed data must have been saved
    previously by the compressor and transmitted to the decompressor by some
-   mechanism outside the scope of this compression library.) Upon exit, destLen
+   mechanism outside the scope of this compression library.)  On exit, *destLen
    is the actual size of the uncompressed data.
 
      uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
@@ -1309,8 +1356,10 @@ ZEXTERN int ZEXPORT uncompress(Bytef *dest,   uLongf *destLen,
    buffer with the uncompressed data up to that point.
 */
 
-ZEXTERN int ZEXPORT uncompress2(Bytef *dest,   uLongf *destLen,
+ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen,
                                 const Bytef *source, uLong *sourceLen);
+ZEXTERN int ZEXPORT uncompress2_z(Bytef *dest, z_size_t *destLen,
+                                  const Bytef *source, z_size_t *sourceLen);
 /*
      Same as uncompress, except that sourceLen is a pointer, where the
    length of the source is *sourceLen.  On return, *sourceLen is the number of
@@ -1338,13 +1387,17 @@ ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode);
    'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression
    as in "wb9F".  (See the description of deflateInit2 for more information
    about the strategy parameter.)  'T' will request transparent writing or
-   appending with no compression and not using the gzip format.
+   appending with no compression and not using the gzip format. 'T' cannot be
+   used to force transparent reading. Transparent reading is automatically
+   performed if there is no gzip header at the start. Transparent reading can
+   be disabled with the 'G' option, which will instead return an error if there
+   is no gzip header. 'N' will open the file in non-blocking mode.
 
-     "a" can be used instead of "w" to request that the gzip stream that will
-   be written be appended to the file.  "+" will result in an error, since
+     'a' can be used instead of 'w' to request that the gzip stream that will
+   be written be appended to the file.  '+' will result in an error, since
    reading and writing to the same gzip file is not supported.  The addition of
-   "x" when writing will create the file exclusively, which fails if the file
-   already exists.  On systems that support it, the addition of "e" when
+   'x' when writing will create the file exclusively, which fails if the file
+   already exists.  On systems that support it, the addition of 'e' when
    reading or writing will set the flag to close the file on an execve() call.
 
      These functions, as well as gzip, will read and decode a sequence of gzip
@@ -1363,14 +1416,22 @@ ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode);
    insufficient memory to allocate the gzFile state, or if an invalid mode was
    specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
    errno can be checked to determine if the reason gzopen failed was that the
-   file could not be opened.
+   file could not be opened. Note that if 'N' is in mode for non-blocking, the
+   open() itself can fail in order to not block. In that case gzopen() will
+   return NULL and errno will be EAGAIN or ENONBLOCK. The call to gzopen() can
+   then be re-tried. If the application would like to block on opening the
+   file, then it can use open() without O_NONBLOCK, and then gzdopen() with the
+   resulting file descriptor and 'N' in the mode, which will set it to non-
+   blocking.
 */
 
 ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode);
 /*
      Associate a gzFile with the file descriptor fd.  File descriptors are
    obtained from calls like open, dup, creat, pipe or fileno (if the file has
-   been previously opened with fopen).  The mode parameter is as in gzopen.
+   been previously opened with fopen).  The mode parameter is as in gzopen. An
+   'e' in mode will set fd's flag to close the file on an execve() call. An 'N'
+   in mode will set fd's non-blocking flag.
 
      The next call of gzclose on the returned gzFile will also close the file
    descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
@@ -1440,10 +1501,16 @@ ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len);
    stream.  Alternatively, gzerror can be used before gzclose to detect this
    case.
 
+     gzread can be used to read a gzip file on a non-blocking device. If the
+   input stalls and there is no uncompressed data to return, then gzread() will
+   return -1, and errno will be EAGAIN or EWOULDBLOCK. gzread() can then be
+   called again.
+
      gzread returns the number of uncompressed bytes actually read, less than
    len for end of file, or -1 for error.  If len is too large to fit in an int,
    then nothing is read, -1 is returned, and the error state is set to
-   Z_STREAM_ERROR.
+   Z_STREAM_ERROR. If some data was read before an error, then that data is
+   returned until exhausted, after which the next call will signal the error.
 */
 
 ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems,
@@ -1467,15 +1534,20 @@ ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems,
    multiple of size, then the final partial item is nevertheless read into buf
    and the end-of-file flag is set.  The length of the partial item read is not
    provided, but could be inferred from the result of gztell().  This behavior
-   is the same as the behavior of fread() implementations in common libraries,
-   but it prevents the direct use of gzfread() to read a concurrently written
-   file, resetting and retrying on end-of-file, when size is not 1.
+   is the same as that of fread() implementations in common libraries. This
+   could result in data loss if used with size != 1 when reading a concurrently
+   written file or a non-blocking file. In that case, use size == 1 or gzread()
+   instead.
 */
 
 ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len);
 /*
      Compress and write the len uncompressed bytes at buf to file. gzwrite
-   returns the number of uncompressed bytes written or 0 in case of error.
+   returns the number of uncompressed bytes written, or 0 in case of error or
+   if len is 0.  If the write destination is non-blocking, then gzwrite() may
+   return a number of bytes written that is not 0 and less than len.
+
+     If len does not fit in an int, then 0 is returned and nothing is written.
 */
 
 ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size,
@@ -1490,9 +1562,18 @@ ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size,
    if there was an error.  If the multiplication of size and nitems overflows,
    i.e. the product does not fit in a z_size_t, then nothing is written, zero
    is returned, and the error state is set to Z_STREAM_ERROR.
+
+     If writing a concurrently read file or a non-blocking file with size != 1,
+   a partial item could be written, with no way of knowing how much of it was
+   not written, resulting in data loss.  In that case, use size == 1 or
+   gzwrite() instead.
 */
 
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
 ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...);
+#else
+ZEXTERN int ZEXPORTVA gzprintf();
+#endif
 /*
      Convert, format, compress, and write the arguments (...) to file under
    control of the string format, as in fprintf.  gzprintf returns the number of
@@ -1500,11 +1581,19 @@ ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...);
    of error.  The number of uncompressed bytes written is limited to 8191, or
    one less than the buffer size given to gzbuffer().  The caller should assure
    that this limit is not exceeded.  If it is exceeded, then gzprintf() will
-   return an error (0) with nothing written.  In this case, there may also be a
-   buffer overflow with unpredictable consequences, which is possible only if
-   zlib was compiled with the insecure functions sprintf() or vsprintf(),
-   because the secure snprintf() or vsnprintf() functions were not available.
-   This can be determined using zlibCompileFlags().
+   return an error (0) with nothing written.
+
+     In that last case, there may also be a buffer overflow with unpredictable
+   consequences, which is possible only if zlib was compiled with the insecure
+   functions sprintf() or vsprintf(), because the secure snprintf() and
+   vsnprintf() functions were not available. That would only be the case for
+   a non-ANSI C compiler. zlib may have been built without gzprintf() because
+   secure functions were not available and having gzprintf() be insecure was
+   not an option, in which case, gzprintf() returns Z_STREAM_ERROR. All of
+   these possibilities can be determined using zlibCompileFlags().
+
+     If a Z_BUF_ERROR is returned, then nothing was written due to a stall on
+   the non-blocking write destination.
 */
 
 ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s);
@@ -1513,6 +1602,11 @@ ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s);
    the terminating null character.
 
      gzputs returns the number of characters written, or -1 in case of error.
+   The number of characters written may be less than the length of the string
+   if the write destination is non-blocking.
+
+     If the length of the string does not fit in an int, then -1 is returned
+   and nothing is written.
 */
 
 ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len);
@@ -1525,8 +1619,13 @@ ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len);
    left untouched.
 
      gzgets returns buf which is a null-terminated string, or it returns NULL
-   for end-of-file or in case of error.  If there was an error, the contents at
-   buf are indeterminate.
+   for end-of-file or in case of error. If some data was read before an error,
+   then that data is returned until exhausted, after which the next call will
+   return NULL to signal the error.
+
+     gzgets can be used on a file being concurrently written, and on a non-
+   blocking device, both as for gzread(). However lines may be broken in the
+   middle, leaving it up to the application to reassemble them as needed.
 */
 
 ZEXTERN int ZEXPORT gzputc(gzFile file, int c);
@@ -1537,11 +1636,19 @@ ZEXTERN int ZEXPORT gzputc(gzFile file, int c);
 
 ZEXTERN int ZEXPORT gzgetc(gzFile file);
 /*
-     Read and decompress one byte from file.  gzgetc returns this byte or -1
-   in case of end of file or error.  This is implemented as a macro for speed.
-   As such, it does not do all of the checking the other functions do.  I.e.
-   it does not check to see if file is NULL, nor whether the structure file
-   points to has been clobbered or not.
+     Read and decompress one byte from file. gzgetc returns this byte or -1 in
+   case of end of file or error. If some data was read before an error, then
+   that data is returned until exhausted, after which the next call will return
+   -1 to signal the error.
+
+     This is implemented as a macro for speed. As such, it does not do all of
+   the checking the other functions do. I.e. it does not check to see if file
+   is NULL, nor whether the structure file points to has been clobbered or not.
+
+     gzgetc can be used to read a gzip file on a non-blocking device. If the
+   input stalls and there is no uncompressed data to return, then gzgetc() will
+   return -1, and errno will be EAGAIN or EWOULDBLOCK. gzread() can then be
+   called again.
 */
 
 ZEXTERN int ZEXPORT gzungetc(int c, gzFile file);
@@ -1554,6 +1661,11 @@ ZEXTERN int ZEXPORT gzungetc(int c, gzFile file);
    output buffer size of pushed characters is allowed.  (See gzbuffer above.)
    The pushed character will be discarded if the stream is repositioned with
    gzseek() or gzrewind().
+
+     gzungetc(-1, file) will force any pending seek to execute. Then gztell()
+   will report the position, even if the requested seek reached end of file.
+   This can be used to determine the number of uncompressed bytes in a gzip
+   file without having to read it into a buffer.
 */
 
 ZEXTERN int ZEXPORT gzflush(gzFile file, int flush);
@@ -1583,7 +1695,8 @@ ZEXTERN z_off_t ZEXPORT gzseek(gzFile file,
      If the file is opened for reading, this function is emulated but can be
    extremely slow.  If the file is opened for writing, only forward seeks are
    supported; gzseek then compresses a sequence of zeroes up to the new
-   starting position.
+   starting position. For reading or writing, any actual seeking is deferred
+   until the next read or write operation, or close operation when writing.
 
      gzseek returns the resulting offset location as measured in bytes from
    the beginning of the uncompressed stream, or -1 in case of error, in
@@ -1591,7 +1704,7 @@ ZEXTERN z_off_t ZEXPORT gzseek(gzFile file,
    would be before the current position.
 */
 
-ZEXTERN int ZEXPORT    gzrewind(gzFile file);
+ZEXTERN int ZEXPORT gzrewind(gzFile file);
 /*
      Rewind file. This function is supported only for reading.
 
@@ -1599,7 +1712,7 @@ ZEXTERN int ZEXPORT    gzrewind(gzFile file);
 */
 
 /*
-ZEXTERN z_off_t ZEXPORT    gztell(gzFile file);
+ZEXTERN z_off_t ZEXPORT gztell(gzFile file);
 
      Return the starting position for the next gzread or gzwrite on file.
    This position represents a number of bytes in the uncompressed data stream,
@@ -1644,8 +1757,11 @@ ZEXTERN int ZEXPORT gzdirect(gzFile file);
 
      If gzdirect() is used immediately after gzopen() or gzdopen() it will
    cause buffers to be allocated to allow reading the file to determine if it
-   is a gzip file.  Therefore if gzbuffer() is used, it should be called before
-   gzdirect().
+   is a gzip file. Therefore if gzbuffer() is used, it should be called before
+   gzdirect(). If the input is being written concurrently or the device is non-
+   blocking, then gzdirect() may give a different answer once four bytes of
+   input have been accumulated, which is what is needed to confirm or deny a
+   gzip header. Before this, gzdirect() will return true (1).
 
      When writing, gzdirect() returns true (1) if transparent writing was
    requested ("wT" for the gzopen() mode), or false (0) otherwise.  (Note:
@@ -1655,7 +1771,7 @@ ZEXTERN int ZEXPORT gzdirect(gzFile file);
    gzip file reading and decompression, which may not be desired.)
 */
 
-ZEXTERN int ZEXPORT    gzclose(gzFile file);
+ZEXTERN int ZEXPORT gzclose(gzFile file);
 /*
      Flush all pending output for file, if necessary, close file and
    deallocate the (de)compression state.  Note that once file is closed, you
@@ -1683,9 +1799,10 @@ ZEXTERN int ZEXPORT gzclose_w(gzFile file);
 ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum);
 /*
      Return the error message for the last error which occurred on file.
-   errnum is set to zlib error number.  If an error occurred in the file system
-   and not in the compression library, errnum is set to Z_ERRNO and the
-   application may consult errno to get the exact error code.
+   If errnum is not NULL, *errnum is set to zlib error number.  If an error
+   occurred in the file system and not in the compression library, *errnum is
+   set to Z_ERRNO and the application may consult errno to get the exact error
+   code.
 
      The application must not modify the returned string.  Future calls to
    this function may invalidate the previously returned string.  If file is
@@ -1736,7 +1853,8 @@ ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len);
 ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf,
                                 z_size_t len);
 /*
-     Same as adler32(), but with a size_t length.
+     Same as adler32(), but with a size_t length.  Note that a long is 32 bits
+   on Windows.
 */
 
 /*
@@ -1772,7 +1890,8 @@ ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len);
 ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf,
                               z_size_t len);
 /*
-     Same as crc32(), but with a size_t length.
+     Same as crc32(), but with a size_t length.  Note that a long is 32 bits on
+   Windows.
 */
 
 /*
@@ -1782,14 +1901,14 @@ ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2);
    seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
    calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
    check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
-   len2. len2 must be non-negative.
+   len2. len2 must be non-negative, otherwise zero is returned.
 */
 
 /*
 ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2);
 
      Return the operator corresponding to length len2, to be used with
-   crc32_combine_op(). len2 must be non-negative.
+   crc32_combine_op(). len2 must be non-negative, otherwise zero is returned.
 */
 
 ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op);
@@ -1912,9 +2031,9 @@ ZEXTERN int ZEXPORT gzgetc_(gzFile file);       /* backward compatibility */
      ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int);
      ZEXTERN z_off_t ZEXPORT gztell64(gzFile);
      ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile);
-     ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t);
-     ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t);
-     ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t);
+     ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t);
+     ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t);
+     ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t);
 #  endif
 #else
    ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *);
diff --git a/src/java.base/share/native/libzip/zlib/zutil.c b/src/java.base/share/native/libzip/zlib/zutil.c
index 92dda78497b..c57c162ffde 100644
--- a/src/java.base/share/native/libzip/zlib/zutil.c
+++ b/src/java.base/share/native/libzip/zlib/zutil.c
@@ -23,7 +23,7 @@
  */
 
 /* zutil.c -- target dependent utility functions for the compression library
- * Copyright (C) 1995-2017 Jean-loup Gailly
+ * Copyright (C) 1995-2026 Jean-loup Gailly
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -110,28 +110,36 @@ uLong ZEXPORT zlibCompileFlags(void) {
     flags += 1L << 21;
 #endif
 #if defined(STDC) || defined(Z_HAVE_STDARG_H)
-#  ifdef NO_vsnprintf
-    flags += 1L << 25;
-#    ifdef HAS_vsprintf_void
-    flags += 1L << 26;
-#    endif
-#  else
-#    ifdef HAS_vsnprintf_void
-    flags += 1L << 26;
-#    endif
-#  endif
+#   ifdef NO_vsnprintf
+#       ifdef ZLIB_INSECURE
+            flags += 1L << 25;
+#       else
+            flags += 1L << 27;
+#       endif
+#       ifdef HAS_vsprintf_void
+            flags += 1L << 26;
+#       endif
+#   else
+#       ifdef HAS_vsnprintf_void
+            flags += 1L << 26;
+#       endif
+#   endif
 #else
     flags += 1L << 24;
-#  ifdef NO_snprintf
-    flags += 1L << 25;
-#    ifdef HAS_sprintf_void
-    flags += 1L << 26;
-#    endif
-#  else
-#    ifdef HAS_snprintf_void
-    flags += 1L << 26;
-#    endif
-#  endif
+#   ifdef NO_snprintf
+#       ifdef ZLIB_INSECURE
+            flags += 1L << 25;
+#       else
+            flags += 1L << 27;
+#       endif
+#       ifdef HAS_sprintf_void
+            flags += 1L << 26;
+#       endif
+#   else
+#       ifdef HAS_snprintf_void
+            flags += 1L << 26;
+#       endif
+#   endif
 #endif
     return flags;
 }
@@ -166,28 +174,34 @@ const char * ZEXPORT zError(int err) {
 
 #ifndef HAVE_MEMCPY
 
-void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) {
-    if (len == 0) return;
-    do {
-        *dest++ = *source++; /* ??? to be unrolled */
-    } while (--len != 0);
+void ZLIB_INTERNAL zmemcpy(void FAR *dst, const void FAR *src, z_size_t n) {
+    uchf *p = dst;
+    const uchf *q = src;
+    while (n) {
+        *p++ = *q++;
+        n--;
+    }
 }
 
-int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) {
-    uInt j;
-
-    for (j = 0; j < len; j++) {
-        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+int ZLIB_INTERNAL zmemcmp(const void FAR *s1, const void FAR *s2, z_size_t n) {
+    const uchf *p = s1, *q = s2;
+    while (n) {
+        if (*p++ != *q++)
+            return (int)p[-1] - (int)q[-1];
+        n--;
     }
     return 0;
 }
 
-void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) {
+void ZLIB_INTERNAL zmemzero(void FAR *b, z_size_t len) {
+    uchf *p = b;
     if (len == 0) return;
-    do {
-        *dest++ = 0;  /* ??? to be unrolled */
-    } while (--len != 0);
+    while (len) {
+        *p++ = 0;
+        len--;
+    }
 }
+
 #endif
 
 #ifndef Z_SOLO
diff --git a/src/java.base/share/native/libzip/zlib/zutil.h b/src/java.base/share/native/libzip/zlib/zutil.h
index 2b7e697bef9..b337065875d 100644
--- a/src/java.base/share/native/libzip/zlib/zutil.h
+++ b/src/java.base/share/native/libzip/zlib/zutil.h
@@ -23,7 +23,7 @@
  */
 
 /* zutil.h -- internal interface and configuration of the compression library
- * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler
+ * Copyright (C) 1995-2026 Jean-loup Gailly, Mark Adler
  * For conditions of distribution and use, see copyright notice in zlib.h
  */
 
@@ -60,6 +60,10 @@
    define "local" for the non-static meaning of "static", for readability
    (compile with -Dlocal if your debugger can't find static symbols) */
 
+extern const char deflate_copyright[];
+extern const char inflate_copyright[];
+extern const char inflate9_copyright[];
+
 typedef unsigned char  uch;
 typedef uch FAR uchf;
 typedef unsigned short ush;
@@ -72,6 +76,8 @@ typedef unsigned long  ulg;
 #    define Z_U8 unsigned long
 #  elif (ULLONG_MAX == 0xffffffffffffffff)
 #    define Z_U8 unsigned long long
+#  elif (ULONG_LONG_MAX == 0xffffffffffffffff)
+#    define Z_U8 unsigned long long
 #  elif (UINT_MAX == 0xffffffffffffffff)
 #    define Z_U8 unsigned
 #  endif
@@ -87,7 +93,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
 /* To be used only when the state is known to be valid */
 
         /* common constants */
-
+#if MAX_WBITS < 9 || MAX_WBITS > 15
+#  error MAX_WBITS must be in 9..15
+#endif
 #ifndef DEF_WBITS
 #  define DEF_WBITS MAX_WBITS
 #endif
@@ -165,7 +173,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
 #  define OS_CODE  7
 #endif
 
-#ifdef __acorn
+#if defined(__acorn) || defined(__riscos)
 #  define OS_CODE 13
 #endif
 
@@ -192,11 +200,10 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
 #endif
 
 /* provide prototypes for these when building zlib without LFS */
-#if !defined(_WIN32) && \
-    (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
-    ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t);
-    ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t);
-    ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t);
+#ifndef Z_LARGE64
+   ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t);
+   ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t);
+   ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t);
 #endif
 
         /* common defaults */
@@ -235,9 +242,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
 #    define zmemzero(dest, len) memset(dest, 0, len)
 #  endif
 #else
-   void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len);
-   int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len);
-   void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len);
+   void ZLIB_INTERNAL zmemcpy(void FAR *, const void FAR *, z_size_t);
+   int ZLIB_INTERNAL zmemcmp(const void FAR *, const void FAR *, z_size_t);
+   void ZLIB_INTERNAL zmemzero(void FAR *, z_size_t);
 #endif
 
 /* Diagnostic functions */
@@ -275,4 +282,74 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
 #define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
                     (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
 
+#ifdef Z_ONCE
+/*
+  Create a local z_once() function depending on the availability of atomics.
+ */
+
+/* Check for the availability of atomics. */
+#if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \
+    !defined(__STDC_NO_ATOMICS__)
+
+#include 
+typedef struct {
+    atomic_flag begun;
+    atomic_int done;
+} z_once_t;
+#define Z_ONCE_INIT {ATOMIC_FLAG_INIT, 0}
+
+/*
+  Run the provided init() function exactly once, even if multiple threads
+  invoke once() at the same time. The state must be a once_t initialized with
+  Z_ONCE_INIT.
+ */
+local void z_once(z_once_t *state, void (*init)(void)) {
+    if (!atomic_load(&state->done)) {
+        if (atomic_flag_test_and_set(&state->begun))
+            while (!atomic_load(&state->done))
+                ;
+        else {
+            init();
+            atomic_store(&state->done, 1);
+        }
+    }
+}
+
+#else   /* no atomics */
+
+#warning zlib not thread-safe
+
+typedef struct z_once_s {
+    volatile int begun;
+    volatile int done;
+} z_once_t;
+#define Z_ONCE_INIT {0, 0}
+
+/* Test and set. Alas, not atomic, but tries to limit the period of
+   vulnerability. */
+local int test_and_set(int volatile *flag) {
+    int was;
+
+    was = *flag;
+    *flag = 1;
+    return was;
+}
+
+/* Run the provided init() function once. This is not thread-safe. */
+local void z_once(z_once_t *state, void (*init)(void)) {
+    if (!state->done) {
+        if (test_and_set(&state->begun))
+            while (!state->done)
+                ;
+        else {
+            init();
+            state->done = 1;
+        }
+    }
+}
+
+#endif /* ?atomics */
+
+#endif /* Z_ONCE */
+
 #endif /* ZUTIL_H */

From 173153e1b25c5081d6e6886fe9588847f5a564b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= 
Date: Thu, 26 Feb 2026 12:03:16 +0000
Subject: [PATCH 061/636] 8376403: Avoid loading ArrayDeque in
 java.util.zip.ZipFile

Reviewed-by: lancea, jpai
---
 .../share/classes/java/util/zip/ZipFile.java  | 40 +++++++++----------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java
index a198c35c366..43a3b37b7d0 100644
--- a/src/java.base/share/classes/java/util/zip/ZipFile.java
+++ b/src/java.base/share/classes/java/util/zip/ZipFile.java
@@ -692,7 +692,7 @@ public class ZipFile implements ZipConstants, Closeable {
         final Set istreams;
 
         // List of cached Inflater objects for decompression
-        Deque inflaterCache;
+        List inflaterCache;
 
         final Cleanable cleanable;
 
@@ -702,7 +702,7 @@ public class ZipFile implements ZipConstants, Closeable {
             assert zipCoder != null : "null ZipCoder";
             this.cleanable = CleanerFactory.cleaner().register(zf, this);
             this.istreams = Collections.newSetFromMap(new WeakHashMap<>());
-            this.inflaterCache = new ArrayDeque<>();
+            this.inflaterCache = new ArrayList<>();
             this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0, zipCoder);
         }
 
@@ -715,10 +715,10 @@ public class ZipFile implements ZipConstants, Closeable {
          * a new one.
          */
         Inflater getInflater() {
-            Inflater inf;
             synchronized (inflaterCache) {
-                if ((inf = inflaterCache.poll()) != null) {
-                    return inf;
+                if (!inflaterCache.isEmpty()) {
+                    // return the most recently used Inflater from the cache of not-in-use Inflaters
+                    return inflaterCache.removeLast();
                 }
             }
             return new Inflater(true);
@@ -728,7 +728,7 @@ public class ZipFile implements ZipConstants, Closeable {
          * Releases the specified inflater to the list of available inflaters.
          */
         void releaseInflater(Inflater inf) {
-            Deque inflaters = this.inflaterCache;
+            List inflaters = this.inflaterCache;
             if (inflaters != null) {
                 synchronized (inflaters) {
                     // double checked!
@@ -747,13 +747,12 @@ public class ZipFile implements ZipConstants, Closeable {
             IOException ioe = null;
 
             // Release cached inflaters and close the cache first
-            Deque inflaters = this.inflaterCache;
+            List inflaters = this.inflaterCache;
             if (inflaters != null) {
                 synchronized (inflaters) {
                     // no need to double-check as only one thread gets a
                     // chance to execute run() (Cleaner guarantee)...
-                    Inflater inf;
-                    while ((inf = inflaters.poll()) != null) {
+                    for (Inflater inf : inflaters) {
                         inf.end();
                     }
                     // close inflaters cache
@@ -762,23 +761,22 @@ public class ZipFile implements ZipConstants, Closeable {
             }
 
             // Close streams, release their inflaters
-            if (istreams != null) {
-                synchronized (istreams) {
-                    if (!istreams.isEmpty()) {
-                        InputStream[] copy = istreams.toArray(new InputStream[0]);
-                        istreams.clear();
-                        for (InputStream is : copy) {
-                            try {
-                                is.close();
-                            } catch (IOException e) {
-                                if (ioe == null) ioe = e;
-                                else ioe.addSuppressed(e);
-                            }
+            synchronized (istreams) {
+                if (!istreams.isEmpty()) {
+                    InputStream[] copy = istreams.toArray(new InputStream[0]);
+                    istreams.clear();
+                    for (InputStream is : copy) {
+                        try {
+                            is.close();
+                        } catch (IOException e) {
+                            if (ioe == null) ioe = e;
+                            else ioe.addSuppressed(e);
                         }
                     }
                 }
             }
 
+
             // Release ZIP src
             if (zsrc != null) {
                 synchronized (zsrc) {

From b13a291667535fdea30936ea5dc87f405e637069 Mon Sep 17 00:00:00 2001
From: Alan Bateman 
Date: Thu, 26 Feb 2026 13:38:14 +0000
Subject: [PATCH 062/636] 8378268: Thread.join can wait on Thread, allows
 joinNanos to be removed

Reviewed-by: jpai, vklang
---
 .../share/classes/java/lang/Thread.java       | 33 +++------
 .../classes/java/lang/VirtualThread.java      | 67 ++++---------------
 2 files changed, 23 insertions(+), 77 deletions(-)

diff --git a/src/java.base/share/classes/java/lang/Thread.java b/src/java.base/share/classes/java/lang/Thread.java
index 57d28aca5f4..4890c1af45b 100644
--- a/src/java.base/share/classes/java/lang/Thread.java
+++ b/src/java.base/share/classes/java/lang/Thread.java
@@ -1881,8 +1881,8 @@ public class Thread implements Runnable {
      * been {@link #start() started}.
      *
      * @implNote
-     * For platform threads, the implementation uses a loop of {@code this.wait}
-     * calls conditioned on {@code this.isAlive}. As a thread terminates the
+     * This implementation uses a loop of {@code this.wait} calls
+     * conditioned on {@code this.isAlive}. As a thread terminates the
      * {@code this.notifyAll} method is invoked. It is recommended that
      * applications not use {@code wait}, {@code notify}, or
      * {@code notifyAll} on {@code Thread} instances.
@@ -1901,13 +1901,12 @@ public class Thread implements Runnable {
     public final void join(long millis) throws InterruptedException {
         if (millis < 0)
             throw new IllegalArgumentException("timeout value is negative");
-
-        if (this instanceof VirtualThread vthread) {
-            if (isAlive()) {
-                long nanos = MILLISECONDS.toNanos(millis);
-                vthread.joinNanos(nanos);
-            }
+        if (!isAlive())
             return;
+
+        // ensure there is a notifyAll to wake up waiters when this thread terminates
+        if (this instanceof VirtualThread vthread) {
+            vthread.beforeJoin();
         }
 
         synchronized (this) {
@@ -1936,8 +1935,8 @@ public class Thread implements Runnable {
      * been {@link #start() started}.
      *
      * @implNote
-     * For platform threads, the implementation uses a loop of {@code this.wait}
-     * calls conditioned on {@code this.isAlive}. As a thread terminates the
+     * This implementation uses a loop of {@code this.wait} calls
+     * conditioned on {@code this.isAlive}. As a thread terminates the
      * {@code this.notifyAll} method is invoked. It is recommended that
      * applications not use {@code wait}, {@code notify}, or
      * {@code notifyAll} on {@code Thread} instances.
@@ -1966,16 +1965,6 @@ public class Thread implements Runnable {
             throw new IllegalArgumentException("nanosecond timeout value out of range");
         }
 
-        if (this instanceof VirtualThread vthread) {
-            if (isAlive()) {
-                // convert arguments to a total in nanoseconds
-                long totalNanos = MILLISECONDS.toNanos(millis);
-                totalNanos += Math.min(Long.MAX_VALUE - totalNanos, nanos);
-                vthread.joinNanos(totalNanos);
-            }
-            return;
-        }
-
         if (nanos > 0 && millis < Long.MAX_VALUE) {
             millis++;
         }
@@ -2035,10 +2024,6 @@ public class Thread implements Runnable {
         if (nanos <= 0)
             return false;
 
-        if (this instanceof VirtualThread vthread) {
-            return vthread.joinNanos(nanos);
-        }
-
         // convert to milliseconds
         long millis = MILLISECONDS.convert(nanos, NANOSECONDS);
         if (nanos > NANOSECONDS.convert(millis, MILLISECONDS)) {
diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java
index 6cd7ccbbba1..f058f967b91 100644
--- a/src/java.base/share/classes/java/lang/VirtualThread.java
+++ b/src/java.base/share/classes/java/lang/VirtualThread.java
@@ -26,7 +26,6 @@ package java.lang;
 
 import java.util.Locale;
 import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ForkJoinPool;
@@ -68,7 +67,6 @@ final class VirtualThread extends BaseVirtualThread {
     private static final long STATE = U.objectFieldOffset(VirtualThread.class, "state");
     private static final long PARK_PERMIT = U.objectFieldOffset(VirtualThread.class, "parkPermit");
     private static final long CARRIER_THREAD = U.objectFieldOffset(VirtualThread.class, "carrierThread");
-    private static final long TERMINATION = U.objectFieldOffset(VirtualThread.class, "termination");
     private static final long ON_WAITING_LIST = U.objectFieldOffset(VirtualThread.class, "onWaitingList");
 
     // scheduler and continuation
@@ -184,8 +182,8 @@ final class VirtualThread extends BaseVirtualThread {
     // carrier thread when mounted, accessed by VM
     private volatile Thread carrierThread;
 
-    // termination object when joining, created lazily if needed
-    private volatile CountDownLatch termination;
+    // true to notifyAll after this virtual thread terminates
+    private volatile boolean notifyAllAfterTerminate;
 
     /**
      * Returns the default scheduler.
@@ -677,11 +675,11 @@ final class VirtualThread extends BaseVirtualThread {
         assert carrierThread == null;
         setState(TERMINATED);
 
-        // notify anyone waiting for this virtual thread to terminate
-        CountDownLatch termination = this.termination;
-        if (termination != null) {
-            assert termination.getCount() == 1;
-            termination.countDown();
+        // notifyAll to wakeup any threads waiting for this thread to terminate
+        if (notifyAllAfterTerminate) {
+            synchronized (this) {
+                notifyAll();
+            }
         }
 
         // notify container
@@ -740,6 +738,13 @@ final class VirtualThread extends BaseVirtualThread {
         // do nothing
     }
 
+    /**
+     * Invoked by Thread.join before a thread waits for this virtual thread to terminate.
+     */
+    void beforeJoin() {
+        notifyAllAfterTerminate = true;
+    }
+
     /**
      * Parks until unparked or interrupted. If already unparked then the parking
      * permit is consumed and this method completes immediately (meaning it doesn't
@@ -999,36 +1004,6 @@ final class VirtualThread extends BaseVirtualThread {
         }
     }
 
-    /**
-     * Waits up to {@code nanos} nanoseconds for this virtual thread to terminate.
-     * A timeout of {@code 0} means to wait forever.
-     *
-     * @throws InterruptedException if interrupted while waiting
-     * @return true if the thread has terminated
-     */
-    boolean joinNanos(long nanos) throws InterruptedException {
-        if (state() == TERMINATED)
-            return true;
-
-        // ensure termination object exists, then re-check state
-        CountDownLatch termination = getTermination();
-        if (state() == TERMINATED)
-            return true;
-
-        // wait for virtual thread to terminate
-        if (nanos == 0) {
-            termination.await();
-        } else {
-            boolean terminated = termination.await(nanos, NANOSECONDS);
-            if (!terminated) {
-                // waiting time elapsed
-                return false;
-            }
-        }
-        assert state() == TERMINATED;
-        return true;
-    }
-
     @Override
     void blockedOn(Interruptible b) {
         disableSuspendAndPreempt();
@@ -1239,20 +1214,6 @@ final class VirtualThread extends BaseVirtualThread {
         return obj == this;
     }
 
-    /**
-     * Returns the termination object, creating it if needed.
-     */
-    private CountDownLatch getTermination() {
-        CountDownLatch termination = this.termination;
-        if (termination == null) {
-            termination = new CountDownLatch(1);
-            if (!U.compareAndSetReference(this, TERMINATION, null, termination)) {
-                termination = this.termination;
-            }
-        }
-        return termination;
-    }
-
     /**
      * Returns the lock object to synchronize on when accessing carrierThread.
      * The lock prevents carrierThread from being reset to null during unmount.

From 82ff0255c59645ec115ac7a5fa055667770bf0cf Mon Sep 17 00:00:00 2001
From: Vicente Romero 
Date: Thu, 26 Feb 2026 14:12:50 +0000
Subject: [PATCH 063/636] 8374910: Use of containsTypeEquivalent in array type
 equality test seems bogus

Reviewed-by: liach
---
 .../share/classes/com/sun/tools/javac/code/Types.java           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
index 539b1470a75..9ffab9fd961 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
@@ -1436,7 +1436,7 @@ public class Types {
                     return visit(s, t);
 
                 return s.hasTag(ARRAY)
-                    && containsTypeEquivalent(t.elemtype, elemtype(s));
+                    && visit(t.elemtype, elemtype(s));
             }
 
             @Override

From 3b8abd459ffc195957a8cb6a45d4e72e100099cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= 
Date: Thu, 26 Feb 2026 15:12:21 +0000
Subject: [PATCH 064/636] 8378398: Modernize
 test/jdk/java/net/URLClassLoader/HttpTest.java

Reviewed-by: dfuchs
---
 .../jdk/java/net/URLClassLoader/HttpTest.java | 459 ++++++++++--------
 1 file changed, 262 insertions(+), 197 deletions(-)

diff --git a/test/jdk/java/net/URLClassLoader/HttpTest.java b/test/jdk/java/net/URLClassLoader/HttpTest.java
index 620eb34dbaa..09ee6dcfa85 100644
--- a/test/jdk/java/net/URLClassLoader/HttpTest.java
+++ b/test/jdk/java/net/URLClassLoader/HttpTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -24,233 +24,298 @@
 /**
  * @test
  * @bug 4636331
+ * @modules jdk.httpserver
  * @library /test/lib
- * @summary Check that URLClassLoader doesn't create excessive http
- *          connections
+ * @summary Check that URLClassLoader with HTTP paths lookups produce the expected http requests
+ * @run junit HttpTest
  */
 import java.net.*;
 import java.io.*;
+import java.nio.charset.StandardCharsets;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
 import jdk.test.lib.net.URIBuilder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class HttpTest {
 
-    /*
-     * Simple http server to service http requests. Auto shutdown
-     * if "idle" (no requests) for 10 seconds. Forks worker thread
-     * to service persistent connections. Work threads shutdown if
-     * "idle" for 5 seconds.
-     */
-    static class HttpServer implements Runnable {
+    // HTTP server used to track requests
+    static HttpServer server;
 
-        private static HttpServer svr = null;
-        private static Counters cnts = null;
-        private static ServerSocket ss;
+    // RequestLog for capturing requests
+    static class RequestLog {
+        List log = new ArrayList<>();
 
-        private static Object counterLock = new Object();
-        private static int getCount = 0;
-        private static int headCount = 0;
-
-        class Worker extends Thread {
-            Socket s;
-            Worker(Socket s) {
-                this.s = s;
-            }
-
-            public void run() {
-                InputStream in = null;
-                try {
-                    in = s.getInputStream();
-                    for (;;) {
-
-                        // read entire request from client
-                        byte b[] = new byte[1024];
-                        int n, total=0;
-
-                        // max 5 seconds to wait for new request
-                        s.setSoTimeout(5000);
-                        try {
-                            do {
-                                n = in.read(b, total, b.length-total);
-                                // max 0.5 seconds between each segment
-                                // of request.
-                                s.setSoTimeout(500);
-                                if (n > 0) total += n;
-                            } while (n > 0);
-                        } catch (SocketTimeoutException e) { }
-
-                        if (total == 0) {
-                            s.close();
-                            return;
-                        }
-
-                        boolean getRequest = false;
-                        if (b[0] == 'G' && b[1] == 'E' && b[2] == 'T')
-                            getRequest = true;
-
-                        synchronized (counterLock) {
-                            if (getRequest)
-                                getCount++;
-                            else
-                                headCount++;
-                        }
-
-                        // response to client
-                        PrintStream out = new PrintStream(
-                                new BufferedOutputStream(
-                                        s.getOutputStream() ));
-                        out.print("HTTP/1.1 200 OK\r\n");
-
-                        out.print("Content-Length: 75000\r\n");
-                        out.print("\r\n");
-                        if (getRequest) {
-                            for (int i=0; i<75*1000; i++) {
-                                out.write( (byte)'.' );
-                            }
-                        }
-                        out.flush();
-
-                    } // for
-
-                } catch (Exception e) {
-                    unexpected(e);
-                } finally {
-                    if (in != null) { try {in.close(); } catch(IOException e) {unexpected(e);} }
-                }
-            }
+        // Add a request to the log
+        public synchronized void capture(String method, URI uri) {
+            log.add(new Request(method, uri));
         }
 
-        HttpServer() throws Exception {
-            ss = new ServerSocket();
-            ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
+        // Clear requests
+        public synchronized void clear() {
+            log.clear();
         }
 
-        public void run() {
-            try {
-                // shutdown if no request in 10 seconds.
-                ss.setSoTimeout(10000);
-                for (;;) {
-                    Socket s = ss.accept();
-                    (new Worker(s)).start();
-                }
-            } catch (Exception e) {
-            }
+        public synchronized List requests() {
+            return List.copyOf(log);
         }
-
-        void unexpected(Exception e) {
-            System.out.println(e);
-            e.printStackTrace();
-        }
-
-        public static HttpServer create() throws Exception {
-            if (svr != null)
-                return svr;
-            cnts = new Counters();
-            svr = new HttpServer();
-            (new Thread(svr)).start();
-            return svr;
-        }
-
-        public static void shutdown() throws Exception {
-            if (svr != null) {
-                ss.close();
-                svr = null;
-            }
-        }
-
-        public int port() {
-            return ss.getLocalPort();
-        }
-
-        public static class Counters {
-            public void reset() {
-                synchronized (counterLock) {
-                    getCount = 0;
-                    headCount = 0;
-                }
-            }
-
-            public int getCount() {
-                synchronized (counterLock) {
-                    return getCount;
-                }
-            }
-
-            public int headCount() {
-                synchronized (counterLock) {
-                    return headCount;
-                }
-            }
-
-            public String toString() {
-                synchronized (counterLock) {
-                    return "GET count: " + getCount + "; " +
-                       "HEAD count: " + headCount;
-                }
-            }
-        }
-
-        public Counters counters() {
-            return cnts;
-        }
-
     }
 
-    public static void main(String args[]) throws Exception {
-        boolean failed = false;
+    // Represents a single request
+    record Request(String method, URI path) {}
 
-        // create http server
-        HttpServer svr = HttpServer.create();
+    // Request log for this test
+    static RequestLog log = new RequestLog();
 
-        // create class loader
-        URL urls[] = {
-                URIBuilder.newBuilder().scheme("http").loopback().port(svr.port())
-                        .path("/dir1/").toURL(),
-                URIBuilder.newBuilder().scheme("http").loopback().port(svr.port())
-                        .path("/dir2/").toURL(),
+    // Handlers specific to tests
+    static Map handlers = new ConcurrentHashMap<>();
+
+    // URLClassLoader with HTTP URL class path
+    private static URLClassLoader loader;
+
+    @BeforeAll
+    static void setup() throws Exception {
+        server = HttpServer.create();
+        server.bind(new InetSocketAddress(
+                InetAddress.getLoopbackAddress(), 0), 0);
+        server.createContext("/", e -> {
+            // Capture request in the log
+            log.capture(e.getRequestMethod(), e.getRequestURI());
+            // Check for custom handler
+            HttpHandler custom = handlers.get(e.getRequestURI());
+            if (custom != null) {
+                custom.handle(e);
+            } else {
+                // Successful responses echo the request path in the body
+                byte[] response = e.getRequestURI().getPath()
+                        .getBytes(StandardCharsets.UTF_8);
+                e.sendResponseHeaders(200, response.length);
+                try (var out = e.getResponseBody()) {
+                    out.write(response);
+                }
+            }
+            e.close();
+        });
+        server.start();
+        int port = server.getAddress().getPort();
+
+        // Create class loader with two HTTP URLs
+        URL[] searchPath = new URL[] {
+                getHttpUri("/dir1/", port),
+                getHttpUri("/dir2/", port)
         };
-        URLClassLoader cl = new URLClassLoader(urls);
+        loader = new URLClassLoader(searchPath);
+    }
 
-        // Test 1 - check that getResource does single HEAD request
-        svr.counters().reset();
-        URL url = cl.getResource("foo.gif");
-        System.out.println(svr.counters());
+    // Create an HTTP URL for the given path and port using the loopback address
+    private static URL getHttpUri(String path, int port) throws Exception {
+        return URIBuilder.newBuilder()
+                .scheme("http")
+                .loopback()
+                .port(port)
+                .path(path).toURL();
+    }
 
-        if (svr.counters().getCount() > 0 ||
-            svr.counters().headCount() > 1) {
-            failed = true;
+    // Add redirect handler for a given path
+    private static void redirect(String path, String target) {
+        handlers.put(URI.create(path), e -> {
+            e.getResponseHeaders().set("Location", target);
+            e.sendResponseHeaders(301, 0);
+        });
+    }
+
+    // Return 404 not found for a given path
+    private static void notFound(String path) {
+        handlers.put(URI.create(path),  e ->
+                e.sendResponseHeaders(404, 0));
+    }
+
+    @AfterAll
+    static void shutdown() {
+        server.stop(2000);
+    }
+
+    @BeforeEach
+    void reset() {
+        synchronized (log) {
+            log.clear();
+        }
+        handlers.clear();
+    }
+
+    // Check that getResource does single HEAD request
+    @Test
+    void getResourceSingleHead() {
+        URL url = loader.getResource("foo.gif");
+        // Expect one HEAD
+        assertRequests(e -> e
+                .request("HEAD", "/dir1/foo.gif")
+        );
+    }
+
+    // Check that getResource follows redirects
+    @Test
+    void getResourceShouldFollowRedirect() {
+        redirect("/dir1/foo.gif", "/dir1/target.gif");
+        URL url = loader.getResource("foo.gif");
+        // Expect extra HEAD for redirect target
+        assertRequests(e -> e
+                .request("HEAD", "/dir1/foo.gif")
+                .request("HEAD", "/dir1/target.gif")
+        );
+
+        /*
+         * Note: Long-standing behavior is that URLClassLoader:getResource
+         * returns a URL for the requested resource, not the location redirected to
+         */
+        assertEquals("/dir1/foo.gif", url.getPath());
+
+    }
+
+    // Check that getResource treats a redirect to a not-found resource as a not-found resource
+    @Test
+    void getResourceRedirectTargetNotFound() {
+        redirect("/dir1/foo.gif", "/dir1/target.gif");
+        notFound("/dir1/target.gif");
+        URL url = loader.getResource("foo.gif");
+        // Expect extra HEAD for redirect target and next URL in search path
+        assertRequests(e -> e
+                .request("HEAD", "/dir1/foo.gif")
+                .request("HEAD", "/dir1/target.gif")
+                .request("HEAD", "/dir2/foo.gif")
+
+        );
+        // Should find URL for /dir2
+        assertEquals("/dir2/foo.gif", url.getPath());
+    }
+
+    // Check that getResourceAsStream does one HEAD and one GET request
+    @Test
+    void getResourceAsStreamSingleGet() throws IOException {
+        // Expect content from the first path
+        try (var in = loader.getResourceAsStream("foo2.gif")) {
+            assertEquals("/dir1/foo2.gif",
+                    new String(in.readAllBytes(), StandardCharsets.UTF_8));
+        }
+        // Expect one HEAD, one GET
+        assertRequests( e -> e
+                .request("HEAD", "/dir1/foo2.gif")
+                .request("GET",  "/dir1/foo2.gif")
+        );
+    }
+
+    // Check that getResourceAsStream follows redirects
+    @Test
+    void getResourceAsStreamFollowRedirect() throws IOException {
+        redirect("/dir1/foo.gif", "/dir1/target.gif");
+        // Expect content from the redirected location
+        try (var in = loader.getResourceAsStream("foo.gif")) {
+            assertEquals("/dir1/target.gif",
+                    new String(in.readAllBytes(), StandardCharsets.UTF_8));
         }
 
-        // Test 2 - check that getResourceAsStream does at most
-        //          one GET request
-        svr.counters().reset();
-        InputStream in = cl.getResourceAsStream("foo2.gif");
-        in.close();
-        System.out.println(svr.counters());
-        if (svr.counters().getCount() > 1) {
-            failed = true;
+        /*
+         * Note: Long standing behavior of URLClassLoader::getResourceAsStream
+         * is to use HEAD during the findResource resource discovery and to not
+         * "remember" the HEAD redirect location when performing the GET. This
+         * explains why we observe two redirects here, one for HEAD, one for GET.
+         */
+        assertRequests( e -> e
+                .request("HEAD", "/dir1/foo.gif")
+                .request("HEAD", "/dir1/target.gif")
+                .request("GET",  "/dir1/foo.gif")
+                .request("GET",  "/dir1/target.gif")
+        );
+    }
+
+    // getResourceAsStream on a 404 should try next path
+    @Test
+    void getResourceTryNextPath() throws IOException {
+        // Make the first path return 404
+        notFound("/dir1/foo.gif");
+        // Expect content from the second path
+        try (var in = loader.getResourceAsStream("foo.gif")) {
+            assertEquals("/dir2/foo.gif",
+                    new String(in.readAllBytes(), StandardCharsets.UTF_8));
         }
+        // Expect two HEADs, one GET
+        assertRequests(e -> e
+                .request("HEAD", "/dir1/foo.gif")
+                .request("HEAD", "/dir2/foo.gif")
+                .request("GET",  "/dir2/foo.gif")
+        );
+    }
 
-        // Test 3 - check that getResources only does HEAD requests
-        svr.counters().reset();
-        Enumeration e = cl.getResources("foos.gif");
-        try {
-            for (;;) {
-                e.nextElement();
-            }
-        } catch (NoSuchElementException exc) { }
-        System.out.println(svr.counters());
-        if (svr.counters().getCount() > 1) {
-            failed = true;
-        }
+    // Check that getResources only does HEAD requests
+    @Test
+    void getResourcesOnlyHead() throws IOException {
+        Collections.list(loader.getResources("foos.gif"));
+        // Expect one HEAD for each path
+        assertRequests(e ->  e
+                .request("HEAD", "/dir1/foos.gif")
+                .request("HEAD", "/dir2/foos.gif")
+        );
+    }
 
-        // shutdown http server
-        svr.shutdown();
+    // Check that getResources skips 404 URL
+    @Test
+    void getResourcesShouldSkipFailedHead() throws IOException {
+        // Make first path fail with 404
+        notFound("/dir1/foos.gif");
+        List resources = Collections.list(loader.getResources("foos.gif"));
+        // Expect one HEAD for each path
+        assertRequests(e ->  e
+                .request("HEAD", "/dir1/foos.gif")
+                .request("HEAD", "/dir2/foos.gif")
+        );
 
-        if (failed) {
-            throw new Exception("Excessive http connections established - Test failed");
+        // Expect a single URL to be returned
+        assertEquals(1, resources.size());
+    }
+
+    // Utils for asserting requests
+    static class Expect {
+        List requests = new ArrayList<>();
+
+        Expect request(String method, String path) {
+            requests.add(new Request(method, URI.create(path)));
+            return this;
         }
     }
 
+    static void assertRequests(Consumer e) {
+        // Collect expected requests
+        Expect exp = new Expect();
+        e.accept(exp);
+        List expected = exp.requests;
+
+        // Actual requests
+        List requests = log.requests();
+
+        // Verify expected number of requests
+        assertEquals(expected.size(), requests.size(), "Unexpected request count");
+
+        // Verify expected requests in order
+        for (int i = 0; i < expected.size(); i++) {
+            Request ex = expected.get(i);
+            Request req = requests.get(i);
+            // Verify method
+            assertEquals(ex.method, req.method,
+                    String.format("Request %s has unexpected method %s", i, ex.method)
+            );
+            // Verify path
+            assertEquals(ex.path, req.path,
+                    String.format("Request %s has unexpected request URI %s", i, ex.path)
+            );
+        }
+    }
 }

From 4f83d211d16bf7aab8b3d7128df6764e017166ef Mon Sep 17 00:00:00 2001
From: Vicente Romero 
Date: Thu, 26 Feb 2026 15:18:54 +0000
Subject: [PATCH 065/636] 8368864: Confusing error message (or wrong error)
 when record component has @deprecated Javadoc tag

Reviewed-by: jlahoda
---
 .../sun/tools/javac/parser/JavacParser.java   |  6 +++
 .../javac/records/RecordCompilationTests.java | 52 +++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
index 16f26c836f8..d32b892eb54 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
@@ -5406,6 +5406,12 @@ public class JavacParser implements Parser {
 
         if (recordComponent) {
             mods = modifiersOpt();
+            /* it could be that the user added a javadoc with the @deprecated tag, when analyzing this
+             * javadoc, javac will set the DEPRECATED flag. This is correct in most cases but not for
+             * record components and thus should be removed in that case. Any javadoc applied to
+             * record components is ignored
+             */
+            mods.flags &= ~Flags.DEPRECATED;
         } else {
             mods = optFinal(Flags.PARAMETER | (lambdaParameter ? Flags.LAMBDA_PARAMETER : 0));
         }
diff --git a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java
index a8384ba4692..48b5cdd8588 100644
--- a/test/langtools/tools/javac/records/RecordCompilationTests.java
+++ b/test/langtools/tools/javac/records/RecordCompilationTests.java
@@ -2169,4 +2169,56 @@ class RecordCompilationTests extends CompilationTestCase {
                 """
         );
     }
+
+    @Test
+    void testDeprecatedJavadoc() {
+        String[] previousOptions = getCompileOptions();
+        try {
+            setCompileOptions(new String[] {"-Xlint:deprecation"});
+            assertOKWithWarning("compiler.warn.has.been.deprecated",
+                """
+                record R(
+                    /**
+                     * @deprecated
+                     */
+                    @Deprecated
+                    int i
+                ) {}
+                class Client {
+                    R r;
+                    int j = r.i();
+                }
+                """
+            );
+            assertOKWithWarning("compiler.warn.has.been.deprecated",
+                """
+                record R(
+                    @Deprecated
+                    int i
+                ) {}
+                class Client {
+                    R r;
+                    int j = r.i();
+                }
+                """
+            );
+            // javadoc tag only has no effect
+            assertOK(
+                    """
+                    record R(
+                        /**
+                         * @deprecated
+                         */
+                        int i
+                    ) {}
+                    class Client {
+                        R r;
+                        int j = r.i();
+                    }
+                    """
+            );
+        } finally {
+            setCompileOptions(previousOptions);
+        }
+    }
 }

From 94de8982f99ff7fd5d955246a56a12bf2bf69785 Mon Sep 17 00:00:00 2001
From: Vicente Romero 
Date: Thu, 26 Feb 2026 15:21:31 +0000
Subject: [PATCH 066/636] 8372382: Invalid RuntimeVisibleTypeAnnotations for
 compact record constructor

Reviewed-by: liach
---
 .../com/sun/tools/javac/parser/JavacParser.java    |  5 +++--
 .../TypeAnnotationsPositionsOnRecords.java         | 14 ++++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
index d32b892eb54..df5da5cb954 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
@@ -4421,12 +4421,13 @@ public class JavacParser implements Parser {
                 JCMethodDecl methDef = (JCMethodDecl) def;
                 if (methDef.name == names.init && methDef.params.isEmpty() && (methDef.mods.flags & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0) {
                     ListBuffer tmpParams = new ListBuffer<>();
+                    TreeCopier copier = new TreeCopier<>(F);
                     for (JCVariableDecl param : headerFields) {
                         tmpParams.add(F.at(param)
                                 // we will get flags plus annotations from the record component
                                 .VarDef(F.Modifiers(Flags.PARAMETER | Flags.GENERATED_MEMBER | Flags.MANDATED | param.mods.flags & Flags.VARARGS,
-                                        param.mods.annotations),
-                                param.name, param.vartype, null));
+                                        copier.copy(param.mods.annotations)),
+                                param.name, copier.copy(param.vartype), null));
                     }
                     methDef.params = tmpParams.toList();
                 }
diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java
index dfa266ef035..b8ee32fcacd 100644
--- a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsPositionsOnRecords.java
@@ -78,6 +78,17 @@ public class TypeAnnotationsPositionsOnRecords {
             record Record6(String t1, @Nullable String t2) {
                 public Record6 {}
             }
+
+            class Test2 {
+                @Target(ElementType.TYPE_USE)
+                @Retention(RetentionPolicy.RUNTIME)
+                public @interface Anno {}
+
+                class Foo {}
+                record Record7(Test2.@Anno Foo foo) {
+                    public Record7 {} // compact constructor
+                }
+            }
             """;
 
     public static void main(String... args) throws Exception {
@@ -100,6 +111,8 @@ public class TypeAnnotationsPositionsOnRecords {
                 "Record5.class").toUri()), 1);
         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
                 "Record6.class").toUri()), 1);
+        checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
+                "Test2$Record7.class").toUri()), 0);
     }
 
     void compileTestClass() throws Exception {
@@ -110,6 +123,7 @@ public class TypeAnnotationsPositionsOnRecords {
 
     void checkClassFile(final File cfile, int... taPositions) throws Exception {
         ClassModel classFile = ClassFile.of().parse(cfile.toPath());
+        System.err.println("-----------loading " + cfile.getPath());
         int accessorPos = 0;
         int checkedAccessors = 0;
         for (MethodModel method : classFile.methods()) {

From 71a1af7d0b4723c8ed740fc40ede75091ecf8c07 Mon Sep 17 00:00:00 2001
From: Phil Race 
Date: Thu, 26 Feb 2026 16:23:24 +0000
Subject: [PATCH 067/636] 8378377: Remove use of AppContext from JEditorPane

Reviewed-by: serb, dnguyen, psadhukhan
---
 .../classes/javax/swing/JEditorPane.java      | 95 +++++--------------
 1 file changed, 23 insertions(+), 72 deletions(-)

diff --git a/src/java.desktop/share/classes/javax/swing/JEditorPane.java b/src/java.desktop/share/classes/javax/swing/JEditorPane.java
index 2f8f0f30722..3134b8bace2 100644
--- a/src/java.desktop/share/classes/javax/swing/JEditorPane.java
+++ b/src/java.desktop/share/classes/javax/swing/JEditorPane.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -1227,12 +1227,11 @@ public class JEditorPane extends JTextComponent {
      */
     @SuppressWarnings("deprecation")
     public static EditorKit createEditorKitForContentType(String type) {
-        Hashtable kitRegistry = getKitRegistry();
         EditorKit k = kitRegistry.get(type);
         if (k == null) {
             // try to dynamically load the support
-            String classname = getKitTypeRegistry().get(type);
-            ClassLoader loader = getKitLoaderRegistry().get(type);
+            String classname = kitTypeRegistry.get(type);
+            ClassLoader loader = kitLoaderRegistry.get(type);
             try {
                 Class c;
                 if (loader != null) {
@@ -1287,13 +1286,13 @@ public class JEditorPane extends JTextComponent {
      * @param loader the ClassLoader to use to load the name
      */
     public static void registerEditorKitForContentType(String type, String classname, ClassLoader loader) {
-        getKitTypeRegistry().put(type, classname);
+        kitTypeRegistry.put(type, classname);
         if (loader != null) {
-            getKitLoaderRegistry().put(type, loader);
+            kitLoaderRegistry.put(type, loader);
         } else {
-            getKitLoaderRegistry().remove(type);
+            kitLoaderRegistry.remove(type);
         }
-        getKitRegistry().remove(type);
+        kitRegistry.remove(type);
     }
 
     /**
@@ -1306,63 +1305,27 @@ public class JEditorPane extends JTextComponent {
      * @since 1.3
      */
     public static String getEditorKitClassNameForContentType(String type) {
-        return getKitTypeRegistry().get(type);
+        return kitTypeRegistry.get(type);
     }
 
-    private static Hashtable getKitTypeRegistry() {
-        loadDefaultKitsIfNecessary();
-        @SuppressWarnings("unchecked")
-        Hashtable tmp =
-            (Hashtable)SwingUtilities.appContextGet(kitTypeRegistryKey);
-        return tmp;
-    }
+    private static final Hashtable kitTypeRegistry = new Hashtable<>();
+    private static final Hashtable kitLoaderRegistry = new Hashtable<>();
 
-    private static Hashtable getKitLoaderRegistry() {
-        loadDefaultKitsIfNecessary();
-        @SuppressWarnings("unchecked")
-        Hashtable tmp =
-            (Hashtable)SwingUtilities.appContextGet(kitLoaderRegistryKey);
-        return tmp;
-    }
+    private static final Hashtable kitRegistry = new Hashtable<>(3);
 
-    private static Hashtable getKitRegistry() {
-        @SuppressWarnings("unchecked")
-        Hashtable ht =
-            (Hashtable)SwingUtilities.appContextGet(kitRegistryKey);
-        if (ht == null) {
-            ht = new Hashtable<>(3);
-            SwingUtilities.appContextPut(kitRegistryKey, ht);
-        }
-        return ht;
-    }
-
-    /**
-     * This is invoked every time the registries are accessed. Loading
-     * is done this way instead of via a static as the static is only
-     * called once when running in an AppContext.
-     */
-    private static void loadDefaultKitsIfNecessary() {
-        if (SwingUtilities.appContextGet(kitTypeRegistryKey) == null) {
-            synchronized(defaultEditorKitMap) {
-                if (defaultEditorKitMap.size() == 0) {
-                    defaultEditorKitMap.put("text/plain",
-                                            "javax.swing.JEditorPane$PlainEditorKit");
-                    defaultEditorKitMap.put("text/html",
-                                            "javax.swing.text.html.HTMLEditorKit");
-                    defaultEditorKitMap.put("text/rtf",
-                                            "javax.swing.text.rtf.RTFEditorKit");
-                    defaultEditorKitMap.put("application/rtf",
-                                            "javax.swing.text.rtf.RTFEditorKit");
-                }
-            }
-            Hashtable ht = new Hashtable<>();
-            SwingUtilities.appContextPut(kitTypeRegistryKey, ht);
-            ht = new Hashtable<>();
-            SwingUtilities.appContextPut(kitLoaderRegistryKey, ht);
-            for (String key : defaultEditorKitMap.keySet()) {
-                registerEditorKitForContentType(key,defaultEditorKitMap.get(key));
-            }
+    static final Map defaultEditorKitMap = new HashMap(0);
 
+    static {
+        defaultEditorKitMap.put("text/plain",
+                                "javax.swing.JEditorPane$PlainEditorKit");
+        defaultEditorKitMap.put("text/html",
+                                "javax.swing.text.html.HTMLEditorKit");
+        defaultEditorKitMap.put("text/rtf",
+                                "javax.swing.text.rtf.RTFEditorKit");
+        defaultEditorKitMap.put("application/rtf",
+                                "javax.swing.text.rtf.RTFEditorKit");
+        for (String key : defaultEditorKitMap.keySet()) {
+            registerEditorKitForContentType(key,defaultEditorKitMap.get(key));
         }
     }
 
@@ -1587,16 +1550,6 @@ public class JEditorPane extends JTextComponent {
      */
     private Hashtable typeHandlers;
 
-    /*
-     * Private AppContext keys for this class's static variables.
-     */
-    private static final Object kitRegistryKey =
-        new StringBuffer("JEditorPane.kitRegistry");
-    private static final Object kitTypeRegistryKey =
-        new StringBuffer("JEditorPane.kitTypeRegistry");
-    private static final Object kitLoaderRegistryKey =
-        new StringBuffer("JEditorPane.kitLoaderRegistry");
-
     /**
      * @see #getUIClassID
      * @see #readObject
@@ -1633,8 +1586,6 @@ public class JEditorPane extends JTextComponent {
      */
     public static final String HONOR_DISPLAY_PROPERTIES = "JEditorPane.honorDisplayProperties";
 
-    static final Map defaultEditorKitMap = new HashMap(0);
-
     /**
      * Returns a string representation of this JEditorPane.
      * This method

From fcc2a2922fe0312758a9eef5f1ea371e5803bc8b Mon Sep 17 00:00:00 2001
From: Phil Race 
Date: Thu, 26 Feb 2026 16:23:49 +0000
Subject: [PATCH 068/636] 8378297: Remove AppContext from several Swing
 component and related classes

Reviewed-by: azvegint, psadhukhan, dnguyen
---
 .../classes/javax/swing/DebugGraphics.java    | 16 ++--
 .../share/classes/javax/swing/JDialog.java    | 24 ++----
 .../share/classes/javax/swing/JFrame.java     | 24 ++----
 .../share/classes/javax/swing/JPopupMenu.java | 20 +----
 .../classes/javax/swing/PopupFactory.java     | 63 ++++++----------
 .../classes/javax/swing/SwingUtilities.java   | 20 ++---
 .../classes/javax/swing/ToolTipManager.java   | 13 ++--
 .../swing/ToolTipManager/Test6657026.java     | 75 -------------------
 8 files changed, 57 insertions(+), 198 deletions(-)
 delete mode 100644 test/jdk/javax/swing/ToolTipManager/Test6657026.java

diff --git a/src/java.desktop/share/classes/javax/swing/DebugGraphics.java b/src/java.desktop/share/classes/javax/swing/DebugGraphics.java
index 83b44716c04..512b56ee28a 100644
--- a/src/java.desktop/share/classes/javax/swing/DebugGraphics.java
+++ b/src/java.desktop/share/classes/javax/swing/DebugGraphics.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -1492,14 +1492,12 @@ public class DebugGraphics extends Graphics {
     /** Returns DebugGraphicsInfo, or creates one if none exists.
       */
     static DebugGraphicsInfo info() {
-        DebugGraphicsInfo debugGraphicsInfo = (DebugGraphicsInfo)
-            SwingUtilities.appContextGet(debugGraphicsInfoKey);
-        if (debugGraphicsInfo == null) {
-            debugGraphicsInfo = new DebugGraphicsInfo();
-            SwingUtilities.appContextPut(debugGraphicsInfoKey,
-                                         debugGraphicsInfo);
+        synchronized (DebugGraphicsInfo.class) {
+            if (debugGraphicsInfo == null) {
+                debugGraphicsInfo = new DebugGraphicsInfo();
+            }
+            return debugGraphicsInfo;
         }
-        return debugGraphicsInfo;
     }
-    private static final Class debugGraphicsInfoKey = DebugGraphicsInfo.class;
+    private static DebugGraphicsInfo debugGraphicsInfo;
 }
diff --git a/src/java.desktop/share/classes/javax/swing/JDialog.java b/src/java.desktop/share/classes/javax/swing/JDialog.java
index 99d6385cd7c..a7d8791c72c 100644
--- a/src/java.desktop/share/classes/javax/swing/JDialog.java
+++ b/src/java.desktop/share/classes/javax/swing/JDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -101,13 +101,6 @@ public class JDialog extends Dialog implements WindowConstants,
                                                RootPaneContainer,
                                TransferHandler.HasGetTransferHandler
 {
-    /**
-     * Key into the AppContext, used to check if should provide decorations
-     * by default.
-     */
-    private static final Object defaultLookAndFeelDecoratedKey =
-            new StringBuffer("JDialog.defaultLookAndFeelDecorated");
-
     private int defaultCloseOperation = HIDE_ON_CLOSE;
 
     /**
@@ -1119,6 +1112,8 @@ public class JDialog extends Dialog implements WindowConstants,
         }
     }
 
+    private static boolean defaultLAFDecorated;
+
     /**
      * Provides a hint as to whether or not newly created {@code JDialog}s
      * should have their Window decorations (such as borders, widgets to
@@ -1144,11 +1139,7 @@ public class JDialog extends Dialog implements WindowConstants,
      * @since 1.4
      */
     public static void setDefaultLookAndFeelDecorated(boolean defaultLookAndFeelDecorated) {
-        if (defaultLookAndFeelDecorated) {
-            SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.TRUE);
-        } else {
-            SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.FALSE);
-        }
+        defaultLAFDecorated = defaultLookAndFeelDecorated;
     }
 
     /**
@@ -1160,12 +1151,7 @@ public class JDialog extends Dialog implements WindowConstants,
      * @since 1.4
      */
     public static boolean isDefaultLookAndFeelDecorated() {
-        Boolean defaultLookAndFeelDecorated =
-            (Boolean) SwingUtilities.appContextGet(defaultLookAndFeelDecoratedKey);
-        if (defaultLookAndFeelDecorated == null) {
-            defaultLookAndFeelDecorated = Boolean.FALSE;
-        }
-        return defaultLookAndFeelDecorated.booleanValue();
+        return defaultLAFDecorated;
     }
 
     /**
diff --git a/src/java.desktop/share/classes/javax/swing/JFrame.java b/src/java.desktop/share/classes/javax/swing/JFrame.java
index 8bde7e18f03..bab215625b9 100644
--- a/src/java.desktop/share/classes/javax/swing/JFrame.java
+++ b/src/java.desktop/share/classes/javax/swing/JFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -126,13 +126,6 @@ public class JFrame  extends Frame implements WindowConstants,
                                               RootPaneContainer,
                               TransferHandler.HasGetTransferHandler
 {
-    /**
-     * Key into the AppContext, used to check if should provide decorations
-     * by default.
-     */
-    private static final Object defaultLookAndFeelDecoratedKey =
-            new StringBuffer("JFrame.defaultLookAndFeelDecorated");
-
     private int defaultCloseOperation = HIDE_ON_CLOSE;
 
     /**
@@ -755,6 +748,8 @@ public class JFrame  extends Frame implements WindowConstants,
         }
     }
 
+    private static boolean defaultLAFDecorated;
+
     /**
      * Provides a hint as to whether or not newly created JFrames
      * should have their Window decorations (such as borders, widgets to
@@ -780,11 +775,7 @@ public class JFrame  extends Frame implements WindowConstants,
      * @since 1.4
      */
     public static void setDefaultLookAndFeelDecorated(boolean defaultLookAndFeelDecorated) {
-        if (defaultLookAndFeelDecorated) {
-            SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.TRUE);
-        } else {
-            SwingUtilities.appContextPut(defaultLookAndFeelDecoratedKey, Boolean.FALSE);
-        }
+        defaultLAFDecorated = defaultLookAndFeelDecorated;
     }
 
 
@@ -797,12 +788,7 @@ public class JFrame  extends Frame implements WindowConstants,
      * @since 1.4
      */
     public static boolean isDefaultLookAndFeelDecorated() {
-        Boolean defaultLookAndFeelDecorated =
-            (Boolean) SwingUtilities.appContextGet(defaultLookAndFeelDecoratedKey);
-        if (defaultLookAndFeelDecorated == null) {
-            defaultLookAndFeelDecorated = Boolean.FALSE;
-        }
-        return defaultLookAndFeelDecorated.booleanValue();
+        return defaultLAFDecorated;
     }
 
     /**
diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java
index 1b04e8c6169..2ecc7cf3b1e 100644
--- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java
+++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java
@@ -112,12 +112,6 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement {
      */
     private static final String uiClassID = "PopupMenuUI";
 
-    /**
-     * Key used in AppContext to determine if light way popups are the default.
-     */
-    private static final Object defaultLWPopupEnabledKey =
-        new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
-
     /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
     static boolean popupPositionFixDisabled =
          System.getProperty("javax.swing.adjustPopupLocationToFit","").equals("false");
@@ -153,6 +147,8 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement {
     private static final boolean VERBOSE = false; // show reuse hits/misses
     private static final boolean DEBUG =   false;  // show bad params, misc.
 
+    private static boolean defaultLWPopupEnabled = true;
+
     /**
      *  Sets the default value of the lightWeightPopupEnabled
      *  property.
@@ -163,8 +159,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement {
      *  @see #setLightWeightPopupEnabled
      */
     public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
-        SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
-                                     Boolean.valueOf(aFlag));
+        defaultLWPopupEnabled = aFlag;
     }
 
     /**
@@ -177,14 +172,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement {
      *  @see #setDefaultLightWeightPopupEnabled
      */
     public static boolean getDefaultLightWeightPopupEnabled() {
-        Boolean b = (Boolean)
-            SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
-        if (b == null) {
-            SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
-                                         Boolean.TRUE);
-            return true;
-        }
-        return b.booleanValue();
+        return defaultLWPopupEnabled;
     }
 
     /**
diff --git a/src/java.desktop/share/classes/javax/swing/PopupFactory.java b/src/java.desktop/share/classes/javax/swing/PopupFactory.java
index 2b274c816b1..208177fc55e 100644
--- a/src/java.desktop/share/classes/javax/swing/PopupFactory.java
+++ b/src/java.desktop/share/classes/javax/swing/PopupFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -80,13 +80,6 @@ public class PopupFactory {
             }
         });
     }
-    /**
-     * The shared instanceof PopupFactory is per
-     * AppContext. This is the key used in the
-     * AppContext to locate the PopupFactory.
-     */
-    private static final Object SharedInstanceKey =
-        new StringBuffer("PopupFactory.SharedInstanceKey");
 
     /**
      * Max number of items to store in any one particular cache.
@@ -118,6 +111,7 @@ public class PopupFactory {
      */
     public PopupFactory() {}
 
+    static PopupFactory sharedFactory;
     /**
      * Sets the PopupFactory that will be used to obtain
      * Popups.
@@ -132,7 +126,9 @@ public class PopupFactory {
         if (factory == null) {
             throw new IllegalArgumentException("PopupFactory can not be null");
         }
-        SwingUtilities.appContextPut(SharedInstanceKey, factory);
+        synchronized (PopupFactory.class) {
+            sharedFactory = factory;
+        }
     }
 
     /**
@@ -142,14 +138,12 @@ public class PopupFactory {
      * @return Shared PopupFactory
      */
     public static PopupFactory getSharedInstance() {
-        PopupFactory factory = (PopupFactory)SwingUtilities.appContextGet(
-                         SharedInstanceKey);
-
-        if (factory == null) {
-            factory = new PopupFactory();
-            setSharedInstance(factory);
+        synchronized (PopupFactory.class) {
+            if (sharedFactory == null) {
+                sharedFactory = new PopupFactory();
+            }
+            return sharedFactory;
         }
-        return factory;
     }
 
 
@@ -351,8 +345,6 @@ public class PopupFactory {
      * Popup implementation that uses a Window as the popup.
      */
     private static class HeavyWeightPopup extends Popup {
-        private static final Object heavyWeightPopupCacheKey =
-                 new StringBuffer("PopupFactory.heavyWeightPopupCache");
 
         private volatile boolean isCacheEnabled = true;
 
@@ -434,21 +426,16 @@ public class PopupFactory {
             }
         }
 
+        private static Map> cache;
         /**
          * Returns the cache to use for heavy weight popups. Maps from
          * Window to a List of
          * HeavyWeightPopups.
          */
-        @SuppressWarnings("unchecked")
         private static Map> getHeavyWeightPopupCache() {
             synchronized (HeavyWeightPopup.class) {
-                Map> cache = (Map>)SwingUtilities.appContextGet(
-                                  heavyWeightPopupCacheKey);
-
                 if (cache == null) {
                     cache = new HashMap<>(2);
-                    SwingUtilities.appContextPut(heavyWeightPopupCacheKey,
-                                                 cache);
                 }
                 return cache;
             }
@@ -728,18 +715,17 @@ public class PopupFactory {
             return popup;
         }
 
+        private static List cache;
         /**
          * Returns the cache to use for heavy weight popups.
          */
-        @SuppressWarnings("unchecked")
         private static List getLightWeightPopupCache() {
-            List cache = (List)SwingUtilities.appContextGet(
-                                   lightWeightPopupCacheKey);
-            if (cache == null) {
-                cache = new ArrayList<>();
-                SwingUtilities.appContextPut(lightWeightPopupCacheKey, cache);
+            synchronized (LightWeightPopup.class) {
+                if (cache == null) {
+                    cache = new ArrayList<>();
+                }
+                return cache;
             }
-            return cache;
         }
 
         /**
@@ -849,8 +835,6 @@ public class PopupFactory {
      * Popup implementation that uses a Panel as the popup.
      */
     private static class MediumWeightPopup extends ContainerPopup {
-        private static final Object mediumWeightPopupCacheKey =
-                             new StringBuffer("PopupFactory.mediumPopupCache");
 
         /** Child of the panel. The contents are added to this. */
         private JRootPane rootPane;
@@ -877,19 +861,18 @@ public class PopupFactory {
             return popup;
         }
 
+        private static List cache;
         /**
          * Returns the cache to use for medium weight popups.
          */
         @SuppressWarnings("unchecked")
         private static List getMediumWeightPopupCache() {
-            List cache = (List)SwingUtilities.appContextGet(
-                                    mediumWeightPopupCacheKey);
-
-            if (cache == null) {
-                cache = new ArrayList<>();
-                SwingUtilities.appContextPut(mediumWeightPopupCacheKey, cache);
+            synchronized (MediumWeightPopup.class) {
+                if (cache == null) {
+                    cache = new ArrayList<>();
+                }
+                return cache;
             }
-            return cache;
         }
 
         /**
diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java
index bc6441212de..afe1c444c31 100644
--- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java
+++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -1899,11 +1899,6 @@ public class SwingUtilities implements SwingConstants
         return null;
     }
 
-
-    // Don't use String, as it's not guaranteed to be unique in a Hashtable.
-    private static final Object sharedOwnerFrameKey =
-       new StringBuffer("SwingUtilities.sharedOwnerFrame");
-
     @SuppressWarnings("serial") // JDK-implementation class
     static class SharedOwnerFrame extends Frame implements WindowListener {
         public void addNotify() {
@@ -1961,6 +1956,7 @@ public class SwingUtilities implements SwingConstants
         }
     }
 
+    private static Frame sharedOwnerFrame;
     /**
      * Returns a toolkit-private, shared, invisible Frame
      * to be the owner for JDialogs and JWindows created with
@@ -1970,14 +1966,12 @@ public class SwingUtilities implements SwingConstants
      * @see java.awt.GraphicsEnvironment#isHeadless
      */
     static Frame getSharedOwnerFrame() throws HeadlessException {
-        Frame sharedOwnerFrame =
-            (Frame)SwingUtilities.appContextGet(sharedOwnerFrameKey);
-        if (sharedOwnerFrame == null) {
-            sharedOwnerFrame = new SharedOwnerFrame();
-            SwingUtilities.appContextPut(sharedOwnerFrameKey,
-                                         sharedOwnerFrame);
+        synchronized (SharedOwnerFrame.class) {
+            if (sharedOwnerFrame == null) {
+                sharedOwnerFrame = new SharedOwnerFrame();
+            }
+            return sharedOwnerFrame;
         }
-        return sharedOwnerFrame;
     }
 
     /**
diff --git a/src/java.desktop/share/classes/javax/swing/ToolTipManager.java b/src/java.desktop/share/classes/javax/swing/ToolTipManager.java
index a9e2c6e69bb..4c3d0209823 100644
--- a/src/java.desktop/share/classes/javax/swing/ToolTipManager.java
+++ b/src/java.desktop/share/classes/javax/swing/ToolTipManager.java
@@ -61,7 +61,6 @@ public final class ToolTipManager extends MouseAdapter implements MouseMotionLis
     JComponent insideComponent;
     MouseEvent mouseEvent;
     boolean showImmediately;
-    private static final Object TOOL_TIP_MANAGER_KEY = new Object();
     transient Popup tipWindow;
     /** The Window tip is being displayed in. This will be non-null if
      * the Window tip is in differs from that of insideComponent's Window.
@@ -394,19 +393,19 @@ public final class ToolTipManager extends MouseAdapter implements MouseMotionLis
         }
     }
 
+    private static ToolTipManager manager;
     /**
      * Returns a shared ToolTipManager instance.
      *
      * @return a shared ToolTipManager object
      */
     public static ToolTipManager sharedInstance() {
-        Object value = SwingUtilities.appContextGet(TOOL_TIP_MANAGER_KEY);
-        if (value instanceof ToolTipManager) {
-            return (ToolTipManager) value;
+        synchronized(ToolTipManager.class) {
+            if (manager == null) {
+                manager = new ToolTipManager();
+            }
+            return manager;
         }
-        ToolTipManager manager = new ToolTipManager();
-        SwingUtilities.appContextPut(TOOL_TIP_MANAGER_KEY, manager);
-        return manager;
     }
 
     // add keylistener here to trigger tip for access
diff --git a/test/jdk/javax/swing/ToolTipManager/Test6657026.java b/test/jdk/javax/swing/ToolTipManager/Test6657026.java
deleted file mode 100644
index 0678d57f768..00000000000
--- a/test/jdk/javax/swing/ToolTipManager/Test6657026.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2009, 2015, 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 6657026
- * @summary Tests shared ToolTipManager in different application contexts
- * @author Sergey Malenkov
- * @modules java.desktop/sun.awt
- */
-
-import sun.awt.SunToolkit;
-import javax.swing.ToolTipManager;
-
-public class Test6657026 implements Runnable {
-
-    private static final int DISMISS = 4000;
-    private static final int INITIAL = 750;
-    private static final int RESHOW = 500;
-
-    public static void main(String[] args) throws InterruptedException {
-        ToolTipManager manager = ToolTipManager.sharedInstance();
-        if (DISMISS != manager.getDismissDelay()) {
-            throw new Error("unexpected dismiss delay");
-        }
-        if (INITIAL != manager.getInitialDelay()) {
-            throw new Error("unexpected initial delay");
-        }
-        if (RESHOW != manager.getReshowDelay()) {
-            throw new Error("unexpected reshow delay");
-        }
-        manager.setDismissDelay(DISMISS + 1);
-        manager.setInitialDelay(INITIAL + 1);
-        manager.setReshowDelay(RESHOW + 1);
-
-        ThreadGroup group = new ThreadGroup("$$$");
-        Thread thread = new Thread(group, new Test6657026());
-        thread.start();
-        thread.join();
-    }
-
-    public void run() {
-        SunToolkit.createNewAppContext();
-        ToolTipManager manager = ToolTipManager.sharedInstance();
-        if (DISMISS != manager.getDismissDelay()) {
-            throw new Error("shared dismiss delay");
-        }
-        if (INITIAL != manager.getInitialDelay()) {
-            throw new Error("shared initial delay");
-        }
-        if (RESHOW != manager.getReshowDelay()) {
-            throw new Error("shared reshow delay");
-        }
-    }
-}

From 8b805630b4c54a8e9e489cff08f0260cd42dc362 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= 
Date: Thu, 26 Feb 2026 16:33:51 +0000
Subject: [PATCH 069/636] 8376477: Avoid loading empty Lock classes in Shutdown
 and ReferenceQueue

Reviewed-by: rriggs, shade
---
 src/java.base/share/classes/java/lang/Shutdown.java        | 7 +++----
 .../share/classes/java/lang/ref/ReferenceQueue.java        | 5 ++---
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/java.base/share/classes/java/lang/Shutdown.java b/src/java.base/share/classes/java/lang/Shutdown.java
index 87c4732a5ce..a9d4d6a28a9 100644
--- a/src/java.base/share/classes/java/lang/Shutdown.java
+++ b/src/java.base/share/classes/java/lang/Shutdown.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -55,11 +55,10 @@ class Shutdown {
     private static int currentRunningHook = -1;
 
     /* The preceding static fields are protected by this lock */
-    private static class Lock { };
-    private static Object lock = new Lock();
+    private static final Object lock = new Object();
 
     /* Lock object for the native halt method */
-    private static Object haltLock = new Lock();
+    private static final Object haltLock = new Object();
 
     /**
      * Add a new system shutdown hook.  Checks the shutdown state and
diff --git a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java
index d3879d4a8fc..ee1892e8878 100644
--- a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java
+++ b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -60,8 +60,7 @@ public class ReferenceQueue<@jdk.internal.RequiresIdentity T> {
     private volatile Reference head;
     private long queueLength = 0;
 
-    private static class Lock { };
-    private final Lock lock = new Lock();
+    private final Object lock = new Object();
 
     /**
      * Constructs a new reference-object queue.

From aa6c06e1665cd44ae880824aedb3c861f0951cb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= 
Date: Thu, 26 Feb 2026 18:50:45 +0000
Subject: [PATCH 070/636] 8309748: Improve host selection in `External
 Specifications` page

Reviewed-by: nbenalla
---
 .../share/classes/java/lang/Character.java    | 14 ++---
 .../formats/html/ExternalSpecsWriter.java     | 62 +++++++++++++++++--
 .../html/resources/standard.properties        |  5 +-
 .../formats/html/resources/stylesheet.css     |  4 +-
 .../doclet/testSpecTag/TestSpecTag.java       | 43 +++++++++----
 5 files changed, 98 insertions(+), 30 deletions(-)

diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java
index ffda729a45a..33284d86e2d 100644
--- a/src/java.base/share/classes/java/lang/Character.java
+++ b/src/java.base/share/classes/java/lang/Character.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -11233,7 +11233,7 @@ class Character implements java.io.Serializable, Comparable, Constabl
      * @param   codePoint the character (Unicode code point) to be tested.
      * @return  {@code true} if the character is an Emoji;
      *          {@code false} otherwise.
-     * @spec https://unicode.org/reports/tr51/ Unicode Emoji
+     * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
      * @since   21
      */
     public static boolean isEmoji(int codePoint) {
@@ -11252,7 +11252,7 @@ class Character implements java.io.Serializable, Comparable, Constabl
      * @param   codePoint the character (Unicode code point) to be tested.
      * @return  {@code true} if the character has the Emoji Presentation
      *          property; {@code false} otherwise.
-     * @spec https://unicode.org/reports/tr51/ Unicode Emoji
+     * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
      * @since   21
      */
     public static boolean isEmojiPresentation(int codePoint) {
@@ -11271,7 +11271,7 @@ class Character implements java.io.Serializable, Comparable, Constabl
      * @param   codePoint the character (Unicode code point) to be tested.
      * @return  {@code true} if the character is an Emoji Modifier;
      *          {@code false} otherwise.
-     * @spec https://unicode.org/reports/tr51/ Unicode Emoji
+     * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
      * @since   21
      */
     public static boolean isEmojiModifier(int codePoint) {
@@ -11290,7 +11290,7 @@ class Character implements java.io.Serializable, Comparable, Constabl
      * @param   codePoint the character (Unicode code point) to be tested.
      * @return  {@code true} if the character is an Emoji Modifier Base;
      *          {@code false} otherwise.
-     * @spec https://unicode.org/reports/tr51/ Unicode Emoji
+     * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
      * @since   21
      */
     public static boolean isEmojiModifierBase(int codePoint) {
@@ -11309,7 +11309,7 @@ class Character implements java.io.Serializable, Comparable, Constabl
      * @param   codePoint the character (Unicode code point) to be tested.
      * @return  {@code true} if the character is an Emoji Component;
      *          {@code false} otherwise.
-     * @spec https://unicode.org/reports/tr51/ Unicode Emoji
+     * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
      * @since   21
      */
     public static boolean isEmojiComponent(int codePoint) {
@@ -11328,7 +11328,7 @@ class Character implements java.io.Serializable, Comparable, Constabl
      * @param   codePoint the character (Unicode code point) to be tested.
      * @return  {@code true} if the character is an Extended Pictographic;
      *          {@code false} otherwise.
-     * @spec https://unicode.org/reports/tr51/ Unicode Emoji
+     * @spec https://www.unicode.org/reports/tr51/ Unicode Emoji
      * @since   21
      */
     public static boolean isExtendedPictographic(int codePoint) {
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java
index 0115de5558f..b8767dd9913 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2024, 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
@@ -57,7 +57,11 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
 import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
 import jdk.javadoc.internal.html.Content;
 import jdk.javadoc.internal.html.ContentBuilder;
+import jdk.javadoc.internal.html.HtmlAttr;
+import jdk.javadoc.internal.html.HtmlId;
+import jdk.javadoc.internal.html.HtmlTag;
 import jdk.javadoc.internal.html.HtmlTree;
+import jdk.javadoc.internal.html.Script;
 import jdk.javadoc.internal.html.Text;
 
 import static java.util.stream.Collectors.groupingBy;
@@ -108,6 +112,24 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
                         HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING,
                                 contents.getContent("doclet.External_Specifications"))))
                 .addMainContent(mainContent)
+                .addMainContent(new Script("""
+                        let select = document.getElementById('specs-by-domain');
+                        select.addEventListener("change", selectHost);
+                        addEventListener("pageshow", selectHost);
+                        function selectHost() {
+                            const selectedClass = select.value ? "external-specs-tab" + select.value : "external-specs";
+                            let tabPanel = document.getElementById("external-specs.tabpanel");
+                            let count = 0;
+                            tabPanel.querySelectorAll("div.external-specs").forEach(function(elem) {
+                                elem.style.display = elem.classList.contains(selectedClass) ? "" : "none";
+                                if (elem.style.display === "") {
+                                    let isEvenRow = count++ % 4 < 2;
+                                    toggleStyle(elem.classList, isEvenRow, evenRowColor, oddRowColor);
+                                }
+                            });
+                        }
+                        selectHost();
+                        """).asContent())
                 .setFooter(getFooter()));
         printHtmlDocument(null, "external specifications", body);
 
@@ -180,7 +202,7 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
         boolean noHost = false;
         for (var searchIndexItems : searchIndexMap.values()) {
             try {
-                URI uri = getSpecURI(searchIndexItems.get(0));
+                URI uri = getSpecURI(searchIndexItems.getFirst());
                 String host = uri.getHost();
                 if (host != null) {
                     hostNamesSet.add(host);
@@ -191,14 +213,19 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
                 // ignore
             }
         }
-        var hostNamesList = new ArrayList<>(hostNamesSet);
 
         var table = new Table(HtmlStyles.summaryTable)
                 .setCaption(contents.externalSpecifications)
                 .setHeader(new TableHeader(contents.specificationLabel, contents.referencedIn))
                 .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast)
-                .setId(HtmlIds.EXTERNAL_SPECS);
+                .setId(HtmlIds.EXTERNAL_SPECS)
+                .setDefaultTab(contents.externalSpecifications)
+                .setRenderTabs(false);
+
+        var hostNamesList = new ArrayList<>(hostNamesSet);
+        Content selector = Text.EMPTY;
         if ((hostNamesList.size() + (noHost ? 1 : 0)) > 1) {
+            selector = createHostSelect(hostNamesList, noHost);
             for (var host : hostNamesList) {
                 table.addTab(Text.of(host), u -> host.equals(u.getHost()));
             }
@@ -207,10 +234,9 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
                         u -> u.getHost() == null);
             }
         }
-        table.setDefaultTab(Text.of(resources.getText("doclet.External_Specifications.All_Specifications")));
 
         for (List searchIndexItems : searchIndexMap.values()) {
-            IndexItem ii = searchIndexItems.get(0);
+            IndexItem ii = searchIndexItems.getFirst();
             Content specName = createSpecLink(ii);
             Content referencesList = HtmlTree.UL(HtmlStyles.refList, searchIndexItems,
                     item -> HtmlTree.LI(createLink(item)));
@@ -227,6 +253,7 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
                 table.addRow(specName, references);
             }
         }
+        content.add(selector);
         content.add(table);
     }
 
@@ -235,6 +262,29 @@ public class ExternalSpecsWriter extends HtmlDocletWriter {
                 .collect(groupingBy(IndexItem::getLabel, () -> new TreeMap<>(getTitleComparator()), toList()));
     }
 
+    private Content createHostSelect(List hosts, boolean hasLocal) {
+        var index = 1;
+        var id = HtmlId.of("specs-by-domain");
+        var specsByHost = resources.getText("doclet.External_Specifications.by-host");
+        var select = HtmlTree.of(HtmlTag.SELECT)
+                .setId(id)
+                .add(HtmlTree.of(HtmlTag.OPTION)
+                        .put(HtmlAttr.VALUE, "")
+                        .add(Text.of(resources.getText("doclet.External_Specifications.all-hosts"))));
+
+        for (var host : hosts) {
+            select.add(HtmlTree.of(HtmlTag.OPTION)
+                    .put(HtmlAttr.VALUE, Integer.toString(index++))
+                    .add(Text.of(host)));
+        }
+        if (hasLocal) {
+            select.add(HtmlTree.of(HtmlTag.OPTION)
+                    .put(HtmlAttr.VALUE, Integer.toString(index))
+                    .add(Text.of("Local")));
+        }
+        return new ContentBuilder(HtmlTree.LABEL(id.name(), Text.of(specsByHost)), Text.of(" "), select);
+    }
+
     Comparator getTitleComparator() {
         Collator collator = Collator.getInstance();
         return (s1, s2) -> {
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties
index 4366295477b..1aba5c9862b 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 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
@@ -180,7 +180,8 @@ doclet.Inheritance_Tree=Inheritance Tree
 doclet.DefinedIn=Defined In
 doclet.ReferencedIn=Referenced In
 doclet.External_Specifications=External Specifications
-doclet.External_Specifications.All_Specifications=All Specifications
+doclet.External_Specifications.by-host=Show specifications by host name:
+doclet.External_Specifications.all-hosts=All host names
 doclet.External_Specifications.no-host=Local
 doclet.Specification=Specification
 doclet.Summary_Page=Summary Page
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css
index 6198df5c2f3..5bd14f7cf33 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
@@ -1228,7 +1228,7 @@ input::placeholder {
 input:focus::placeholder {
     color: transparent;
 }
-select#search-modules {
+select {
     margin: 0 10px 10px 2px;
     font-size: var(--nav-font-size);
     padding: 3px 5px;
diff --git a/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java b/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java
index e914f8022fd..1c3e8306d7b 100644
--- a/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java
+++ b/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 6251738 8226279 8297802 8305407
+ * @bug 6251738 8226279 8297802 8305407 8309748
  * @summary JDK-8226279 javadoc should support a new at-spec tag
  * @library /tools/lib ../../lib
  * @modules jdk.javadoc/jdk.javadoc.internal.tool
@@ -343,16 +343,15 @@ public class TestSpecTag extends JavadocTester {
 
         checkOutput("external-specs.html", true,
                 """
-                    
\ - \ - \ -
+ +
+
+
External Specifications
+
Specification
@@ -369,7 +368,25 @@ public class TestSpecTag extends JavadocTester { -
"""); +
""", + """ + """); } @Test From 5e85d99c360fa12042a42fb3ed8ceb50c733d7a0 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 26 Feb 2026 20:17:08 +0000 Subject: [PATCH 071/636] 8378715: Use early field initialization for java.lang.invoke generated code Reviewed-by: jvernee --- .../java/lang/invoke/ClassSpecializer.java | 16 +++++++--------- .../lang/invoke/InnerClassLambdaMetafactory.java | 8 ++++---- .../java/lang/invoke/MethodHandleProxies.java | 12 ++++++------ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java index 4b6d74b1b2e..be3805cf5b1 100644 --- a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java +++ b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -746,15 +746,7 @@ abstract class ClassSpecializer.SpeciesDat new Consumer<>() { @Override public void accept(CodeBuilder cob) { - cob.aload(0); // this - final List ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList()); - for (Var ca : ctorArgs) { - ca.emitLoadInstruction(cob); - } - - // super(ca...) - cob.invokespecial(superClassDesc, INIT_NAME, methodDesc(superCtorType)); // store down fields Var lastFV = AFTER_THIS.lastOf(ctorArgs); @@ -766,6 +758,12 @@ abstract class ClassSpecializer.SpeciesDat cob.putfield(classDesc, f.name, f.desc); } + // super(ca...) + cob.aload(0); // this + for (Var ca : ctorArgs) { + ca.emitLoadInstruction(cob); + } + cob.invokespecial(superClassDesc, INIT_NAME, methodDesc(superCtorType)); cob.return_(); } }); diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 4dac59771e8..d5cfc6f11a2 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -391,15 +391,15 @@ import sun.invoke.util.Wrapper; new Consumer<>() { @Override public void accept(CodeBuilder cob) { - cob.aload(0) - .invokespecial(CD_Object, INIT_NAME, MTD_void); int parameterCount = factoryType.parameterCount(); for (int i = 0; i < parameterCount; i++) { cob.aload(0) .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i)) .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); } - cob.return_(); + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void) + .return_(); } }); } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index 70592351827..16f5c7e59b8 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -362,10 +362,8 @@ public final class MethodHandleProxies { // (Lookup, MethodHandle target, MethodHandle callerBoundTarget) clb.withMethodBody(INIT_NAME, MTD_void_Lookup_MethodHandle_MethodHandle, 0, cob -> { - cob.aload(0) - .invokespecial(CD_Object, INIT_NAME, MTD_void) - // call ensureOriginalLookup to verify the given Lookup has access - .aload(1) + // call ensureOriginalLookup to verify the given Lookup has access + cob.aload(1) .invokestatic(proxyDesc, ENSURE_ORIGINAL_LOOKUP, MTD_void_Lookup) // this.target = target; .aload(0) @@ -383,7 +381,9 @@ public final class MethodHandleProxies { } // complete - cob.return_(); + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void) + .return_(); }); // private static void ensureOriginalLookup(Lookup) checks if the given Lookup From cd462a88c62fd07fe213f442fc3989c78313a274 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 26 Feb 2026 20:29:51 +0000 Subject: [PATCH 072/636] 8378385: Remove AppContext from AWT Windows implementation classes Reviewed-by: dnguyen, serb --- .../sun/awt/windows/WComponentPeer.java | 4 +-- .../sun/awt/windows/WEmbeddedFrame.java | 5 ++-- .../classes/sun/awt/windows/WInputMethod.java | 7 ++--- .../sun/awt/windows/WMenuItemPeer.java | 4 +-- .../classes/sun/awt/windows/WToolkit.java | 30 ++----------------- .../sun/awt/windows/WTrayIconPeer.java | 4 +-- 6 files changed, 14 insertions(+), 40 deletions(-) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java index 1b3fb9a85e9..00ad60c8bb3 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.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 @@ -902,7 +902,7 @@ public abstract class WComponentPeer extends WObjectPeer */ void postEvent(AWTEvent event) { preprocessPostEvent(event); - WToolkit.postEvent(WToolkit.targetToAppContext(target), event); + WToolkit.postEvent(event); } void preprocessPostEvent(AWTEvent event) {} diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java index 9a74a1c25f7..0bd44d8ca85 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.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 @@ -238,8 +238,7 @@ public final class WEmbeddedFrame extends EmbeddedFrame { peer.emulateActivation(true); } }; - WToolkit.postEvent(WToolkit.targetToAppContext(this), - new InvocationEvent(this, r)); + WToolkit.postEvent(new InvocationEvent(this, r)); } } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java index a9237e21ff6..50ad23fabcd 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -602,7 +602,7 @@ final class WInputMethod extends InputMethodAdapter commitedTextLength, TextHitInfo.leading(caretPos), TextHitInfo.leading(visiblePos)); - WToolkit.postEvent(WToolkit.targetToAppContext(source), event); + WToolkit.postEvent(event); } public void inquireCandidatePosition() @@ -641,8 +641,7 @@ final class WInputMethod extends InputMethodAdapter openCandidateWindow(awtFocussedComponentPeer, x, y); } }; - WToolkit.postEvent(WToolkit.targetToAppContext(source), - new InvocationEvent(source, r)); + WToolkit.postEvent(new InvocationEvent(source, r)); } // java.awt.Toolkit#getNativeContainer() is not available diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java index 4abb9fa84ae..2594bd38a7d 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.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 @@ -123,7 +123,7 @@ class WMenuItemPeer extends WObjectPeer implements MenuItemPeer { * Post an event. Queue it for execution by the callback thread. */ void postEvent(AWTEvent event) { - WToolkit.postEvent(WToolkit.targetToAppContext(target), event); + WToolkit.postEvent(event); } native void create(WMenuPeer parent); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java index 0fdc4c6005b..4ed3e6b7e68 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.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 @@ -123,7 +123,6 @@ import javax.swing.text.JTextComponent; import sun.awt.AWTAccessor; import sun.awt.AWTAutoShutdown; -import sun.awt.AppContext; import sun.awt.DisplayChangedListener; import sun.awt.LightweightFrame; import sun.awt.SunToolkit; @@ -777,20 +776,7 @@ public final class WToolkit extends SunToolkit implements Runnable { ((DisplayChangedListener) lge).displayChanged(); } }; - if (AppContext.getAppContext() != null) { - // Common case, standalone application - EventQueue.invokeLater(runnable); - } else { - if (displayChangeExecutor == null) { - // No synchronization, called on the Toolkit thread only - displayChangeExecutor = Executors.newFixedThreadPool(1, r -> { - Thread t = Executors.defaultThreadFactory().newThread(r); - t.setDaemon(true); - return t; - }); - } - displayChangeExecutor.submit(runnable); - } + EventQueue.invokeLater(runnable); } /** @@ -910,17 +896,7 @@ public final class WToolkit extends SunToolkit implements Runnable { } updateXPStyleEnabled(props.get(XPSTYLE_THEME_ACTIVE)); - - if (AppContext.getAppContext() == null) { - // We cannot post the update to any EventQueue. Listeners will - // be called on EDTs by DesktopPropertyChangeSupport - updateProperties(props); - } else { - // Cannot update on Toolkit thread. - // DesktopPropertyChangeSupport will call listeners on Toolkit - // thread if it has AppContext (standalone mode) - EventQueue.invokeLater(() -> updateProperties(props)); - } + EventQueue.invokeLater(() -> updateProperties(props)); } private synchronized void updateProperties(final Map props) { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java index 7cf92292a32..5c04803669d 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.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 @@ -181,7 +181,7 @@ final class WTrayIconPeer extends WObjectPeer implements TrayIconPeer { } void postEvent(AWTEvent event) { - WToolkit.postEvent(WToolkit.targetToAppContext(target), event); + WToolkit.postEvent(event); } native void create(); From 871730aa9b2c799e8a5ff9e6c1d3b507d16c0c6b Mon Sep 17 00:00:00 2001 From: Kirill Shirokov Date: Thu, 26 Feb 2026 21:43:46 +0000 Subject: [PATCH 073/636] 8377862: Jtreg is unable to detect SkippedException because it is wrapped by CompileFramework Reviewed-by: mhaessig, epeter --- .../compile_framework/CompileFramework.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java index d9a98582aec..3612f5ab3d3 100644 --- a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.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 @@ -28,6 +28,8 @@ import java.lang.reflect.Method; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import jtreg.SkippedException; /** * This is the entry-point for the Compile Framework. Its purpose it to allow @@ -132,10 +134,31 @@ public class CompileFramework { } catch (IllegalAccessException e) { throw new CompileFrameworkException("Illegal access:", e); } catch (InvocationTargetException e) { + // Rethrow jtreg.SkippedException so the tests are properly skipped. + // If we wrapped the SkippedException instead, it would get buried + // in the exception causes and cause a failed test instead. + findJtregSkippedExceptionInCauses(e).ifPresent(ex -> { throw ex; }); throw new CompileFrameworkException("Invocation target:", e); } } + private static Optional findJtregSkippedExceptionInCauses(Throwable ex) { + while (ex != null) { + // jtreg.SkippedException can be from a different classloader, comparing by name + if (ex.getClass().getName().equals(SkippedException.class.getName())) { + return Optional.of((RuntimeException) ex); + } + + if (ex.getCause() == ex) { + break; + } + + ex = ex.getCause(); + } + + return Optional.empty(); + } + private Method findMethod(String className, String methodName) { Class c = getClass(className); Method[] methods = c.getDeclaredMethods(); From 526228ca3f785263ed03995df3da09ef737ba4ca Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Thu, 26 Feb 2026 22:23:21 +0000 Subject: [PATCH 074/636] 8377142: Jtreg test gc/shenandoah/oom/TestThreadFailure.java triggers assert(young_reserve + reserve_for_mixed + reserve_for_promo <= old_available + young_available) failed Reviewed-by: wkemper --- .../shenandoah/shenandoahGenerationalHeap.cpp | 203 +++++++++++------- 1 file changed, 122 insertions(+), 81 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 98d30a3481f..2c2e5533c01 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -605,7 +605,8 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x ShenandoahOldGeneration* old_gen = old_generation(); size_t old_capacity = old_gen->max_capacity(); size_t old_usage = old_gen->used(); // includes humongous waste - size_t old_available = ((old_capacity >= old_usage)? old_capacity - old_usage: 0) + old_trashed_regions * region_size_bytes; + size_t old_currently_available = + ((old_capacity >= old_usage)? old_capacity - old_usage: 0) + old_trashed_regions * region_size_bytes; ShenandoahYoungGeneration* young_gen = young_generation(); size_t young_capacity = young_gen->max_capacity(); @@ -621,7 +622,8 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x size_t young_reserve = (young_generation()->max_capacity() * ShenandoahEvacReserve) / 100; // If ShenandoahOldEvacPercent equals 100, max_old_reserve is limited only by mutator_xfer_limit and young_reserve - const size_t bound_on_old_reserve = ((old_available + mutator_xfer_limit + young_reserve) * ShenandoahOldEvacPercent) / 100; + const size_t bound_on_old_reserve = + ((old_currently_available + mutator_xfer_limit + young_reserve) * ShenandoahOldEvacPercent) / 100; size_t proposed_max_old = ((ShenandoahOldEvacPercent == 100)? bound_on_old_reserve: MIN2((young_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), @@ -631,68 +633,105 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x } // Decide how much old space we should reserve for a mixed collection - size_t reserve_for_mixed = 0; + size_t proposed_reserve_for_mixed = 0; const size_t old_fragmented_available = - old_available - (old_generation()->free_unaffiliated_regions() + old_trashed_regions) * region_size_bytes; + old_currently_available - (old_generation()->free_unaffiliated_regions() + old_trashed_regions) * region_size_bytes; if (old_fragmented_available > proposed_max_old) { - // After we've promoted regions in place, there may be an abundance of old-fragmented available memory, - // even more than the desired percentage for old reserve. We cannot transfer these fragmented regions back - // to young. Instead we make the best of the situation by using this fragmented memory for both promotions - // and evacuations. + // In this case, the old_fragmented_available is greater than the desired amount of evacuation to old. + // We'll use all of this memory to hold results of old evacuation, and we'll give back to the young generation + // any old regions that are not fragmented. + // + // This scenario may happen after we have promoted many regions in place, and each of these regions had non-zero + // unused memory, so there is now an abundance of old-fragmented available memory, even more than the desired + // percentage for old reserve. We cannot transfer these fragmented regions back to young. Instead we make the + // best of the situation by using this fragmented memory for both promotions and evacuations. + proposed_max_old = old_fragmented_available; } - size_t reserve_for_promo = old_fragmented_available; + // Otherwise: old_fragmented_available <= proposed_max_old. Do not shrink proposed_max_old from the original computation. + + // Though we initially set proposed_reserve_for_promo to equal the entirety of old fragmented available, we have the + // opportunity below to shift some of this memory into the proposed_reserve_for_mixed. + size_t proposed_reserve_for_promo = old_fragmented_available; const size_t max_old_reserve = proposed_max_old; + const size_t mixed_candidate_live_memory = old_generation()->unprocessed_collection_candidates_live_memory(); const bool doing_mixed = (mixed_candidate_live_memory > 0); if (doing_mixed) { - // We want this much memory to be unfragmented in order to reliably evacuate old. This is conservative because we - // may not evacuate the entirety of unprocessed candidates in a single mixed evacuation. + // In the ideal, all of the memory reserved for mixed evacuation would be unfragmented, but we don't enforce + // this. Note that the initial value of max_evac_need is conservative because we may not evacuate all of the + // remaining mixed evacuation candidates in a single cycle. const size_t max_evac_need = (size_t) (mixed_candidate_live_memory * ShenandoahOldEvacWaste); - assert(old_available >= old_generation()->free_unaffiliated_regions() * region_size_bytes, + assert(old_currently_available >= old_generation()->free_unaffiliated_regions() * region_size_bytes, "Unaffiliated available must be less than total available"); // We prefer to evacuate all of mixed into unfragmented memory, and will expand old in order to do so, unless // we already have too much fragmented available memory in old. - reserve_for_mixed = max_evac_need; - if (reserve_for_mixed + reserve_for_promo > max_old_reserve) { - // In this case, we'll allow old-evac to target some of the fragmented old memory. - size_t excess_reserves = (reserve_for_mixed + reserve_for_promo) - max_old_reserve; - if (reserve_for_promo > excess_reserves) { - reserve_for_promo -= excess_reserves; + proposed_reserve_for_mixed = max_evac_need; + if (proposed_reserve_for_mixed + proposed_reserve_for_promo > max_old_reserve) { + // We're trying to reserve more memory than is available. So we need to shrink our reserves. + size_t excess_reserves = (proposed_reserve_for_mixed + proposed_reserve_for_promo) - max_old_reserve; + // We need to shrink reserves by excess_reserves. We prefer to shrink by reducing promotion, giving priority to mixed + // evacuation. If the promotion reserve is larger than the amount we need to shrink by, do all the shrinkage there. + if (proposed_reserve_for_promo > excess_reserves) { + proposed_reserve_for_promo -= excess_reserves; } else { - excess_reserves -= reserve_for_promo; - reserve_for_promo = 0; - reserve_for_mixed -= excess_reserves; + // Otherwise, we'll shrink promotion reserve to zero and we'll shrink the mixed-evac reserve by the remaining excess. + excess_reserves -= proposed_reserve_for_promo; + proposed_reserve_for_promo = 0; + proposed_reserve_for_mixed -= excess_reserves; } } } + assert(proposed_reserve_for_mixed + proposed_reserve_for_promo <= max_old_reserve, + "Reserve for mixed (%zu) plus reserve for promotions (%zu) must be less than maximum old reserve (%zu)", + proposed_reserve_for_mixed, proposed_reserve_for_promo, max_old_reserve); // Decide how much additional space we should reserve for promotions from young. We give priority to mixed evacations // over promotions. const size_t promo_load = old_generation()->get_promotion_potential(); const bool doing_promotions = promo_load > 0; - if (doing_promotions) { - // We've already set aside all of the fragmented available memory within old-gen to represent old objects - // to be promoted from young generation. promo_load represents the memory that we anticipate to be promoted - // from regions that have reached tenure age. In the ideal, we will always use fragmented old-gen memory - // to hold individually promoted objects and will use unfragmented old-gen memory to represent the old-gen - // evacuation workloa. - // We're promoting and have an estimate of memory to be promoted from aged regions - assert(max_old_reserve >= (reserve_for_mixed + reserve_for_promo), "Sanity"); - const size_t available_for_additional_promotions = max_old_reserve - (reserve_for_mixed + reserve_for_promo); - size_t promo_need = (size_t)(promo_load * ShenandoahPromoEvacWaste); - if (promo_need > reserve_for_promo) { - reserve_for_promo += MIN2(promo_need - reserve_for_promo, available_for_additional_promotions); + // promo_load represents the combined total of live memory within regions that have reached tenure age. The true + // promotion potential is larger than this, because individual objects within regions that have not yet reached tenure + // age may be promotable. On the other hand, some of the objects that we intend to promote in the next GC cycle may + // die before they are next marked. In the future, the promo_load will include the total size of tenurable objects + // residing in regions that have not yet reached tenure age. + + if (doing_promotions) { + // We are always doing promotions, even when old_generation->get_promotion_potential() returns 0. As currently implemented, + // get_promotion_potential() only knows the total live memory contained within young-generation regions whose age is + // tenurable. It does not know whether that memory will still be live at the end of the next mark cycle, and it doesn't + // know how much memory is contained within objects whose individual ages are tenurable, which reside in regions with + // non-tenurable age. We use this, as adjusted by ShenandoahPromoEvacWaste, as an approximation of the total amount of + // memory to be promoted. In the near future, we expect to implement a change that will allow get_promotion_potential() + // to account also for the total memory contained within individual objects that are tenure-ready even when they do + // not reside in aged regions. This will represent a conservative over approximation of promotable memory because + // some of these objects may die before the next GC cycle executes. + + // Be careful not to ask for too much promotion reserves. We have observed jtreg test failures under which a greedy + // promotion reserve causes a humongous allocation which is awaiting a full GC to fail (specifically + // gc/TestAllocHumongousFragment.java). This happens if too much of the memory reclaimed by the full GC + // is immediately reserved so that it cannot be allocated by the waiting mutator. It's not clear that this + // particular test is representative of the needs of typical GenShen users. It is really a test of high frequency + // Full GCs under heap fragmentation stress. + + size_t promo_need = (size_t) (promo_load * ShenandoahPromoEvacWaste); + if (promo_need > proposed_reserve_for_promo) { + const size_t available_for_additional_promotions = + max_old_reserve - (proposed_reserve_for_mixed + proposed_reserve_for_promo); + if (proposed_reserve_for_promo + available_for_additional_promotions >= promo_need) { + proposed_reserve_for_promo = promo_need; + } else { + proposed_reserve_for_promo += available_for_additional_promotions; + } } - // We've already reserved all the memory required for the promo_load, and possibly more. The excess - // can be consumed by objects promoted from regions that have not yet reached tenure age. } + // else, leave proposed_reserve_for_promo as is. By default, it is initialized to represent old_fragmented_available. // This is the total old we want to reserve (initialized to the ideal reserve) - size_t old_reserve = reserve_for_mixed + reserve_for_promo; + size_t proposed_old_reserve = proposed_reserve_for_mixed + proposed_reserve_for_promo; // We now check if the old generation is running a surplus or a deficit. size_t old_region_deficit = 0; @@ -702,68 +741,70 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x // align the mutator_xfer_limit on region size mutator_xfer_limit = mutator_region_xfer_limit * region_size_bytes; - if (old_available >= old_reserve) { + if (old_currently_available >= proposed_old_reserve) { // We are running a surplus, so the old region surplus can go to young - const size_t old_surplus = old_available - old_reserve; + const size_t old_surplus = old_currently_available - proposed_old_reserve; old_region_surplus = old_surplus / region_size_bytes; const size_t unaffiliated_old_regions = old_generation()->free_unaffiliated_regions() + old_trashed_regions; old_region_surplus = MIN2(old_region_surplus, unaffiliated_old_regions); old_generation()->set_region_balance(checked_cast(old_region_surplus)); - } else if (old_available + mutator_xfer_limit >= old_reserve) { - // Mutator's xfer limit is sufficient to satisfy our need: transfer all memory from there - size_t old_deficit = old_reserve - old_available; + old_currently_available -= old_region_surplus * region_size_bytes; + young_available += old_region_surplus * region_size_bytes; + } else if (old_currently_available + mutator_xfer_limit >= proposed_old_reserve) { + // We know that old_currently_available < proposed_old_reserve because above test failed. Expand old_currently_available. + // Mutator's xfer limit is sufficient to satisfy our need: transfer all memory from there. + size_t old_deficit = proposed_old_reserve - old_currently_available; old_region_deficit = (old_deficit + region_size_bytes - 1) / region_size_bytes; old_generation()->set_region_balance(0 - checked_cast(old_region_deficit)); + old_currently_available += old_region_deficit * region_size_bytes; + young_available -= old_region_deficit * region_size_bytes; } else { - // We'll try to xfer from both mutator excess and from young collector reserve - size_t available_reserves = old_available + young_reserve + mutator_xfer_limit; - size_t old_entitlement = (available_reserves * ShenandoahOldEvacPercent) / 100; + // We know that (old_currently_available < proposed_old_reserve) and + // (old_currently_available + mutator_xfer_limit < proposed_old_reserve) because above tests failed. + // We need to shrink proposed_old_reserves. - // Round old_entitlement down to nearest multiple of regions to be transferred to old - size_t entitled_xfer = old_entitlement - old_available; - entitled_xfer = region_size_bytes * (entitled_xfer / region_size_bytes); - size_t unaffiliated_young_regions = young_generation()->free_unaffiliated_regions(); - size_t unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes; - if (entitled_xfer > unaffiliated_young_memory) { - entitled_xfer = unaffiliated_young_memory; - } - old_entitlement = old_available + entitled_xfer; - if (old_entitlement < old_reserve) { - // There's not enough memory to satisfy our desire. Scale back our old-gen intentions. - size_t budget_overrun = old_reserve - old_entitlement;; - if (reserve_for_promo > budget_overrun) { - reserve_for_promo -= budget_overrun; - old_reserve -= budget_overrun; - } else { - budget_overrun -= reserve_for_promo; - reserve_for_promo = 0; - reserve_for_mixed = (reserve_for_mixed > budget_overrun)? reserve_for_mixed - budget_overrun: 0; - old_reserve = reserve_for_promo + reserve_for_mixed; - } - } + // We could potentially shrink young_reserves in order to further expand proposed_old_reserves. Let's not bother. The + // important thing is that we keep a total amount of memory in reserve in preparation for the next GC cycle. At + // the time we choose the next collection set, we'll have an opportunity to shift some of these young reserves + // into old reserves if that makes sense. - // Because of adjustments above, old_reserve may be smaller now than it was when we tested the branch - // condition above: "(old_available + mutator_xfer_limit >= old_reserve) - // Therefore, we do NOT know that: mutator_xfer_limit < old_reserve - old_available - - size_t old_deficit = old_reserve - old_available; - old_region_deficit = (old_deficit + region_size_bytes - 1) / region_size_bytes; - - // Shrink young_reserve to account for loan to old reserve - const size_t reserve_xfer_regions = old_region_deficit - mutator_region_xfer_limit; - young_reserve -= reserve_xfer_regions * region_size_bytes; + // Start by taking all of mutator_xfer_limit into old_currently_available. + size_t old_region_deficit = mutator_region_xfer_limit; old_generation()->set_region_balance(0 - checked_cast(old_region_deficit)); + old_currently_available += old_region_deficit * region_size_bytes; + young_available -= old_region_deficit * region_size_bytes; + + assert(old_currently_available < proposed_old_reserve, + "Old currently available (%zu) must be less than old reserve (%zu)", old_currently_available, proposed_old_reserve); + + // There's not enough memory to satisfy our desire. Scale back our old-gen intentions. We prefer to satisfy + // the budget_overrun entirely from the promotion reserve, if that is large enough. Otherwise, we'll satisfy + // the overrun from a combination of promotion and mixed-evacuation reserves. + size_t budget_overrun = proposed_old_reserve - old_currently_available; + if (proposed_reserve_for_promo > budget_overrun) { + proposed_reserve_for_promo -= budget_overrun; + // Dead code: + // proposed_old_reserve -= budget_overrun; + } else { + budget_overrun -= proposed_reserve_for_promo; + proposed_reserve_for_promo = 0; + proposed_reserve_for_mixed = (proposed_reserve_for_mixed > budget_overrun)? proposed_reserve_for_mixed - budget_overrun: 0; + // Dead code: + // Note: proposed_reserve_for_promo is 0 and proposed_reserve_for_mixed may equal 0. + // proposed_old_reserve = proposed_reserve_for_mixed; + } } - assert(old_region_deficit == 0 || old_region_surplus == 0, "Only surplus or deficit, never both"); - assert(young_reserve + reserve_for_mixed + reserve_for_promo <= old_available + young_available, + assert(old_region_deficit == 0 || old_region_surplus == 0, + "Only surplus (%zu) or deficit (%zu), never both", old_region_surplus, old_region_deficit); + assert(young_reserve + proposed_reserve_for_mixed + proposed_reserve_for_promo <= old_currently_available + young_available, "Cannot reserve more memory than is available: %zu + %zu + %zu <= %zu + %zu", - young_reserve, reserve_for_mixed, reserve_for_promo, old_available, young_available); + young_reserve, proposed_reserve_for_mixed, proposed_reserve_for_promo, old_currently_available, young_available); // deficit/surplus adjustments to generation sizes will precede rebuild young_generation()->set_evacuation_reserve(young_reserve); - old_generation()->set_evacuation_reserve(reserve_for_mixed); - old_generation()->set_promoted_reserve(reserve_for_promo); + old_generation()->set_evacuation_reserve(proposed_reserve_for_mixed); + old_generation()->set_promoted_reserve(proposed_reserve_for_promo); } void ShenandoahGenerationalHeap::coalesce_and_fill_old_regions(bool concurrent) { From 4a6de12b3a356b4aec9049ec3ee1ee26cd4517bf Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Thu, 26 Feb 2026 23:59:09 +0000 Subject: [PATCH 075/636] 8371438: jpackage should handle the case when "--mac-sign" is specified without signing identity options Reviewed-by: almatvee --- .../jdk/jpackage/internal/MacFromOptions.java | 98 ++++-- .../jdk/jpackage/internal/OptionUtils.java | 7 +- .../jpackage/internal/cli/StandardOption.java | 2 +- test/jdk/tools/jpackage/TEST.properties | 4 +- .../jdk/jpackage/test/MacHelperTest.java | 144 ++++++++- .../jdk/jpackage/test/JPackageCommand.java | 11 +- .../helpers/jdk/jpackage/test/MacHelper.java | 182 +++++++++-- .../helpers/jdk/jpackage/test/MacSign.java | 128 ++++++-- .../jdk/jpackage/test/mock/CommandAction.java | 30 ++ .../test/mock/MockIllegalStateException.java | 4 +- .../test/stdmock/MacSecurityMock.java | 176 +++++++++++ .../test/stdmock/MacSignMockUtils.java | 284 +++++++++++++++++ .../cli/OptionsValidationFailTest.excludes | 10 + .../jpackage/macosx/SigningAppImageTest.java | 15 +- .../macosx/SigningAppImageTwoStepsTest.java | 33 +- .../tools/jpackage/macosx/SigningBase.java | 20 +- .../jpackage/macosx/SigningPackageTest.java | 89 +++++- .../macosx/SigningPackageTwoStepTest.java | 2 +- .../SigningRuntimeImagePackageTest.java | 2 +- test/jdk/tools/jpackage/share/ErrorTest.java | 293 +++++++++++++++++- 20 files changed, 1397 insertions(+), 137 deletions(-) create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSecurityMock.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSignMockUtils.java diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java index 0598e6bc2a9..4cd4f386ad8 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -29,6 +29,8 @@ import static jdk.jpackage.internal.FromOptions.createPackageBuilder; import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasJliLib; import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasNoBinDir; +import static jdk.jpackage.internal.OptionUtils.isBundlingOperation; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_PKG; import static jdk.jpackage.internal.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; import static jdk.jpackage.internal.cli.StandardOption.ICON; @@ -120,23 +122,39 @@ final class MacFromOptions { final Optional pkgSigningIdentityBuilder; - if (sign && (MAC_INSTALLER_SIGN_IDENTITY.containsIn(options) || MAC_SIGNING_KEY_NAME.containsIn(options))) { + if (!sign) { + pkgSigningIdentityBuilder = Optional.empty(); + } else if (hasAppImageSignIdentity(options) && !hasPkgInstallerSignIdentity(options)) { + // They explicitly request to sign the app image, + // but don't specify signing identity for signing the PKG package. + // They want signed app image inside of unsigned PKG. + pkgSigningIdentityBuilder = Optional.empty(); + } else { final var signingIdentityBuilder = createSigningIdentityBuilder(options); - MAC_INSTALLER_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); - MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { - final StandardCertificateSelector domain; - if (appStore) { - domain = StandardCertificateSelector.APP_STORE_PKG_INSTALLER; - } else { - domain = StandardCertificateSelector.PKG_INSTALLER; - } - signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); - }); + MAC_INSTALLER_SIGN_IDENTITY.findIn(options).ifPresentOrElse( + signingIdentityBuilder::signingIdentity, + () -> { + MAC_SIGNING_KEY_NAME.findIn(options).or(() -> { + if (MAC_APP_IMAGE_SIGN_IDENTITY.findIn(options).isPresent()) { + return Optional.empty(); + } else { + return Optional.of(""); + } + }).ifPresent(userName -> { + final StandardCertificateSelector domain; + if (appStore) { + domain = StandardCertificateSelector.APP_STORE_PKG_INSTALLER; + } else { + domain = StandardCertificateSelector.PKG_INSTALLER; + } + + signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); + }); + } + ); pkgSigningIdentityBuilder = Optional.of(signingIdentityBuilder); - } else { - pkgSigningIdentityBuilder = Optional.empty(); } ApplicationWithDetails app = null; @@ -230,33 +248,47 @@ final class MacFromOptions { MAC_BUNDLE_IDENTIFIER.ifPresentIn(options, appBuilder::bundleIdentifier); MAC_APP_CATEGORY.ifPresentIn(options, appBuilder::category); - final boolean sign; + final boolean sign = MAC_SIGN.getFrom(options); final boolean appStore; - if (PREDEFINED_APP_IMAGE.containsIn(options) && OptionUtils.bundlingOperation(options) != SIGN_MAC_APP_IMAGE) { + if (PREDEFINED_APP_IMAGE.containsIn(options)) { final var appImageFileOptions = superAppBuilder.externalApplication().orElseThrow().extra(); - sign = MAC_SIGN.getFrom(appImageFileOptions); appStore = MAC_APP_STORE.getFrom(appImageFileOptions); } else { - sign = MAC_SIGN.getFrom(options); appStore = MAC_APP_STORE.getFrom(options); } appBuilder.appStore(appStore); - if (sign && (MAC_APP_IMAGE_SIGN_IDENTITY.containsIn(options) || MAC_SIGNING_KEY_NAME.containsIn(options))) { - final var signingIdentityBuilder = createSigningIdentityBuilder(options); - MAC_APP_IMAGE_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); - MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { - final StandardCertificateSelector domain; - if (appStore) { - domain = StandardCertificateSelector.APP_STORE_APP_IMAGE; - } else { - domain = StandardCertificateSelector.APP_IMAGE; - } + final var signOnlyPkgInstaller = sign && ( + isBundlingOperation(options, CREATE_MAC_PKG) + && !hasAppImageSignIdentity(options) + && hasPkgInstallerSignIdentity(options)); - signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); - }); + if (sign && !signOnlyPkgInstaller) { + final var signingIdentityBuilder = createSigningIdentityBuilder(options); + + MAC_APP_IMAGE_SIGN_IDENTITY.findIn(options).ifPresentOrElse( + signingIdentityBuilder::signingIdentity, + () -> { + MAC_SIGNING_KEY_NAME.findIn(options).or(() -> { + if (MAC_INSTALLER_SIGN_IDENTITY.containsIn(options)) { + return Optional.empty(); + } else { + return Optional.of(""); + } + }).ifPresent(userName -> { + final StandardCertificateSelector domain; + if (appStore) { + domain = StandardCertificateSelector.APP_STORE_APP_IMAGE; + } else { + domain = StandardCertificateSelector.APP_IMAGE; + } + + signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); + }); + } + ); final var signingBuilder = new AppImageSigningConfigBuilder(signingIdentityBuilder); if (appStore) { @@ -331,4 +363,12 @@ final class MacFromOptions { return builder.create(fa); } + + private static boolean hasAppImageSignIdentity(Options options) { + return options.contains(MAC_SIGNING_KEY_NAME) || options.contains(MAC_APP_IMAGE_SIGN_IDENTITY); + } + + private static boolean hasPkgInstallerSignIdentity(Options options) { + return options.contains(MAC_SIGNING_KEY_NAME) || options.contains(MAC_INSTALLER_SIGN_IDENTITY); + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java index 97e1274f078..b39e67a9eb7 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.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 @@ -32,6 +32,7 @@ import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_RUNTIME_IMAGE; import java.nio.file.Path; +import java.util.Objects; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardBundlingOperation; @@ -51,4 +52,8 @@ final class OptionUtils { static StandardBundlingOperation bundlingOperation(Options options) { return StandardBundlingOperation.valueOf(BUNDLING_OPERATION_DESCRIPTOR.getFrom(options)).orElseThrow(); } + + static boolean isBundlingOperation(Options options, StandardBundlingOperation op) { + return bundlingOperation(options).equals(Objects.requireNonNull(op)); + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index 4450a6168e2..eca82da99aa 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java @@ -109,7 +109,7 @@ public final class StandardOption { public static final OptionValue VERBOSE = auxilaryOption("verbose").create(); - public static final OptionValue TYPE = option("type", BundleType.class).addAliases("t") + static final OptionValue TYPE = option("type", BundleType.class).addAliases("t") .scope(StandardBundlingOperation.values()).inScope(NOT_BUILDING_APP_IMAGE) .converterExceptionFactory(ERROR_WITH_VALUE).converterExceptionFormatString("ERR_InvalidInstallerType") .converter(str -> { diff --git a/test/jdk/tools/jpackage/TEST.properties b/test/jdk/tools/jpackage/TEST.properties index 19eda305364..5cc4aa7a1b9 100644 --- a/test/jdk/tools/jpackage/TEST.properties +++ b/test/jdk/tools/jpackage/TEST.properties @@ -13,7 +13,7 @@ maxOutputSize = 2000000 # Run jpackage tests on windows platform sequentially. # Having "share" directory in the list affects tests on other platforms. # The better option would be: -# if (platfrom == windowws) { +# if (platfrom == windows) { # exclusiveAccess.dirs=share windows # } # but conditionals are not supported by jtreg configuration files. @@ -29,4 +29,6 @@ modules = \ jdk.jpackage/jdk.jpackage.internal.util.function \ jdk.jpackage/jdk.jpackage.internal.resources:+open \ java.base/jdk.internal.util \ + java.base/sun.security.util \ + java.base/sun.security.x509 \ jdk.jlink/jdk.tools.jlink.internal diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.java index 5d14f021eaf..b8f7bfd456e 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/MacHelperTest.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 @@ -31,14 +31,20 @@ import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.XmlUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -public class MacHelperTest { +public class MacHelperTest extends JUnitAdapter { @Test public void test_flatMapPList() { @@ -105,6 +111,18 @@ public class MacHelperTest { ), props); } + @ParameterizedTest + @MethodSource + public void test_appImageSigned(SignedTestSpec spec) { + spec.test(MacHelper::appImageSigned); + } + + @ParameterizedTest + @MethodSource + public void test_nativePackageSigned(SignedTestSpec spec) { + spec.test(MacHelper::nativePackageSigned); + } + private static String createPListXml(String ...xml) { final List content = new ArrayList<>(); content.add(""); @@ -125,4 +143,126 @@ public class MacHelperTest { throw new RuntimeException(ex); } } + + private static Stream test_appImageSigned() { + + List data = new ArrayList<>(); + + for (var signingIdentityOption : List.of( + List.of(), + List.of("--mac-signing-key-user-name", "foo"), + List.of("--mac-app-image-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo", "--mac-app-image-sign-identity", "bar") + )) { + for (var type : List.of(PackageType.IMAGE, PackageType.MAC_DMG, PackageType.MAC_PKG)) { + for (var withMacSign : List.of(true, false)) { + if (signingIdentityOption.contains("--mac-installer-sign-identity") && type != PackageType.MAC_PKG) { + continue; + } + + var builder = SignedTestSpec.build().type(type).cmdline(signingIdentityOption); + if (withMacSign) { + builder.cmdline("--mac-sign"); + if (Stream.of( + "--mac-signing-key-user-name", + "--mac-app-image-sign-identity" + ).anyMatch(signingIdentityOption::contains) || signingIdentityOption.isEmpty()) { + builder.signed(); + } + } + + data.add(builder); + } + } + } + + return data.stream().map(SignedTestSpec.Builder::create); + } + + private static Stream test_nativePackageSigned() { + + List data = new ArrayList<>(); + + for (var signingIdentityOption : List.of( + List.of(), + List.of("--mac-signing-key-user-name", "foo"), + List.of("--mac-app-image-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo"), + List.of("--mac-installer-sign-identity", "foo", "--mac-app-image-sign-identity", "bar") + )) { + for (var type : List.of(PackageType.MAC_DMG, PackageType.MAC_PKG)) { + for (var withMacSign : List.of(true, false)) { + if (signingIdentityOption.contains("--mac-installer-sign-identity") && type != PackageType.MAC_PKG) { + continue; + } + + var builder = SignedTestSpec.build().type(type).cmdline(signingIdentityOption); + if (withMacSign) { + builder.cmdline("--mac-sign"); + if (type == PackageType.MAC_PKG && (Stream.of( + "--mac-signing-key-user-name", + "--mac-installer-sign-identity" + ).anyMatch(signingIdentityOption::contains) || signingIdentityOption.isEmpty())) { + builder.signed(); + } + } + + data.add(builder); + } + } + } + + return data.stream().map(SignedTestSpec.Builder::create); + } + + private record SignedTestSpec(boolean expectedSigned, PackageType type, List cmdline) { + + SignedTestSpec { + Objects.requireNonNull(type); + cmdline.forEach(Objects::requireNonNull); + } + + void test(Function func) { + var actualSigned = func.apply((new JPackageCommand().addArguments(cmdline).setPackageType(type))); + assertEquals(expectedSigned, actualSigned); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + SignedTestSpec create() { + return new SignedTestSpec( + expectedSigned, + Optional.ofNullable(type).orElse(PackageType.IMAGE), + cmdline); + } + + Builder signed() { + expectedSigned = true; + return this; + } + + Builder type(PackageType v) { + type = v; + return this; + } + + Builder cmdline(String... args) { + return cmdline(List.of(args)); + } + + Builder cmdline(List v) { + cmdline.addAll(v); + return this; + } + + private boolean expectedSigned; + private PackageType type; + private List cmdline = new ArrayList<>(); + } + } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index da6af4bed33..251d95a1089 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1292,7 +1292,7 @@ public class JPackageCommand extends CommandArguments { } }), MAC_BUNDLE_UNSIGNED_SIGNATURE(cmd -> { - if (TKit.isOSX() && !MacHelper.appImageSigned(cmd)) { + if (TKit.isOSX()) { MacHelper.verifyUnsignedBundleSignature(cmd); } }), @@ -1316,7 +1316,14 @@ public class JPackageCommand extends CommandArguments { }), PREDEFINED_APP_IMAGE_COPY(cmd -> { Optional.ofNullable(cmd.getArgumentValue("--app-image")).filter(_ -> { - return !TKit.isOSX() || !MacHelper.signPredefinedAppImage(cmd); + if (!TKit.isOSX() || !cmd.hasArgument("--mac-sign")) { + return true; + } else { + var signAppImage = MacHelper.signPredefinedAppImage(cmd) + || MacHelper.hasAppImageSignIdentity(cmd) + || MacHelper.isSignWithoutSignIdentity(cmd); + return !signAppImage; + } }).filter(_ -> { // Don't examine the contents of the output app image if this is Linux package installing in the "/usr" subtree. return Optional.ofNullable(cmd.onLinuxPackageInstallDir(null, _ -> false)).orElse(true); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index 1191cd02221..1372058d440 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -76,6 +76,7 @@ import jdk.jpackage.internal.util.RetryExecutor; import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.test.MacSign.CertificateHash; import jdk.jpackage.test.MacSign.CertificateRequest; import jdk.jpackage.test.MacSign.CertificateType; import jdk.jpackage.test.MacSign.ResolvedKeychain; @@ -259,10 +260,7 @@ public final class MacHelper { * predefined app image in place and {@code false} otherwise. */ public static boolean signPredefinedAppImage(JPackageCommand cmd) { - Objects.requireNonNull(cmd); - if (!TKit.isOSX()) { - throw new UnsupportedOperationException(); - } + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); return cmd.hasArgument("--mac-sign") && cmd.hasArgument("--app-image") && cmd.isImagePackageType(); } @@ -279,10 +277,7 @@ public final class MacHelper { * otherwise. */ public static boolean appImageSigned(JPackageCommand cmd) { - Objects.requireNonNull(cmd); - if (!TKit.isOSX()) { - throw new UnsupportedOperationException(); - } + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); var runtimeImageBundle = Optional.ofNullable(cmd.getArgumentValue("--runtime-image")).map(Path::of).flatMap(MacBundle::fromPath); var appImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of); @@ -291,23 +286,102 @@ public final class MacHelper { // If the predefined runtime is a signed bundle, bundled image should be signed too. return true; } else if (appImage.map(MacHelper::isBundleSigned).orElse(false)) { - // The external app image is signed, so the app image is signed too. + // The predefined app image is signed, so the app image is signed too. return true; } - if (!cmd.isImagePackageType() && appImage.isPresent()) { - // Building a ".pkg" or a ".dmg" bundle from the predefined app image. - // The predefined app image is unsigned, so the app image bundled - // in the output native package will be unsigned too - // (even if the ".pkg" file may be signed itself, and we never sign ".dmg" files). - return false; - } - if (!cmd.hasArgument("--mac-sign")) { return false; + } else { + return isSignWithoutSignIdentity(cmd) || hasAppImageSignIdentity(cmd); } + } - return (cmd.hasArgument("--mac-signing-key-user-name") || cmd.hasArgument("--mac-app-image-sign-identity")); + /** + * Returns {@code true} if the given jpackage command line is configured such + * that the native package it will produce will be signed. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line is configured such + * the native package it will produce will be signed and {@code false} + * otherwise. + */ + public static boolean nativePackageSigned(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC); + + switch (cmd.packageType()) { + case MAC_DMG -> { + return false; + } + case MAC_PKG -> { + if (!cmd.hasArgument("--mac-sign")) { + return false; + } else { + return isSignWithoutSignIdentity(cmd) || hasPkgInstallerSignIdentity(cmd); + } + } + default -> { + throw new IllegalStateException(); + } + } + } + + /** + * Returns {@code true} if the given jpackage command line has app image signing + * identity option. The command line must have "--mac-sign" option. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line has app image signing + * identity option and {@code false} otherwise. + */ + public static boolean hasAppImageSignIdentity(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); + if (!cmd.hasArgument("--mac-sign")) { + throw new IllegalArgumentException(); + } + return Stream.of( + "--mac-signing-key-user-name", + "--mac-app-image-sign-identity" + ).anyMatch(cmd::hasArgument); + } + + /** + * Returns {@code true} if the given jpackage command line has PKG installer signing + * identity option. The command line must have "--mac-sign" option. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line has PKG installer signing + * identity option and {@code false} otherwise. + */ + public static boolean hasPkgInstallerSignIdentity(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC_PKG); + if (!cmd.hasArgument("--mac-sign")) { + throw new IllegalArgumentException(); + } + return Stream.of( + "--mac-signing-key-user-name", + "--mac-installer-sign-identity" + ).anyMatch(cmd::hasArgument); + } + + /** + * Returns {@code true} if the given jpackage command line doesn't have signing + * identity options. The command line must have "--mac-sign" option. + * + * @param cmd the jpackage command to examine + * @return {@code true} if the given jpackage command line doesn't have signing + * identity options and {@code false} otherwise. + */ + public static boolean isSignWithoutSignIdentity(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); + if (!cmd.hasArgument("--mac-sign")) { + throw new IllegalArgumentException(); + } + return Stream.of( + "--mac-signing-key-user-name", + "--mac-app-image-sign-identity", + "--mac-installer-sign-identity" + ).noneMatch(cmd::hasArgument); } public static void writeFaPListFragment(JPackageCommand cmd, XMLStreamWriter xml) { @@ -702,6 +776,14 @@ public final class MacHelper { MacSign.CertificateType.CODE_SIGN, Name.KEY_IDENTITY_APP_IMAGE, MacSign.CertificateType.INSTALLER, Name.KEY_IDENTITY_INSTALLER)), + /** + * "--mac-installer-sign-identity" or "--mac-app-image-sign-identity" option + * with the SHA1 of a signing certificate + */ + SIGN_KEY_IDENTITY_SHA1(Map.of( + MacSign.CertificateType.CODE_SIGN, Name.KEY_IDENTITY_APP_IMAGE, + MacSign.CertificateType.INSTALLER, Name.KEY_IDENTITY_INSTALLER)), + /** * "--mac-app-image-sign-identity" regardless of the type of signing identity * (for signing app image or .pkg installer). @@ -714,6 +796,12 @@ public final class MacHelper { */ SIGN_KEY_IDENTITY_INSTALLER(Name.KEY_IDENTITY_INSTALLER), + /** + * No explicit option specifying signing identity. jpackage will pick one from + * the specified keychain. + */ + SIGN_KEY_IMPLICIT, + ; Type(Map optionNameMap) { @@ -736,11 +824,24 @@ public final class MacHelper { return optionNameMapper.apply(Objects.requireNonNull(certType)); } + public boolean passThrough() { + return Stream.of(MacSign.CertificateType.values()) + .map(this::mapOptionName) + .flatMap(Optional::stream) + .map(Name::passThrough) + .distinct() + .reduce((_, _) -> { + throw new IllegalStateException(); + }).orElse(false); + } + public static Type[] defaultValues() { return new Type[] { SIGN_KEY_USER_SHORT_NAME, SIGN_KEY_USER_FULL_NAME, - SIGN_KEY_IDENTITY + SIGN_KEY_IDENTITY, + SIGN_KEY_IDENTITY_SHA1, + SIGN_KEY_IMPLICIT }; } @@ -751,7 +852,7 @@ public final class MacHelper { public String toString() { var sb = new StringBuilder(); sb.append('{'); - applyTo((optionName, _) -> { + type.mapOptionName(certRequest.type()).ifPresent(optionName -> { sb.append(optionName); switch (type) { case SIGN_KEY_USER_FULL_NAME -> { @@ -762,6 +863,9 @@ public final class MacHelper { sb.append("=").append(ENQUOTER.applyTo(optionValue)); }); } + case SIGN_KEY_IDENTITY_SHA1 -> { + sb.append("/sha1"); + } default -> { // NOP } @@ -787,12 +891,16 @@ public final class MacHelper { } public List asCmdlineArgs() { - String[] args = new String[2]; - applyTo((optionName, optionValue) -> { - args[0] = optionName; - args[1] = optionValue; - }); - return List.of(args); + if (type == Type.SIGN_KEY_IMPLICIT) { + return List.of(); + } else { + String[] args = new String[2]; + applyTo((optionName, optionValue) -> { + args[0] = optionName; + args[1] = optionValue; + }); + return List.of(args); + } } public Optional passThrough() { @@ -817,6 +925,9 @@ public final class MacHelper { case SIGN_KEY_USER_SHORT_NAME -> { return certRequest.shortName(); } + case SIGN_KEY_IDENTITY_SHA1 -> { + return CertificateHash.of(certRequest.cert()).toString(); + } default -> { throw new IllegalStateException(); } @@ -960,18 +1071,21 @@ public final class MacHelper { } static void verifyUnsignedBundleSignature(JPackageCommand cmd) { - if (!cmd.isImagePackageType()) { + + if (!cmd.isImagePackageType() && !nativePackageSigned(cmd)) { MacSignVerify.assertUnsigned(cmd.outputBundle()); } - final Path bundleRoot; - if (cmd.isImagePackageType()) { - bundleRoot = cmd.outputBundle(); - } else { - bundleRoot = cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); - } + if (!appImageSigned(cmd)) { + final Path bundleRoot; + if (cmd.isImagePackageType()) { + bundleRoot = cmd.outputBundle(); + } else { + bundleRoot = cmd.pathToUnpackedPackageFile(cmd.appInstallationDirectory()); + } - MacSignVerify.assertAdhocSigned(bundleRoot); + MacSignVerify.assertAdhocSigned(bundleRoot); + } } static PackageHandlers createDmgPackageHandlers() { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java index 4dbb2bc1e18..20f7ac41eef 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java @@ -63,9 +63,11 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Stream; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import jdk.jpackage.internal.util.MemoizingSupplier; import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingSupplier; @@ -280,8 +282,15 @@ public final class MacSign { return sb.toString(); } + public static Builder build() { + return new Builder(); + } + public static final class Builder { + private Builder() { + } + public Builder name(String v) { keychainBuilder.name(v); return this; @@ -306,8 +315,8 @@ public final class MacSign { return new KeychainWithCertsSpec(keychain, List.copyOf(certs)); } - private Keychain.Builder keychainBuilder = new Keychain.Builder(); - private List certs = new ArrayList<>(); + private final Keychain.Builder keychainBuilder = Keychain.build(); + private final List certs = new ArrayList<>(); } } @@ -326,8 +335,15 @@ public final class MacSign { } } + public static Builder build() { + return new Builder(); + } + public static final class Builder { + private Builder() { + } + public Builder name(String v) { name = v; return this; @@ -599,12 +615,7 @@ public final class MacSign { @Override public String toString() { - final var sb = new StringBuilder(); - sb.append(frame("BEGIN " + label)); - sb.append(ENCODER.encodeToString(data)); - sb.append("\n"); - sb.append(frame("END " + label)); - return sb.toString(); + return PemDataFormatter.format(label, data); } static PemData of(X509Certificate cert) { @@ -619,6 +630,21 @@ public final class MacSign { throw new UncheckedIOException(ex); } } + } + + private final class PemDataFormatter { + + static String format(String label, byte[] data) { + Objects.requireNonNull(label); + Objects.requireNonNull(data); + + final var sb = new StringBuilder(); + sb.append(frame("BEGIN " + label)); + sb.append(ENCODER.encodeToString(data)); + sb.append("\n"); + sb.append(frame("END " + label)); + return sb.toString(); + } private static String frame(String str) { return String.format("-----%s-----\n", Objects.requireNonNull(str)); @@ -627,6 +653,11 @@ public final class MacSign { private static final Base64.Encoder ENCODER = Base64.getMimeEncoder(64, "\n".getBytes()); } + public static String formatX509Certificate(X509Certificate cert) { + Objects.requireNonNull(cert); + return PemDataFormatter.format("CERTIFICATE", toSupplier(cert::getEncoded).get()); + } + public enum DigestAlgorithm { SHA1(20, () -> MessageDigest.getInstance("SHA-1")), SHA256(32, () -> MessageDigest.getInstance("SHA-256")); @@ -773,8 +804,15 @@ public final class MacSign { return COMPARATOR.compare(this, o); } + public static Builder build() { + return new Builder(); + } + public static final class Builder { + private Builder() { + } + public Builder userName(String v) { userName = v; return this; @@ -1068,6 +1106,15 @@ public final class MacSign { return !missingKeychain && !missingCertificates && !invalidCertificates; } + /** + * Creates an empty keychain with unique name in the work directory of the current test. + */ + public static Keychain createEmptyKeychain() { + return Keychain.build() + .name(TKit.createUniquePath(TKit.workDir().resolve("empty.keychain")).toAbsolutePath().toString()) + .create().create(); + } + public static Keychain.UsageBuilder withKeychains(KeychainWithCertsSpec... keychains) { return withKeychains(Stream.of(keychains).map(KeychainWithCertsSpec::keychain).toArray(Keychain[]::new)); } @@ -1100,9 +1147,14 @@ public final class MacSign { public static void withKeychain(Consumer consumer, Consumer mutator, ResolvedKeychain keychain) { Objects.requireNonNull(consumer); - withKeychains(() -> { + Objects.requireNonNull(mutator); + if (keychain.isMock()) { consumer.accept(keychain); - }, mutator, keychain.spec().keychain()); + } else { + withKeychains(() -> { + consumer.accept(keychain); + }, mutator, keychain.spec().keychain()); + } } public static void withKeychain(Consumer consumer, ResolvedKeychain keychain) { @@ -1111,7 +1163,15 @@ public final class MacSign { public static final class ResolvedKeychain { public ResolvedKeychain(KeychainWithCertsSpec spec) { + isMock = false; this.spec = Objects.requireNonNull(spec); + certMapSupplier = MemoizingSupplier.runOnce(() -> { + return MacSign.mapCertificateRequests(spec); + }); + } + + public static ResolvedKeychain createMock(String name, Map certs) { + return new ResolvedKeychain(name, certs); } public KeychainWithCertsSpec spec() { @@ -1122,15 +1182,30 @@ public final class MacSign { return spec.keychain().name(); } - public Map mapCertificateRequests() { - if (certMap == null) { - synchronized (this) { - if (certMap == null) { - certMap = MacSign.mapCertificateRequests(spec); - } - } + public boolean isMock() { + return isMock; + } + + public ResolvedKeychain toMock(Map signEnv) { + if (isMock) { + throw new UnsupportedOperationException("Already a mock"); } - return certMap; + + var comm = Comm.compare(Set.copyOf(spec.certificateRequests()), signEnv.keySet()); + if (!comm.unique1().isEmpty()) { + throw new IllegalArgumentException(String.format( + "Signing environment missing %s certificate request mappings in [%s] keychain", + comm.unique1(), name())); + } + + var certs = new HashMap<>(signEnv); + certs.keySet().retainAll(comm.common()); + + return createMock(name(), certs); + } + + public Map mapCertificateRequests() { + return certMapSupplier.get(); } public Function asCertificateResolver() { @@ -1145,8 +1220,23 @@ public final class MacSign { }; } + private ResolvedKeychain(String name, Map certs) { + + var keychainBuilder = KeychainWithCertsSpec.build().name(Objects.requireNonNull(name)); + certs.keySet().forEach(keychainBuilder::addCert); + + var certsCopy = Map.copyOf(Objects.requireNonNull(certs)); + + isMock = true; + spec = keychainBuilder.create(); + certMapSupplier = MemoizingSupplier.runOnce(() -> { + return certsCopy; + }); + } + + private final boolean isMock; private final KeychainWithCertsSpec spec; - private volatile Map certMap; + private final Supplier> certMapSupplier; } private static Map mapCertificateRequests(KeychainWithCertsSpec spec) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java index d9ab38e006a..c4899a5376f 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java @@ -60,6 +60,36 @@ public interface CommandAction { public MockIllegalStateException unexpectedArguments() { return new MockIllegalStateException(String.format("Unexpected arguments: %s", args)); } + + public Context shift(int count) { + if (count < 0) { + throw new IllegalArgumentException(); + } else if (count == 0) { + return this; + } else { + return new Context(out, err, args.subList(Integer.min(count, args.size()), args.size())); + } + } + + public Context shift() { + return shift(1); + } + + public void printlnOut(Object obj) { + out.println(obj); + } + + public void printlnOut(String str) { + out.println(str); + } + + public void printlnErr(Object obj) { + err.println(obj); + } + + public void printlnErr(String str) { + err.println(str); + } } /** diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java index 1817587364a..4656e1d4f7b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java @@ -22,13 +22,15 @@ */ package jdk.jpackage.test.mock; +import java.util.Objects; + /** * Indicates command mock internal error. */ public final class MockIllegalStateException extends IllegalStateException { public MockIllegalStateException(String msg) { - super(msg); + super(Objects.requireNonNull(msg)); } private static final long serialVersionUID = 1L; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSecurityMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSecurityMock.java new file mode 100644 index 00000000000..fe9a67f1889 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSecurityMock.java @@ -0,0 +1,176 @@ +/* + * 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 jdk.jpackage.test.stdmock; + +import java.nio.file.Path; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.ResolvedKeychain; +import jdk.jpackage.test.mock.CommandAction; +import jdk.jpackage.test.mock.MockIllegalStateException; + +/** + * Mocks /usr/bin/security command. + */ +final class MacSecurityMock implements CommandAction { + + MacSecurityMock(MacSignMockUtils.SignEnv signEnv) { + Objects.requireNonNull(signEnv); + + var keychains = signEnv.keychains(); + + var stdUserKeychains = Stream.of(StandardKeychain.values()).map(StandardKeychain::keychainName).filter(name -> { + // Add standard keychain unless it is defined in the signing environment. + return keychains.stream().noneMatch(keychain -> { + return keychain.name().equals(name); + }); + }).map(name -> { + // Assume the standard keychain is empty. + return ResolvedKeychain.createMock(name, Map.of()); + }); + + allKnownKeychains = Stream.of( + stdUserKeychains, + keychains.stream() + ).flatMap(x -> x).collect(Collectors.toUnmodifiableMap(ResolvedKeychain::name, x -> x)); + + currentKeychains.addAll(Stream.of(StandardKeychain.values()) + .map(StandardKeychain::keychainName) + .map(allKnownKeychains::get) + .map(Objects::requireNonNull).toList()); + } + + @Override + public Optional run(Context context) { + switch (context.args().getFirst()) { + case "list-keychains" -> { + listKeychains(context.shift()); + return Optional.of(0); + } + case "find-certificate" -> { + findCertificate(context.shift()); + return Optional.of(0); + } + default -> { + throw context.unexpectedArguments(); + } + } + } + + private void listKeychains(Context context) { + if (context.args().getFirst().equals("-s")) { + currentKeychains.clear(); + currentKeychains.addAll(context.shift().args().stream().map(name -> { + return Optional.ofNullable(allKnownKeychains.get(name)).orElseThrow(() -> { + throw new MockIllegalStateException(String.format("Unknown keychain name: %s", name)); + }); + }).toList()); + } else if (context.args().isEmpty()) { + currentKeychains.stream().map(keychain -> { + return String.format(" \"%s\"", keychain.name()); + }).forEach(context::printlnOut); + } else { + throw context.unexpectedArguments(); + } + } + + private void findCertificate(Context context) { + + var args = new ArrayList<>(context.args()); + for (var mandatoryArg : List.of("-p", "-a")) { + if (!args.remove(mandatoryArg)) { + throw context.unexpectedArguments(); + } + } + + var certNameFilter = context.findOptionValue("-c").map(certNameSubstr -> { + + // Remove option name and its value. + var idx = args.indexOf("-c"); + args.remove(idx); + args.remove(idx); + + Predicate> pred = e -> { + return e.getKey().name().contains(certNameSubstr); + }; + return pred; + }); + + Stream keychains; + if (args.isEmpty()) { + keychains = currentKeychains.stream(); + } else { + // Remaining arguments must be keychain names. + keychains = args.stream().map(keychainName -> { + return Optional.ofNullable(allKnownKeychains.get(keychainName)).orElseThrow(() -> { + throw new MockIllegalStateException(String.format("Unknown keychain name: %s", keychainName)); + }); + }); + } + + var certStream = keychains.flatMap(keychain -> { + return keychain.mapCertificateRequests().entrySet().stream(); + }); + + if (certNameFilter.isPresent()) { + certStream = certStream.filter(certNameFilter.get()); + } + + certStream.map(Map.Entry::getValue).map(MacSign::formatX509Certificate).forEach(formattedCert -> { + context.out().print(formattedCert); + }); + } + + // Keep the order of the items as the corresponding keychains appear + // in the output of the "/usr/bin/security list-keychains" command. + private enum StandardKeychain { + USER_KEYCHAIN { + @Override + String keychainName() { + return Path.of(System.getProperty("user.home")).resolve("Library/Keychains/login.keychain-db").toString(); + } + }, + SYSTEM_KEYCHAIN { + @Override + String keychainName() { + return "/Library/Keychains/System.keychain"; + } + }, + ; + + abstract String keychainName(); + } + + private final List currentKeychains = new ArrayList(); + private final Map allKnownKeychains; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSignMockUtils.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSignMockUtils.java new file mode 100644 index 00000000000..164261a6000 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/MacSignMockUtils.java @@ -0,0 +1,284 @@ +/* + * 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 jdk.jpackage.test.stdmock; + +import static jdk.jpackage.internal.util.function.ExceptionBox.toUnchecked; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.KeychainWithCertsSpec; +import jdk.jpackage.test.MacSign.ResolvedKeychain; +import jdk.jpackage.test.mock.CommandActionSpec; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockSpec; + + +/** + * Utilities to create macOS signing tool mocks. + */ +public final class MacSignMockUtils { + + private MacSignMockUtils() { + } + + public static Map resolveCertificateRequests( + Collection certificateRequests) { + Objects.requireNonNull(certificateRequests); + + var caKeys = createKeyPair(); + + Function resolver = toFunction(certRequest -> { + var builder = new CertificateBuilder() + .setSubjectName("CN=" + certRequest.name()) + .setPublicKey(caKeys.getPublic()) + .setSerialNumber(BigInteger.ONE) + .addSubjectKeyIdExt(caKeys.getPublic()) + .addAuthorityKeyIdExt(caKeys.getPublic()); + + Instant from; + Instant to; + if (certRequest.expired()) { + from = LocalDate.now().minusDays(10).atStartOfDay(ZoneId.systemDefault()).toInstant(); + to = from.plus(Duration.ofDays(1)); + } else { + from = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant(); + to = from.plus(Duration.ofDays(certRequest.days())); + } + builder.setValidity(Date.from(from), Date.from(to)); + + return builder.build(null, caKeys.getPrivate()); + }); + + return certificateRequests.stream() + .distinct() + .collect(Collectors.toUnmodifiableMap(x -> x, resolver)); + } + + public static Map resolveCertificateRequests( + CertificateRequest... certificateRequests) { + return resolveCertificateRequests(List.of(certificateRequests)); + } + + public static final class SignEnv { + + public SignEnv(List spec) { + Objects.requireNonNull(spec); + + spec.stream().map(keychain -> { + return keychain.keychain().name(); + }).collect(Collectors.toMap(x -> x, x -> x, (a, b) -> { + throw new IllegalArgumentException(String.format("Multiple keychains with the same name: %s", a)); + })); + + this.spec = List.copyOf(spec); + this.env = resolveCertificateRequests( + spec.stream().map(KeychainWithCertsSpec::certificateRequests).flatMap(Collection::stream).toList()); + } + + public SignEnv(KeychainWithCertsSpec... spec) { + this(List.of(spec)); + } + + public List keychains() { + return spec.stream().map(ResolvedKeychain::new).map(keychain -> { + return keychain.toMock(env); + }).toList(); + } + + public Map env() { + return env; + } + + private final Map env; + private final List spec; + } + + public static CommandMockSpec securityMock(SignEnv signEnv) { + var action = CommandActionSpec.create("/usr/bin/security", new MacSecurityMock(signEnv)); + return new CommandMockSpec(action.description(), CommandActionSpecs.build().action(action).create()); + } + + private static KeyPair createKeyPair() { + try { + var kpg = KeyPairGenerator.getInstance("RSA"); + return kpg.generateKeyPair(); + } catch (NoSuchAlgorithmException ex) { + throw ExceptionBox.toUnchecked(ex); + } + } + + // + // Reflection proxy for jdk.test.lib.security.CertificateBuilder class. + // + // Can't use it directly because it is impossible to cherry-pick this class from the JDK test lib in JUnit tests due to limitations of jtreg. + // + // Shared jpackage JUnit tests don't require "jdk.jpackage.test.stdmock", but they depend on "jdk.jpackage.test" package. + // Source code for these two packages resides in the same directory tree, so jtreg will pull in classes from both packages for the jpackage JUnit tests. + // Static dependency on jdk.test.lib.security.CertificateBuilder class will force pulling in the entire JDK test lib, because of jtreg limitations. + // + // Use dynamic dependency as a workaround. Tests that require jdk.test.lib.security.CertificateBuilder class, should have + // + // /* + // * ... + // * @library /test/lib + // * @build jdk.test.lib.security.CertificateBuilder + // */ + // + // in their declarations. They also should have + // + // --add-exports java.base/sun.security.x509=ALL-UNNAMED + // --add-exports java.base/sun.security.util=ALL-UNNAMED + // + // on javac and java command lines. + // + private static final class CertificateBuilder { + + CertificateBuilder() { + instance = toSupplier(ctor::newInstance).get(); + } + + CertificateBuilder setSubjectName(String v) { + toRunnable(() -> { + setSubjectName.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder setPublicKey(PublicKey v) { + toRunnable(() -> { + setPublicKey.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder setSerialNumber(BigInteger v) { + toRunnable(() -> { + setSerialNumber.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder addSubjectKeyIdExt(PublicKey v) { + toRunnable(() -> { + addSubjectKeyIdExt.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder addAuthorityKeyIdExt(PublicKey v) { + toRunnable(() -> { + addAuthorityKeyIdExt.invoke(instance, v); + }).run(); + return this; + } + + CertificateBuilder setValidity(Date from, Date to) { + toRunnable(() -> { + setValidity.invoke(instance, from, to); + }).run(); + return this; + } + + X509Certificate build(X509Certificate issuerCert, PrivateKey issuerKey) throws IOException, CertificateException { + try { + return (X509Certificate)toSupplier(() -> { + return build.invoke(instance, issuerCert, issuerKey); + }).get(); + } catch (ExceptionBox box) { + switch (ExceptionBox.unbox(box)) { + case IOException ex -> { + throw ex; + } + case CertificateException ex -> { + throw ex; + } + default -> { + throw box; + } + } + } + } + + private final Object instance; + + private static final Constructor ctor; + private static final Method setSubjectName; + private static final Method setPublicKey; + private static final Method setSerialNumber; + private static final Method addSubjectKeyIdExt; + private static final Method addAuthorityKeyIdExt; + private static final Method setValidity; + private static final Method build; + + static { + try { + var certificateBuilderClass = Class.forName("jdk.test.lib.security.CertificateBuilder"); + + ctor = certificateBuilderClass.getConstructor(); + + setSubjectName = certificateBuilderClass.getMethod("setSubjectName", String.class); + + setPublicKey = certificateBuilderClass.getMethod("setPublicKey", PublicKey.class); + + setSerialNumber = certificateBuilderClass.getMethod("setSerialNumber", BigInteger.class); + + addSubjectKeyIdExt = certificateBuilderClass.getMethod("addSubjectKeyIdExt", PublicKey.class); + + addAuthorityKeyIdExt = certificateBuilderClass.getMethod("addAuthorityKeyIdExt", PublicKey.class); + + setValidity = certificateBuilderClass.getMethod("setValidity", Date.class, Date.class); + + build = certificateBuilderClass.getMethod("build", X509Certificate.class, PrivateKey.class); + + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException ex) { + throw toUnchecked(ex); + } + } + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes index 40f73e624b6..0b98c051238 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes @@ -39,6 +39,16 @@ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 1234]; errors=[ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 256.1]; errors=[message.error-header+[error.msi-product-version-major-out-of-range, 256.1], message.advice-header+[error.version-string-wrong-format.advice]]) ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--launcher-as-service]; errors=[message.error-header+[error.missing-service-installer], message.advice-header+[error.missing-service-installer.advice]]) ErrorTest.test(args-add=[@foo]; errors=[message.error-header+[ERR_CannotParseOptions, foo]]) +ErrorTest.testMacSignWithoutIdentity(IMAGE; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(IMAGE; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_DMG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_DMG; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN], message.error-header+[error.cert.not.found, INSTALLER, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_APP_IMAGE_CERT@@]; args-del=[--name]; errors=[message.error-header+[error.cert.not.found, INSTALLER, KEYCHAIN_WITH_APP_IMAGE_CERT]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_PKG_CERT@@]; args-del=[--name]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, KEYCHAIN_WITH_PKG_CERT]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN], message.error-header+[error.cert.not.found, INSTALLER, EMPTY_KEYCHAIN]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_APP_IMAGE_CERT@@, --app-image, @@APP_IMAGE_WITH_SHORT_NAME@@]; errors=[message.error-header+[error.cert.not.found, INSTALLER, KEYCHAIN_WITH_APP_IMAGE_CERT]]) +ErrorTest.testMacSignWithoutIdentity(MAC_PKG; args-add=[--mac-sign, --mac-signing-keychain, @@KEYCHAIN_WITH_PKG_CERT@@, --app-image, @@APP_IMAGE_WITH_SHORT_NAME@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, KEYCHAIN_WITH_PKG_CERT]]) ErrorTest.testMacSigningIdentityValidation(IMAGE, --mac-app-image-sign-identity, true) ErrorTest.testMacSigningIdentityValidation(IMAGE, --mac-signing-key-user-name, false) ErrorTest.testMacSigningIdentityValidation(MAC_DMG, --mac-app-image-sign-identity, true) diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index 0f299cb5a24..3a257a425b6 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -82,10 +82,17 @@ public class SigningAppImageTest { SigningBase.StandardCertificateRequest.CODESIGN_UNICODE )) { for (var signIdentityType : SignKeyOption.Type.defaultValues()) { - data.add(new SignKeyOptionWithKeychain( - signIdentityType, - certRequest, - SigningBase.StandardKeychain.MAIN.keychain())); + SigningBase.StandardKeychain keychain; + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT) { + keychain = SigningBase.StandardKeychain.SINGLE; + if (!keychain.contains(certRequest)) { + continue; + } + } else { + keychain = SigningBase.StandardKeychain.MAIN; + } + + data.add(new SignKeyOptionWithKeychain(signIdentityType, certRequest, keychain.keychain())); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java index 2c7ae205e69..a6d94b59bd9 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java @@ -93,6 +93,11 @@ public class SigningAppImageTwoStepsTest { return new TestSpec(Optional.ofNullable(signAppImage), sign); } + Builder keychain(SigningBase.StandardKeychain v) { + keychain = Objects.requireNonNull(v); + return this; + } + Builder certRequest(SigningBase.StandardCertificateRequest v) { certRequest = Objects.requireNonNull(v); return this; @@ -117,9 +122,10 @@ public class SigningAppImageTwoStepsTest { return new SignKeyOptionWithKeychain( signIdentityType, certRequest, - SigningBase.StandardKeychain.MAIN.keychain()); + keychain.keychain()); } + private SigningBase.StandardKeychain keychain = SigningBase.StandardKeychain.MAIN; private SigningBase.StandardCertificateRequest certRequest = SigningBase.StandardCertificateRequest.CODESIGN; private SignKeyOption.Type signIdentityType = SignKeyOption.Type.SIGN_KEY_IDENTITY; @@ -144,18 +150,26 @@ public class SigningAppImageTwoStepsTest { }, signOption.keychain()); }, appImageCmd::execute); - var cmd = new JPackageCommand() - .setPackageType(PackageType.IMAGE) - .addArguments("--app-image", appImageCmd.outputBundle()) - .mutate(sign::addTo); + MacSign.withKeychain(keychain -> { + var cmd = new JPackageCommand() + .setPackageType(PackageType.IMAGE) + .addArguments("--app-image", appImageCmd.outputBundle()) + .mutate(sign::addTo); - cmd.executeAndAssertHelloAppImageCreated(); - MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + cmd.executeAndAssertHelloAppImageCreated(); + MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + }, sign.keychain()); } } public static Collection test() { + var signIdentityTypes = List.of( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SignKeyOption.Type.SIGN_KEY_IDENTITY_APP_IMAGE, + SignKeyOption.Type.SIGN_KEY_IMPLICIT + ); + List data = new ArrayList<>(); for (var appImageSign : withAndWithout(SignKeyOption.Type.SIGN_KEY_IDENTITY)) { @@ -167,9 +181,12 @@ public class SigningAppImageTwoStepsTest { .certRequest(SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD) .signAppImage(); }); - for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + for (var signIdentityType : signIdentityTypes) { builder.signIdentityType(signIdentityType) .certRequest(SigningBase.StandardCertificateRequest.CODESIGN); + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT) { + builder.keychain(SigningBase.StandardKeychain.SINGLE); + } data.add(builder.sign().create()); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningBase.java b/test/jdk/tools/jpackage/macosx/SigningBase.java index 5f4367e6096..e7e2d7e44cf 100644 --- a/test/jdk/tools/jpackage/macosx/SigningBase.java +++ b/test/jdk/tools/jpackage/macosx/SigningBase.java @@ -38,7 +38,7 @@ import jdk.jpackage.test.TKit; * @test * @summary Setup the environment for jpackage macos signing tests. * Creates required keychains and signing identities. - * Does NOT run any jpackag tests. + * Does NOT run any jpackage tests. * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @compile -Xlint:all -Werror SigningBase.java @@ -51,7 +51,7 @@ import jdk.jpackage.test.TKit; * @test * @summary Tear down the environment for jpackage macos signing tests. * Deletes required keychains and signing identities. - * Does NOT run any jpackag tests. + * Does NOT run any jpackage tests. * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @compile -Xlint:all -Werror SigningBase.java @@ -83,7 +83,7 @@ public class SigningBase { } private static CertificateRequest.Builder cert() { - return new CertificateRequest.Builder(); + return CertificateRequest.build(); } private final CertificateRequest spec; @@ -118,6 +118,12 @@ public class SigningBase { StandardCertificateRequest.PKG, StandardCertificateRequest.CODESIGN_COPY, StandardCertificateRequest.PKG_COPY), + /** + * A keychain with a single certificate for each role. + */ + SINGLE("jpackagerTest-single.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG), ; StandardKeychain(String keychainName, StandardCertificateRequest... certs) { @@ -145,7 +151,7 @@ public class SigningBase { } private static KeychainWithCertsSpec.Builder keychain(String name) { - return new KeychainWithCertsSpec.Builder().name(name); + return KeychainWithCertsSpec.build().name(name); } private static List signingEnv() { @@ -164,13 +170,13 @@ public class SigningBase { } public static void verifySignTestEnvReady() { - if (!Inner.SIGN_ENV_READY) { + if (!SignEnvReady.VALUE) { TKit.throwSkippedException(new IllegalStateException("Misconfigured signing test environment")); } } - private final class Inner { - private static final boolean SIGN_ENV_READY = MacSign.isDeployed(StandardKeychain.signingEnv()); + private final class SignEnvReady { + static final boolean VALUE = MacSign.isDeployed(StandardKeychain.signingEnv()); } private static final String NAME_ASCII = "jpackage.openjdk.java.net"; diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index 8a93ce5f749..d1c17fb61cd 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -94,7 +94,7 @@ public class SigningPackageTest { } public static Collection test() { - return TestSpec.testCases(true).stream().map(v -> { + return TestSpec.testCases().stream().map(v -> { return new Object[] {v}; }).toList(); } @@ -126,7 +126,10 @@ public class SigningPackageTest { } if (appImageSignOption.isEmpty()) { - if (packageSignOption.get().type() != SignKeyOption.Type.SIGN_KEY_IDENTITY) { + if (!List.of( + SignKeyOption.Type.SIGN_KEY_IDENTITY, + SignKeyOption.Type.SIGN_KEY_IDENTITY_SHA1 + ).contains(packageSignOption.get().type())) { // They request to sign the .pkg installer without // the "--mac-installer-sign-identity" option, // but didn't specify a signing option for the packaged app image. @@ -204,15 +207,61 @@ public class SigningPackageTest { } MacSign.ResolvedKeychain keychain() { - return SigningBase.StandardKeychain.MAIN.keychain(); + return chooseKeychain(Stream.of( + appImageSignOption.stream(), + packageSignOption.stream() + ).flatMap(x -> x).map(SignKeyOption::type).findFirst().orElseThrow()).keychain(); } - static List testCases(boolean withUnicode) { + /** + * Types of test cases to skip. + */ + enum SkipTestCases { + /** + * Skip test cases with signing identities/key names with symbols outside of the + * ASCII codepage. + */ + SKIP_UNICODE, + /** + * Skip test cases in which the value of the "--mac-signing-key-user-name" + * option is the full signing identity name. + */ + SKIP_SIGN_KEY_USER_FULL_NAME, + /** + * Skip test cases in which the value of the "--mac-installer-sign-identity" or + * "--mac-app-image-sign-identity" option is the SHA1 digest of the signing + * certificate. + */ + SKIP_SIGN_KEY_IDENTITY_SHA1, + ; + } + + static List minimalTestCases() { + return testCases(SkipTestCases.values()); + } + + static List testCases(SkipTestCases... skipTestCases) { + + final var skipTestCasesAsSet = Set.of(skipTestCases); + + final var signIdentityTypes = Stream.of(SignKeyOption.Type.defaultValues()).filter(v -> { + switch (v) { + case SIGN_KEY_USER_FULL_NAME -> { + return !skipTestCasesAsSet.contains(SkipTestCases.SKIP_SIGN_KEY_USER_FULL_NAME); + } + case SIGN_KEY_IDENTITY_SHA1 -> { + return !skipTestCasesAsSet.contains(SkipTestCases.SKIP_SIGN_KEY_IDENTITY_SHA1); + } + default -> { + return true; + } + } + }).toList(); List data = new ArrayList<>(); List> certRequestGroups; - if (withUnicode) { + if (!skipTestCasesAsSet.contains(SkipTestCases.SKIP_UNICODE)) { certRequestGroups = List.of( List.of(SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.PKG), List.of(SigningBase.StandardCertificateRequest.CODESIGN_UNICODE, SigningBase.StandardCertificateRequest.PKG_UNICODE) @@ -224,19 +273,33 @@ public class SigningPackageTest { } for (var certRequests : certRequestGroups) { - for (var signIdentityType : SignKeyOption.Type.defaultValues()) { - var keychain = SigningBase.StandardKeychain.MAIN.keychain(); + for (var signIdentityType : signIdentityTypes) { + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT + && !SigningBase.StandardKeychain.SINGLE.contains(certRequests.getFirst())) { + // Skip invalid test case: the keychain for testing signing without + // an explicitly specified signing key option doesn't have this signing key. + break; + } + + if (signIdentityType.passThrough() && !certRequests.contains(SigningBase.StandardCertificateRequest.CODESIGN)) { + // Using a pass-through signing option. + // Doesn't make sense to waste time on testing it with multiple certificates. + // Skip the test cases using non "default" certificate. + break; + } + + var keychain = chooseKeychain(signIdentityType).keychain(); var appImageSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getFirst(), keychain); var pkgSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getLast(), keychain); switch (signIdentityType) { - case SIGN_KEY_IDENTITY -> { + case SIGN_KEY_IDENTITY, SIGN_KEY_IDENTITY_SHA1 -> { // Use "--mac-installer-sign-identity" and "--mac-app-image-sign-identity" signing options. // They allows to sign the packaged app image and the installer (.pkg) separately. data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.empty(), PackageType.MAC)); data.add(new TestSpec(Optional.empty(), Optional.of(pkgSignKeyOption), PackageType.MAC_PKG)); } - case SIGN_KEY_USER_SHORT_NAME -> { + case SIGN_KEY_USER_SHORT_NAME, SIGN_KEY_IMPLICIT -> { // Use "--mac-signing-key-user-name" signing option with short user name or implicit signing option. // It signs both the packaged app image and the installer (.pkg). // Thus, if the installer is not signed, it can be used only with .dmg packaging. @@ -263,5 +326,13 @@ public class SigningPackageTest { return data; } + + private static SigningBase.StandardKeychain chooseKeychain(SignKeyOption.Type signIdentityType) { + if (signIdentityType == SignKeyOption.Type.SIGN_KEY_IMPLICIT) { + return SigningBase.StandardKeychain.SINGLE; + } else { + return SigningBase.StandardKeychain.MAIN; + } + } } } diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java index 23195c9a856..d0de9c8c704 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java @@ -108,7 +108,7 @@ public class SigningPackageTwoStepTest { appImageSignOption = Optional.empty(); } - for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + for (var signPackage : SigningPackageTest.TestSpec.minimalTestCases()) { data.add(new TwoStepsTestSpec(appImageSignOption, signPackage)); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index 604399ebd7a..54021aa2a0b 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -125,7 +125,7 @@ public class SigningRuntimeImagePackageTest { runtimeSignOption = Optional.empty(); } - for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + for (var signPackage : SigningPackageTest.TestSpec.minimalTestCases()) { data.add(new RuntimeTestSpec(runtimeSignOption, runtimeType, signPackage)); } } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index 86390a85068..2133823381c 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -26,7 +26,7 @@ import static java.util.stream.Collectors.toMap; import static jdk.internal.util.OperatingSystem.LINUX; import static jdk.internal.util.OperatingSystem.MACOS; import static jdk.internal.util.OperatingSystem.WINDOWS; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import static jdk.jpackage.test.JPackageCommand.makeAdvice; import static jdk.jpackage.test.JPackageCommand.makeError; @@ -45,6 +45,7 @@ import java.util.function.UnaryOperator; import java.util.regex.Pattern; import java.util.stream.IntStream; import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.TokenReplace; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; @@ -53,14 +54,27 @@ import jdk.jpackage.test.CannedArgument; import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.CertificateType; +import jdk.jpackage.test.MacSign.KeychainWithCertsSpec; +import jdk.jpackage.test.MacSign.ResolvedKeychain; +import jdk.jpackage.test.MacSign.StandardCertificateNamePrefix; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.mock.VerbatimCommandMock; +import jdk.jpackage.test.stdmock.JPackageMockUtils; +import jdk.jpackage.test.stdmock.MacSignMockUtils; /* * @test * @summary Test jpackage output for erroneous input * @library /test/jdk/tools/jpackage/helpers + * @library /test/lib * @build jdk.jpackage.test.* + * @build jdk.jpackage.test.stdmock.* + * @build jdk.test.lib.security.CertificateBuilder * @compile -Xlint:all -Werror ErrorTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ErrorTest @@ -71,7 +85,10 @@ import jdk.jpackage.test.TKit; * @test * @summary Test jpackage output for erroneous input * @library /test/jdk/tools/jpackage/helpers + * @library /test/lib * @build jdk.jpackage.test.* + * @build jdk.jpackage.test.stdmock.* + * @build jdk.test.lib.security.CertificateBuilder * @compile -Xlint:all -Werror ErrorTest.java * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ErrorTest @@ -81,10 +98,10 @@ import jdk.jpackage.test.TKit; public final class ErrorTest { enum Token { - JAVA_HOME(cmd -> { + JAVA_HOME(() -> { return System.getProperty("java.home"); }), - APP_IMAGE(cmd -> { + APP_IMAGE(() -> { final var appImageRoot = TKit.createTempDirectory("appimage"); final var appImageCmd = JPackageCommand.helloAppImage() @@ -92,28 +109,44 @@ public final class ErrorTest { appImageCmd.execute(); - return appImageCmd.outputBundle().toString(); + return appImageCmd.outputBundle(); }), - INVALID_MAC_RUNTIME_BUNDLE(toFunction(cmd -> { + APP_IMAGE_WITH_SHORT_NAME(() -> { + final var appImageRoot = TKit.createTempDirectory("appimage"); + + final var appImageCmd = JPackageCommand.helloAppImage() + .setFakeRuntime().setArgumentValue("--dest", appImageRoot); + + // Let jpackage pick the name from the main class (Hello). It qualifies as the "short" name. + appImageCmd.removeArgumentWithValue("--name"); + + appImageCmd.execute(); + + return appImageCmd.outputBundle(); + }), + INVALID_MAC_RUNTIME_BUNDLE(toSupplier(() -> { // Has "Contents/MacOS/libjli.dylib", but missing "Contents/Home/lib/libjli.dylib". final Path root = TKit.createTempDirectory("mac-invalid-runtime-bundle"); Files.createDirectories(root.resolve("Contents/Home")); Files.createFile(root.resolve("Contents/Info.plist")); Files.createDirectories(root.resolve("Contents/MacOS")); Files.createFile(root.resolve("Contents/MacOS/libjli.dylib")); - return root.toString(); + return root; })), - INVALID_MAC_RUNTIME_IMAGE(toFunction(cmd -> { + INVALID_MAC_RUNTIME_IMAGE(toSupplier(() -> { // Has some files in the "lib" subdirectory, but doesn't have the "lib/libjli.dylib" file. final Path root = TKit.createTempDirectory("mac-invalid-runtime-image"); Files.createDirectories(root.resolve("lib")); Files.createFile(root.resolve("lib/foo")); - return root.toString(); + return root; })), - EMPTY_DIR(toFunction(cmd -> { + EMPTY_DIR(() -> { return TKit.createTempDirectory("empty-dir"); - })), + }), ADD_LAUNCHER_PROPERTY_FILE, + EMPTY_KEYCHAIN, + KEYCHAIN_WITH_APP_IMAGE_CERT, + KEYCHAIN_WITH_PKG_CERT, ; private Token() { @@ -124,6 +157,12 @@ public final class ErrorTest { this.valueSupplier = Optional.of(valueSupplier); } + private Token(Supplier valueSupplier) { + this(_ -> { + return valueSupplier.get(); + }); + } + String token() { return makeToken(name()); } @@ -558,7 +597,7 @@ public final class ErrorTest { } public static Collection invalidAppVersion() { - return fromTestSpecBuilders(Stream.of( + return toTestArgs(Stream.of( // Invalid app version. Just cover all different error messages. // Extensive testing of invalid version strings is done in DottedVersionTest unit test. testSpec().addArgs("--app-version", "").error("error.version-string-empty"), @@ -601,7 +640,7 @@ public final class ErrorTest { argsStream = Stream.concat(argsStream, Stream.of(List.of("--win-console"))); } - return fromTestSpecBuilders(argsStream.map(args -> { + return toTestArgs(argsStream.map(args -> { var builder = testSpec().noAppDesc().nativeType() .addArgs("--runtime-image", Token.JAVA_HOME.token()) .addArgs(args); @@ -629,7 +668,7 @@ public final class ErrorTest { } public static Collection testAdditionLaunchers() { - return fromTestSpecBuilders(Stream.of( + return toTestArgs(Stream.of( testSpec().addArgs("--add-launcher", Token.ADD_LAUNCHER_PROPERTY_FILE.token()) .error("error.parameter-add-launcher-malformed", Token.ADD_LAUNCHER_PROPERTY_FILE, "--add-launcher"), testSpec().removeArgs("--name").addArgs("--name", "foo", "--add-launcher", "foo=" + Token.ADD_LAUNCHER_PROPERTY_FILE.token()) @@ -637,6 +676,184 @@ public final class ErrorTest { )); } + @Test(ifOS = MACOS) + @ParameterSupplier + @ParameterSupplier("testMacPkgSignWithoutIdentity") + public static void testMacSignWithoutIdentity(TestSpec spec) { + // The test called JPackage Command.useToolProviderBy Default(), + // which alters global variables in the test library, + // so run the test case with a new global state to isolate the alteration of the globals. + TKit.withNewState(() -> { + testMacSignWithoutIdentityWithNewTKitState(spec); + }); + } + + private static void testMacSignWithoutIdentityWithNewTKitState(TestSpec spec) { + final Token keychainToken = spec.expectedMessages().stream().flatMap(cannedStr -> { + return Stream.of(cannedStr.args()).filter(Token.class::isInstance).map(Token.class::cast).filter(token -> { + switch (token) { + case EMPTY_KEYCHAIN, KEYCHAIN_WITH_APP_IMAGE_CERT, KEYCHAIN_WITH_PKG_CERT -> { + return true; + } + default -> { + return false; + } + } + }); + }).distinct().reduce((a, b) -> { + throw new IllegalStateException(String.format( + "Error messages %s reference multiple keychains: %s and %s", spec.expectedMessages(), a, b)); + }).orElseThrow(); + + final ResolvedKeychain keychain; + + switch (keychainToken) { + case EMPTY_KEYCHAIN -> { + keychain = new ResolvedKeychain(new KeychainWithCertsSpec(MacSign.createEmptyKeychain(), List.of())); + } + case KEYCHAIN_WITH_APP_IMAGE_CERT, KEYCHAIN_WITH_PKG_CERT -> { + CertificateType existingCertType; + switch (keychainToken) { + case KEYCHAIN_WITH_APP_IMAGE_CERT -> { + existingCertType = CertificateType.CODE_SIGN; + } + case KEYCHAIN_WITH_PKG_CERT -> { + existingCertType = CertificateType.INSTALLER; + } + default -> { + throw new AssertionError(); + } + } + + keychain = Stream.of(SignEnvMock.SingleCertificateKeychain.values()).filter(k -> { + return k.certificateType() == existingCertType; + }).findFirst().orElseThrow().keychain(); + + var script = Script.build() + // Disable the mutation making mocks "run once". + .commandMockBuilderMutator(null) + // Replace "/usr/bin/security" with the mock bound to the keychain mock. + .map(MacSignMockUtils.securityMock(SignEnvMock.VALUE)) + // Don't mock other external commands. + .use(VerbatimCommandMock.INSTANCE) + .createLoop(); + + // Create jpackage tool provider using the /usr/bin/security mock. + var jpackage = JPackageMockUtils.createJPackageToolProvider(OperatingSystem.MACOS, script); + + // Override the default jpackage tool provider with the one using the /usr/bin/security mock. + JPackageCommand.useToolProviderByDefault(jpackage); + } + default -> { + throw new AssertionError(); + } + } + + MacSign.withKeychain(_ -> { + spec.mapExpectedMessages(cannedStr -> { + return cannedStr.mapArgs(arg -> { + switch (arg) { + case StandardCertificateNamePrefix certPrefix -> { + return certPrefix.value(); + } + case Token _ -> { + return keychain.name(); + } + default -> { + return arg; + } + } + }); + }).test(Map.of(keychainToken, _ -> keychain.name())); + }, keychain); + } + + public static Collection testMacSignWithoutIdentity() { + final List testCases = new ArrayList<>(); + + final var signArgs = List.of("--mac-sign", "--mac-signing-keychain", Token.EMPTY_KEYCHAIN.token()); + final var appImageArgs = List.of("--app-image", Token.APP_IMAGE_WITH_SHORT_NAME.token()); + + for (var withAppImage : List.of(true, false)) { + var builder = testSpec(); + if (withAppImage) { + builder.noAppDesc().addArgs(appImageArgs); + } + builder.addArgs(signArgs); + + for (var type: List.of(PackageType.IMAGE, PackageType.MAC_PKG, PackageType.MAC_DMG)) { + builder.setMessages().error("error.cert.not.found", + MacSign.StandardCertificateNamePrefix.CODE_SIGN, Token.EMPTY_KEYCHAIN); + switch (type) { + case MAC_PKG -> { + // jpackage must report two errors: + // 1. It can't find signing identity to sign the app image + // 2. It can't find signing identity to sign the PKG installer + builder.error("error.cert.not.found", + MacSign.StandardCertificateNamePrefix.INSTALLER, Token.EMPTY_KEYCHAIN); + } + default -> { + // NOP + } + } + var testSpec = builder.type(type).create(); + testCases.add(testSpec); + } + } + + return toTestArgs(testCases); + } + + public static Collection testMacPkgSignWithoutIdentity() { + final List testCases = new ArrayList<>(); + + final var appImageArgs = List.of("--app-image", Token.APP_IMAGE_WITH_SHORT_NAME.token()); + + for (var withAppImage : List.of(true, false)) { + for (var existingCertType : CertificateType.values()) { + Token keychain; + StandardCertificateNamePrefix missingCertificateNamePrefix; + switch (existingCertType) { + case INSTALLER -> { + keychain = Token.KEYCHAIN_WITH_PKG_CERT; + missingCertificateNamePrefix = StandardCertificateNamePrefix.CODE_SIGN; + } + case CODE_SIGN -> { + keychain = Token.KEYCHAIN_WITH_APP_IMAGE_CERT; + missingCertificateNamePrefix = StandardCertificateNamePrefix.INSTALLER; + } + default -> { + throw new AssertionError(); + } + } + + var builder = testSpec() + .type(PackageType.MAC_PKG) + .addArgs("--mac-sign", "--mac-signing-keychain", keychain.token()) + .error("error.cert.not.found", missingCertificateNamePrefix, keychain); + + if (withAppImage) { + builder.noAppDesc().addArgs(appImageArgs); + } else { + /* + * Use shorter name to avoid + * + * [03:08:55.623] --mac-package-name is set to 'MacSignWithoutIdentityErrorTest', which is longer than 16 characters. For a better Mac experience consider shortening it. + * + * in the output. + * The same idea is behind using the "APP_IMAGE_WITH_SHORT_NAME" token + * instead of the "APP_IMAGE" for the predefined app image. + */ + builder.removeArgs("--name"); + } + + testCases.add(builder); + } + } + + return toTestArgs(testCases); + } + @Test @ParameterSupplier("invalidNames") public static void testInvalidAppName(InvalidName name) { @@ -1007,8 +1224,14 @@ public final class ErrorTest { ); } - private static Collection toTestArgs(Stream stream) { - return stream.filter(v -> { + private static Collection toTestArgs(Stream stream) { + return stream.map(v -> { + if (v instanceof TestSpec.Builder builder) { + return builder.create(); + } else { + return v; + } + }).filter(v -> { if (v instanceof TestSpec ts) { return ts.isSupported(); } else { @@ -1019,8 +1242,8 @@ public final class ErrorTest { }).toList(); } - private static Collection fromTestSpecBuilders(Stream stream) { - return toTestArgs(stream.map(TestSpec.Builder::create)); + private static Collection toTestArgs(Collection col) { + return toTestArgs(col.stream()); } private static String adjustTextStreamVerifierArg(String str) { @@ -1028,4 +1251,40 @@ public final class ErrorTest { } private static final Pattern LINE_SEP_REGEXP = Pattern.compile("\\R"); + + private final class SignEnvMock { + + enum SingleCertificateKeychain { + FOO(CertificateType.CODE_SIGN), + BAR(CertificateType.INSTALLER), + ; + + SingleCertificateKeychain(CertificateType certificateType) { + this.keychain = KeychainWithCertsSpec.build() + .name(name().toLowerCase() + ".keychain") + .addCert(CertificateRequest.build() + .userName(name().toLowerCase()) + .type(Objects.requireNonNull(certificateType))) + .create(); + } + + static List signingEnv() { + return Stream.of(values()).map(v -> { + return v.keychain; + }).toList(); + } + + CertificateType certificateType() { + return keychain.certificateRequests().getFirst().type(); + } + + ResolvedKeychain keychain() { + return new ResolvedKeychain(keychain).toMock(VALUE.env()); + } + + private final KeychainWithCertsSpec keychain; + } + + static final MacSignMockUtils.SignEnv VALUE = new MacSignMockUtils.SignEnv(SingleCertificateKeychain.signingEnv()); + } } From d887e2e6fdcb0070a5f881098120074d972ee3df Mon Sep 17 00:00:00 2001 From: Ben Taylor Date: Fri, 27 Feb 2026 00:37:13 +0000 Subject: [PATCH 076/636] 8377713: Shenandoah: Convert ShenandoahReferenceProcessor to use Atomic Reviewed-by: shade, wkemper --- .../shenandoahReferenceProcessor.cpp | 22 +++++++++---------- .../shenandoahReferenceProcessor.hpp | 5 +++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 7187431c8f8..37e9729b7ff 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -504,7 +504,7 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLoc if (!CompressedOops::is_null(*list)) { oop head = lrb(CompressedOops::decode_not_null(*list)); shenandoah_assert_not_in_cset_except(&head, head, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); - oop prev = AtomicAccess::xchg(&_pending_list, head); + oop prev = _pending_list.exchange(head); set_oop_field(p, prev); if (prev == nullptr) { // First to prepend to list, record tail @@ -519,14 +519,14 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLoc void ShenandoahReferenceProcessor::work() { // Process discovered references uint max_workers = ShenandoahHeap::heap()->max_workers(); - uint worker_id = AtomicAccess::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1; + uint worker_id = _iterate_discovered_list_id.fetch_then_add(1U, memory_order_relaxed); while (worker_id < max_workers) { if (UseCompressedOops) { process_references(_ref_proc_thread_locals[worker_id], worker_id); } else { process_references(_ref_proc_thread_locals[worker_id], worker_id); } - worker_id = AtomicAccess::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1; + worker_id = _iterate_discovered_list_id.fetch_then_add(1U, memory_order_relaxed); } } @@ -559,7 +559,7 @@ public: void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Phase phase, WorkerThreads* workers, bool concurrent) { - AtomicAccess::release_store_fence(&_iterate_discovered_list_id, 0U); + _iterate_discovered_list_id.release_store_fence(0U); // Process discovered lists ShenandoahReferenceProcessorTask task(phase, concurrent, this); @@ -576,7 +576,7 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Ph void ShenandoahReferenceProcessor::enqueue_references_locked() { // Prepend internal pending list to external pending list - shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); + shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list.load_relaxed(), ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); // During reference processing, we maintain a local list of references that are identified by // _pending_list and _pending_list_tail. _pending_list_tail points to the next field of the last Reference object on @@ -589,7 +589,7 @@ void ShenandoahReferenceProcessor::enqueue_references_locked() { // 2. Overwriting the next field of the last Reference on my local list to point at the previous head of the // global Universe::_reference_pending_list - oop former_head_of_global_list = Universe::swap_reference_pending_list(_pending_list); + oop former_head_of_global_list = Universe::swap_reference_pending_list(_pending_list.load_relaxed()); if (UseCompressedOops) { set_oop_field(reinterpret_cast(_pending_list_tail), former_head_of_global_list); } else { @@ -598,7 +598,7 @@ void ShenandoahReferenceProcessor::enqueue_references_locked() { } void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) { - if (_pending_list == nullptr) { + if (_pending_list.load_relaxed() == nullptr) { // Nothing to enqueue return; } @@ -616,7 +616,7 @@ void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) { } // Reset internal pending list - _pending_list = nullptr; + _pending_list.store_relaxed(nullptr); _pending_list_tail = &_pending_list; } @@ -640,9 +640,9 @@ void ShenandoahReferenceProcessor::abandon_partial_discovery() { clean_discovered_list(_ref_proc_thread_locals[index].discovered_list_addr()); } } - if (_pending_list != nullptr) { - oop pending = _pending_list; - _pending_list = nullptr; + if (_pending_list.load_relaxed() != nullptr) { + oop pending = _pending_list.load_relaxed(); + _pending_list.store_relaxed(nullptr); if (UseCompressedOops) { narrowOop* list = reference_discovered_addr(pending); clean_discovered_list(list); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp index 14adb924585..01c79029132 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp @@ -31,6 +31,7 @@ #include "gc/shared/referenceProcessorStats.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class ShenandoahMarkRefsSuperClosure; class WorkerThreads; @@ -133,10 +134,10 @@ private: ShenandoahRefProcThreadLocal* _ref_proc_thread_locals; - oop _pending_list; + Atomic _pending_list; void* _pending_list_tail; // T* - volatile uint _iterate_discovered_list_id; + Atomic _iterate_discovered_list_id; ReferenceProcessorStats _stats; From 538bebf76e812058145f7a3f5591cbf1c2f756c7 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Fri, 27 Feb 2026 00:52:18 +0000 Subject: [PATCH 077/636] 8376253: [macOS] FileSystemView may not report system icons when -Xcheck:jni is enabled Reviewed-by: prr, dnguyen --- .../macosx/native/libawt_lwawt/awt/CImage.m | 4 +- .../SystemIconPixelDataTest.java | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconPixelDataTest.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m index 051588f95bf..fa534fff275 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CImage.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -270,7 +270,7 @@ JNI_COCOA_ENTER(env); NSRect fromRect = NSMakeRect(0, 0, sw, sh); NSRect toRect = NSMakeRect(0, 0, dw, dh); CImage_CopyNSImageIntoArray(img, dst, fromRect, toRect); - (*env)->ReleasePrimitiveArrayCritical(env, buffer, dst, JNI_ABORT); + (*env)->ReleasePrimitiveArrayCritical(env, buffer, dst, 0); } JNI_COCOA_EXIT(env); diff --git a/test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconPixelDataTest.java b/test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconPixelDataTest.java new file mode 100644 index 00000000000..c71c7a3e6a9 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/FileSystemView/SystemIconPixelDataTest.java @@ -0,0 +1,93 @@ +/* + * Copyright Amazon.com Inc. 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. + */ + +import java.awt.EventQueue; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.swing.Icon; +import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.filechooser.FileSystemView; + +/** + * @test + * @bug 8376253 + * @summary FileSystemView may not report system icons when -Xcheck:jni enabled + * @key headful + * @run main SystemIconPixelDataTest + * @run main/othervm -Xcheck:jni SystemIconPixelDataTest + */ +public final class SystemIconPixelDataTest { + + public static void main(String[] args) throws Exception { + for (LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { + AtomicBoolean ok = new AtomicBoolean(); + EventQueue.invokeAndWait(() -> ok.set(setLookAndFeel(laf))); + if (ok.get()) { + EventQueue.invokeAndWait(SystemIconPixelDataTest::test); + } + } + } + + private static boolean setLookAndFeel(LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + System.out.println("LookAndFeel: " + laf.getClassName()); + return true; + } catch (UnsupportedLookAndFeelException ignored) { + return false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void test() { + FileSystemView fsv = FileSystemView.getFileSystemView(); + File home = fsv.getHomeDirectory(); + Icon icon = fsv.getSystemIcon(home); + if (icon == null) { + return; + } + int w = icon.getIconWidth(); + int h = icon.getIconHeight(); + if (w <= 0 || h <= 0) { + throw new RuntimeException("Invalid icon size: " + w + "x" + h); + } + var img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + icon.paintIcon(null, g, 0, 0); + g.dispose(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (img.getRGB(x, y) != 0) { + return; + } + } + } + throw new RuntimeException("All pixels are zero"); + } +} From 1c6e7ffee4f136d769a050c28ab2aeaa30643eac Mon Sep 17 00:00:00 2001 From: Alex Menkov Date: Fri, 27 Feb 2026 01:56:03 +0000 Subject: [PATCH 078/636] 8377845: Restore regtest for JDK-8324881 with DiagnoseSyncOnValueBasedClasses=2 Reviewed-by: sspitsyn, lmesnik --- test/jdk/com/sun/jdi/EATests.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/jdk/com/sun/jdi/EATests.java b/test/jdk/com/sun/jdi/EATests.java index cb51e91021b..3a936f288cc 100644 --- a/test/jdk/com/sun/jdi/EATests.java +++ b/test/jdk/com/sun/jdi/EATests.java @@ -84,6 +84,15 @@ * @comment Regression test for using the wrong thread when logging during re-locking from deoptimization. * * @comment DiagnoseSyncOnValueBasedClasses=2 will cause logging when locking on \@ValueBased objects. + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:DiagnoseSyncOnValueBasedClasses=2 * * @comment Re-lock may inflate monitors when re-locking, which cause monitorinflation trace logging. * @run driver EATests From d7f4365b296d120521e16666e2ce2177a8d2c44d Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 27 Feb 2026 02:33:54 +0000 Subject: [PATCH 079/636] 8378561: Mark gc/shenandoah/compiler/TestLinkToNativeRBP.java as /native Reviewed-by: shade, wkemper --- .../jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java index 96440ba15ae..2877e8a4a85 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java @@ -30,7 +30,7 @@ * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "ppc64" | os.arch == "ppc64le" * @requires vm.gc.Shenandoah * - * @run main/othervm --enable-native-access=ALL-UNNAMED -XX:+UnlockDiagnosticVMOptions + * @run main/native/othervm --enable-native-access=ALL-UNNAMED -XX:+UnlockDiagnosticVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive TestLinkToNativeRBP * */ From 6daca7ef99b5333be9dd074ff848783807080884 Mon Sep 17 00:00:00 2001 From: Renjith Kannath Pariyangad Date: Fri, 27 Feb 2026 03:15:27 +0000 Subject: [PATCH 080/636] 8268675: RTE from "Printable.print" propagates through "PrinterJob.print" Reviewed-by: psadhukhan, prr --- .../classes/sun/print/RasterPrinterJob.java | 7 ++++- .../ExceptionFromPrintableIsIgnoredTest.java | 29 +++++++------------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index 754af87b94e..32728efde6c 100644 --- a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -1610,6 +1610,11 @@ public abstract class RasterPrinterJob extends PrinterJob { cancelDoc(); } + } catch (PrinterException pe) { + throw pe; + } catch (Throwable printError) { + throw (PrinterException) + new PrinterException().initCause(printError.getCause()); } finally { // reset previousPaper in case this job is invoked again. previousPaper = null; diff --git a/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java b/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java index a62ae536e1b..4c2ff370cb8 100644 --- a/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java +++ b/test/jdk/java/awt/print/PrinterJob/ExceptionFromPrintableIsIgnoredTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 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 @@ -21,15 +21,16 @@ * questions. */ -/* @test - @bug 8262731 - @key headful printer - @summary Verify that "PrinterJob.print" throws the expected exception, - if "Printable.print" throws an exception. - @run main ExceptionFromPrintableIsIgnoredTest MAIN PE - @run main ExceptionFromPrintableIsIgnoredTest MAIN RE - @run main ExceptionFromPrintableIsIgnoredTest EDT PE - @run main ExceptionFromPrintableIsIgnoredTest EDT RE +/* + * @test + * @bug 8262731 8268675 + * @key printer + * @summary Verify that "PrinterJob.print" throws the expected exception, + * if "Printable.print" throws an exception. + * @run main ExceptionFromPrintableIsIgnoredTest MAIN PE + * @run main ExceptionFromPrintableIsIgnoredTest MAIN RE + * @run main ExceptionFromPrintableIsIgnoredTest EDT PE + * @run main ExceptionFromPrintableIsIgnoredTest EDT RE */ import java.awt.Graphics; @@ -64,14 +65,6 @@ public class ExceptionFromPrintableIsIgnoredTest { "Test started. threadType='%s', exceptionType='%s'", threadType, exceptionType)); - String osName = System.getProperty("os.name"); - boolean isOSX = osName.toLowerCase().startsWith("mac"); - if ((exceptionType == TestExceptionType.RE) && !isOSX) { - System.out.println( - "Currently this test scenario can be verified only on macOS."); - return; - } - printError = null; if (threadType == TestThreadType.MAIN) { From dc06fede2af2f10011695b0539b6f4d2cb1f07df Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Fri, 27 Feb 2026 04:47:48 +0000 Subject: [PATCH 081/636] 8373367: interp-only mechanism fails to work for carrier threads in a corner case Reviewed-by: amenkov, lmesnik --- src/hotspot/share/prims/jvmtiEnvBase.cpp | 4 +-- .../share/prims/jvmtiEnvThreadState.cpp | 9 ++---- .../share/prims/jvmtiEnvThreadState.hpp | 4 +-- .../share/prims/jvmtiEventController.cpp | 31 +++++++++++++------ src/hotspot/share/prims/jvmtiThreadState.cpp | 29 ++++++++--------- src/hotspot/share/prims/jvmtiThreadState.hpp | 15 ++++++--- .../share/prims/jvmtiThreadState.inline.hpp | 21 ++++++------- 7 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 4894a4dd21a..401bb4dfdb8 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -2491,7 +2491,7 @@ SetOrClearFramePopClosure::do_thread(Thread *target) { _result = JVMTI_ERROR_NO_MORE_FRAMES; return; } - assert(_state->get_thread_or_saved() == java_thread, "Must be"); + assert(_state->get_thread() == java_thread, "Must be"); RegisterMap reg_map(java_thread, RegisterMap::UpdateMap::include, diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp index 571c4ca5528..303923076b1 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -151,11 +151,6 @@ bool JvmtiEnvThreadState::is_virtual() { return _state->is_virtual(); } -// Use _thread_saved if cthread is detached from JavaThread (_thread == nullptr). -JavaThread* JvmtiEnvThreadState::get_thread_or_saved() { - return _state->get_thread_or_saved(); -} - JavaThread* JvmtiEnvThreadState::get_thread() { return _state->get_thread(); } @@ -344,7 +339,7 @@ void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool ena if (enabled) { // If enabling breakpoint, no need to reset. // Can't do anything if empty stack. - JavaThread* thread = get_thread_or_saved(); + JavaThread* thread = get_thread(); if (event_type == JVMTI_EVENT_SINGLE_STEP && ((thread == nullptr && is_virtual()) || thread->has_last_Java_frame())) { diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp index a2ab25a59dd..16b369e0857 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -170,8 +170,6 @@ public: inline JvmtiThreadState* jvmti_thread_state() { return _state; } - // use _thread_saved if cthread is detached from JavaThread - JavaThread *get_thread_or_saved(); JavaThread *get_thread(); inline JvmtiEnv *get_env() { return _env; } diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index 9df3bbb4b3e..cb44b833c48 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -217,6 +217,10 @@ class EnterInterpOnlyModeClosure : public HandshakeClosure { assert(state != nullptr, "sanity check"); assert(state->get_thread() == jt, "handshake unsafe conditions"); + assert(jt->jvmti_thread_state() == state, "sanity check"); + assert(!jt->is_interp_only_mode(), "sanity check"); + assert(!state->is_interp_only_mode(), "sanity check"); + if (!state->is_pending_interp_only_mode()) { _completed = true; return; // The pending flag has been already cleared, so bail out. @@ -361,7 +365,8 @@ void VM_ChangeSingleStep::doit() { void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state) { EC_TRACE(("[%s] # Entering interpreter only mode", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()))); + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + JavaThread *target = state->get_thread(); Thread *current = Thread::current(); @@ -371,8 +376,13 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state } // This flag will be cleared in EnterInterpOnlyModeClosure handshake. state->set_pending_interp_only_mode(true); - if (target == nullptr) { // an unmounted virtual thread - return; // EnterInterpOnlyModeClosure will be executed right after mount. + + // There are two cases when entering interp_only_mode is postponed: + // 1. Unmounted virtual thread - EnterInterpOnlyModeClosure::do_thread will be executed at mount; + // 2. Carrier thread with mounted virtual thread - EnterInterpOnlyModeClosure::do_thread will be executed at unmount. + if (target == nullptr || // an unmounted virtual thread + JvmtiEnvBase::is_thread_carrying_vthread(target, state->get_thread_oop())) { // a vthread carrying thread + return; // EnterInterpOnlyModeClosure will be executed right after mount or unmount. } EnterInterpOnlyModeClosure hs(state); if (target->is_handshake_safe_for(current)) { @@ -388,7 +398,8 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state void JvmtiEventControllerPrivate::leave_interp_only_mode(JvmtiThreadState *state) { EC_TRACE(("[%s] # Leaving interpreter only mode", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()))); + JvmtiTrace::safe_get_thread_name(state->get_thread()))); + if (state->is_pending_interp_only_mode()) { state->set_pending_interp_only_mode(false); // Just clear the pending flag. assert(!state->is_interp_only_mode(), "sanity check"); @@ -409,7 +420,7 @@ JvmtiEventControllerPrivate::trace_changed(JvmtiThreadState *state, jlong now_en if (changed & bit) { // it changed, print it log_trace(jvmti)("[%s] # %s event %s", - JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(state->get_thread()), (now_enabled & bit)? "Enabling" : "Disabling", JvmtiTrace::event_name((jvmtiEvent)ei)); } } @@ -932,7 +943,7 @@ JvmtiEventControllerPrivate::set_user_enabled(JvmtiEnvBase *env, JavaThread *thr void JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { EC_TRACE(("[%s] # set frame pop - frame=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number() )); ets->get_frame_pops()->set(fpop); @@ -943,7 +954,7 @@ JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFrameP void JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { EC_TRACE(("[%s] # clear frame pop - frame=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number() )); ets->get_frame_pops()->clear(fpop); @@ -953,7 +964,7 @@ JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFram void JvmtiEventControllerPrivate::clear_all_frame_pops(JvmtiEnvThreadState *ets) { EC_TRACE(("[%s] # clear all frame pops", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()) + JvmtiTrace::safe_get_thread_name(ets->get_thread()) )); ets->get_frame_pops()->clear_all(); @@ -965,7 +976,7 @@ JvmtiEventControllerPrivate::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiF int cleared_cnt = ets->get_frame_pops()->clear_to(fpop); EC_TRACE(("[%s] # clear to frame pop - frame=%d, count=%d", - JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()), + JvmtiTrace::safe_get_thread_name(ets->get_thread()), fpop.frame_number(), cleared_cnt )); diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index d7accfd9a0a..32bf2c4e98e 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -57,7 +57,6 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) : _thread_event_enable() { assert(JvmtiThreadState_lock->is_locked(), "sanity check"); _thread = thread; - _thread_saved = nullptr; _exception_state = ES_CLEARED; _hide_single_stepping = false; _pending_interp_only_mode = false; @@ -118,11 +117,11 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) if (thread != nullptr) { if (thread_oop == nullptr || thread->jvmti_vthread() == nullptr || thread->jvmti_vthread() == thread_oop) { - // The JavaThread for carrier or mounted virtual thread case. + // The JavaThread for an active carrier or a mounted virtual thread case. // Set this only if thread_oop is current thread->jvmti_vthread(). thread->set_jvmti_thread_state(this); + assert(!thread->is_interp_only_mode(), "sanity check"); } - thread->set_interp_only_mode(false); } } @@ -135,7 +134,10 @@ JvmtiThreadState::~JvmtiThreadState() { } // clear this as the state for the thread + assert(get_thread() != nullptr, "sanity check"); + assert(get_thread()->jvmti_thread_state() == this, "sanity check"); get_thread()->set_jvmti_thread_state(nullptr); + get_thread()->set_interp_only_mode(false); // zap our env thread states { @@ -323,6 +325,9 @@ void JvmtiThreadState::enter_interp_only_mode() { assert(_thread != nullptr, "sanity check"); assert(JvmtiThreadState_lock->is_locked(), "sanity check"); assert(!is_interp_only_mode(), "entering interp only when in interp only mode"); + assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check"); + assert(_thread->jvmti_thread_state() == this, "sanity check"); + _saved_interp_only_mode = true; _thread->set_interp_only_mode(true); invalidate_cur_stack_depth(); } @@ -330,10 +335,9 @@ void JvmtiThreadState::enter_interp_only_mode() { void JvmtiThreadState::leave_interp_only_mode() { assert(JvmtiThreadState_lock->is_locked(), "sanity check"); assert(is_interp_only_mode(), "leaving interp only when not in interp only mode"); - if (_thread == nullptr) { - // Unmounted virtual thread updates the saved value. - _saved_interp_only_mode = false; - } else { + _saved_interp_only_mode = false; + if (_thread != nullptr && _thread->jvmti_thread_state() == this) { + assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check"); _thread->set_interp_only_mode(false); } } @@ -341,7 +345,7 @@ void JvmtiThreadState::leave_interp_only_mode() { // Helper routine used in several places int JvmtiThreadState::count_frames() { - JavaThread* thread = get_thread_or_saved(); + JavaThread* thread = get_thread(); javaVFrame *jvf; ResourceMark rm; if (thread == nullptr) { @@ -578,11 +582,8 @@ void JvmtiThreadState::update_thread_oop_during_vm_start() { } } +// For virtual threads only. void JvmtiThreadState::set_thread(JavaThread* thread) { - _thread_saved = nullptr; // Common case. - if (!_is_virtual && thread == nullptr) { - // Save JavaThread* if carrier thread is being detached. - _thread_saved = _thread; - } + assert(is_virtual(), "sanity check"); _thread = thread; } diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 866d8828c4c..fa77518a8b6 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -123,8 +123,11 @@ class JvmtiVTSuspender : AllStatic { class JvmtiThreadState : public CHeapObj { private: friend class JvmtiEnv; + // The _thread field is a link to the JavaThread associated with JvmtiThreadState. + // A platform (including carrier) thread should always have a stable link to its JavaThread. + // The _thread field of a virtual thread should point to the JavaThread when + // virtual thread is mounted. It should be set to null when it is unmounted. JavaThread *_thread; - JavaThread *_thread_saved; OopHandle _thread_oop_h; // Jvmti Events that cannot be posted in their current context. JvmtiDeferredEventQueue* _jvmti_event_queue; @@ -219,7 +222,7 @@ class JvmtiThreadState : public CHeapObj { // Used by the interpreter for fullspeed debugging support bool is_interp_only_mode() { - return _thread == nullptr ? _saved_interp_only_mode : _thread->is_interp_only_mode(); + return _saved_interp_only_mode; } void enter_interp_only_mode(); void leave_interp_only_mode(); @@ -248,8 +251,10 @@ class JvmtiThreadState : public CHeapObj { int count_frames(); - inline JavaThread *get_thread() { return _thread; } - inline JavaThread *get_thread_or_saved(); // return _thread_saved if _thread is null + inline JavaThread *get_thread() { + assert(is_virtual() || _thread != nullptr, "sanity check"); + return _thread; + } // Needed for virtual threads as they can migrate to different JavaThread's. // Also used for carrier threads to clear/restore _thread. diff --git a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp index 831a2369a7e..aa81463a696 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -130,22 +130,21 @@ inline JvmtiThreadState* JvmtiThreadState::state_for(JavaThread *thread, Handle return state; } -inline JavaThread* JvmtiThreadState::get_thread_or_saved() { - // Use _thread_saved if cthread is detached from JavaThread (_thread == null). - return (_thread == nullptr && !is_virtual()) ? _thread_saved : _thread; -} - inline void JvmtiThreadState::set_should_post_on_exceptions(bool val) { - get_thread_or_saved()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE); + get_thread()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE); } inline void JvmtiThreadState::unbind_from(JvmtiThreadState* state, JavaThread* thread) { if (state == nullptr) { + assert(!thread->is_interp_only_mode(), "sanity check"); return; } - // Save thread's interp_only_mode. - state->_saved_interp_only_mode = thread->is_interp_only_mode(); - state->set_thread(nullptr); // Make sure stale _thread value is never used. + assert(thread->jvmti_thread_state() == state, "sanity check"); + assert(state->get_thread() == thread, "sanity check"); + assert(thread->is_interp_only_mode() == state->_saved_interp_only_mode, "sanity check"); + if (state->is_virtual()) { // clean _thread link for virtual threads only + state->set_thread(nullptr); // make sure stale _thread value is never used + } } inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* thread) { @@ -158,7 +157,7 @@ inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* threa // Bind JavaThread to JvmtiThreadState. thread->set_jvmti_thread_state(state); - if (state != nullptr) { + if (state != nullptr && state->is_virtual()) { // Bind to JavaThread. state->set_thread(thread); } From 463b9e00ce9e348164d8a6eebe27808bb1e93162 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 27 Feb 2026 06:06:29 +0000 Subject: [PATCH 082/636] 8078744: Right half of system menu icon on title bar does not activate when clicked in Metal L&F Reviewed-by: tr, dnguyen --- .../swing/plaf/metal/MetalTitlePane.java | 3 +- .../swing/plaf/metal/MetalTitlePaneBug.java | 65 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 test/jdk/javax/swing/plaf/metal/MetalTitlePaneBug.java diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java index ae8b379a579..4ed6734df1f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalTitlePane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -437,6 +437,7 @@ class MetalTitlePane extends JComponent { */ private JMenu createMenu() { JMenu menu = new JMenu(""); + menu.setPreferredSize(new Dimension(IMAGE_WIDTH, IMAGE_HEIGHT)); if (getWindowDecorationStyle() == JRootPane.FRAME) { addMenuItems(menu); } diff --git a/test/jdk/javax/swing/plaf/metal/MetalTitlePaneBug.java b/test/jdk/javax/swing/plaf/metal/MetalTitlePaneBug.java new file mode 100644 index 00000000000..dde6249752d --- /dev/null +++ b/test/jdk/javax/swing/plaf/metal/MetalTitlePaneBug.java @@ -0,0 +1,65 @@ +/* + * 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 8078744 + * @summary Verifies right half of system menu icon on title bar + * activates when clicked + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MetalTitlePaneBug + */ + +import javax.swing.JFrame; +import javax.swing.UIManager; + +public class MetalTitlePaneBug { + + static final String INSTRUCTIONS = """ + A Frame is shown with a titlepane. + Click on the left edge of the system menu icon on the title pane. + It should show "Restore", "Minimize", "Maximize", "Close" menu. + Click on the right edge of the system menu icon. + It should also show "Restore", "Minimize", "Maximize", "Close" menu. + If it shows, press Pass else press Fail. + """; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(MetalTitlePaneBug::createUI) + .build() + .awaitAndCheck(); + } + + static JFrame createUI() { + JFrame.setDefaultLookAndFeelDecorated(true); + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(200, 100); + return frame; + } +} From f6c69cadc7c622028fb02cef1b419f54ac05c85e Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Fri, 27 Feb 2026 06:44:51 +0000 Subject: [PATCH 083/636] 8378379: Remove reference to obsolete jdk.net.usePlainSocketImpl property from SSLSocketReset test Reviewed-by: coffeys --- .../sun/security/ssl/SSLSocketImpl/SSLSocketReset.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java index 6b0f365edc7..64779facd26 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketReset.java @@ -21,16 +21,14 @@ * questions. */ -// -// Please run in othervm mode. SunJSSE does not support dynamic system -// properties, no way to re-use system properties in samevm/agentvm mode. -// - /* * @test * @bug 8268965 * @summary Socket reset issue for TLS socket close - * @run main/othervm -Djdk.net.usePlainSocketImpl=true SSLSocketReset + * @comment The test uses SSLContext.getDefault(), so we use othervm to prevent + * usage of unexpected default SSLContext that might be set by some + * other test + * @run main/othervm SSLSocketReset */ import javax.net.ssl.*; From 94a34a32aa723e4620f4ef4700b3e20d6ab9bf62 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Fri, 27 Feb 2026 09:51:40 +0000 Subject: [PATCH 084/636] 8377895: Create sizeof_auto to reduce narrowing conversions Reviewed-by: kbarrett, jsjolen, dlong, aboldtch, stefank, ayang --- src/hotspot/share/oops/cpCache.hpp | 2 +- .../share/utilities/globalDefinitions.hpp | 23 ++++++++++++ .../utilities/test_globalDefinitions.cpp | 35 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/oops/cpCache.hpp b/src/hotspot/share/oops/cpCache.hpp index e9e4f9a40e5..14202f226d1 100644 --- a/src/hotspot/share/oops/cpCache.hpp +++ b/src/hotspot/share/oops/cpCache.hpp @@ -196,7 +196,7 @@ class ConstantPoolCache: public MetaspaceObj { #endif public: - static int size() { return align_metadata_size(sizeof(ConstantPoolCache) / wordSize); } + static int size() { return align_metadata_size(sizeof_auto(ConstantPoolCache) / wordSize); } private: // Helpers diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index ea4f6f99ae6..9560d863a2c 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -168,6 +168,29 @@ class oopDesc; #define SIZE_FORMAT_X_0 "0x%08" PRIxPTR #endif // _LP64 + +template +constexpr auto sizeof_auto_impl() { + if constexpr (N <= std::numeric_limits::max()) return uint8_t(N); + else if constexpr (N <= std::numeric_limits::max()) return uint16_t(N); + else if constexpr (N <= std::numeric_limits::max()) return uint32_t(N); + else return uint64_t(N); +} + +// Yields the size (in bytes) of the operand, using the smallest +// unsigned type that can represent the size value. The operand may be +// an expression, which is an unevaluated operand, or it may be a +// type. All of the restrictions for sizeof operands apply to the +// operand. The result is a constant expression. +// +// Example of correct usage of sizeof/sizeof_auto: +// // this will wrap using sizeof_auto, use sizeof to ensure computation using size_t +// size_t size = std::numeric_limits::max() * sizeof(uint16_t); +// // implicit narrowing conversion or compiler warning/error using stricter compiler flags when using sizeof +// int count = 42 / sizeof_auto(uint16_t); + +#define sizeof_auto(...) sizeof_auto_impl() + // Convert pointer to intptr_t, for use in printing pointers. inline intptr_t p2i(const volatile void* p) { return (intptr_t) p; diff --git a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp index f24d74ea529..6636efbba8e 100644 --- a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp +++ b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp @@ -321,3 +321,38 @@ TEST(globalDefinitions, jlong_from) { val = jlong_from(0xABCD, 0xEFEF); EXPECT_EQ(val, CONST64(0x0000ABCD0000EFEF)); } + +struct NoCopy { + int x; + NONCOPYABLE(NoCopy); +}; + +TEST(globalDefinitions, sizeof_auto) { + char x = 5; + char& y = x; + char* z = &x; + EXPECT_EQ(sizeof_auto(x), sizeof(x)); + EXPECT_EQ(sizeof_auto(y), sizeof(y)); + EXPECT_EQ(sizeof_auto(z), sizeof(z)); + + NoCopy nc{0}; + sizeof_auto(nc); + + static_assert(sizeof_auto(char[1LL]) == 1); + static_assert(sizeof_auto(char[std::numeric_limits::max() + 1LL]) == std::numeric_limits::max() + 1LL); + static_assert(sizeof_auto(char[std::numeric_limits::max() + 1LL]) == std::numeric_limits::max() + 1LL); +#if defined(_LP64) && !defined(_WINDOWS) + // char array sometimes limited to 2 gig length on 32 bit platforms (signed), disabled for Windows because of compiler error C2148. + static_assert(sizeof_auto(char[std::numeric_limits::max() + 1LL]) == std::numeric_limits::max() + 1LL); +#endif + + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max()])) == sizeof(uint8_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max() + 1LL])) == sizeof(uint16_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max()])) == sizeof(uint16_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max() + 1LL])) == sizeof(uint32_t)); +#if defined(_LP64) && !defined(_WINDOWS) + // char array sometimes limited to 2 gig length on 32 bit platforms (signed), disabled for Windows because of compiler error C2148. + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max()])) == sizeof(uint32_t)); + static_assert(sizeof(sizeof_auto(char[std::numeric_limits::max() + 1LL])) == sizeof(uint64_t)); +#endif +} From 4e15a4adfc50e37e2be01e90a67fde4b12126abd Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 27 Feb 2026 11:36:54 +0000 Subject: [PATCH 085/636] 8378823: AIX build fails after zlib updated by JDK-8378631 Reviewed-by: mdoerr, jpai, stuefe --- make/autoconf/lib-bundled.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/autoconf/lib-bundled.m4 b/make/autoconf/lib-bundled.m4 index bc358928af0..7aa5fdf2589 100644 --- a/make/autoconf/lib-bundled.m4 +++ b/make/autoconf/lib-bundled.m4 @@ -267,7 +267,7 @@ AC_DEFUN_ONCE([LIB_SETUP_ZLIB], LIBZ_LIBS="" if test "x$USE_EXTERNAL_LIBZ" = "xfalse"; then LIBZ_CFLAGS="$LIBZ_CFLAGS -I$TOPDIR/src/java.base/share/native/libzip/zlib" - if test "x$OPENJDK_TARGET_OS" = xmacosx; then + if test "x$OPENJDK_TARGET_OS" = xmacosx -o "x$OPENJDK_TARGET_OS" = xaix -o "x$OPENJDK_TARGET_OS" = xlinux; then LIBZ_CFLAGS="$LIBZ_CFLAGS -DHAVE_UNISTD_H=1 -DHAVE_STDARG_H=1" fi else From 5606036793a8819da463bfd12446372276b4effa Mon Sep 17 00:00:00 2001 From: Christoph Langer Date: Fri, 27 Feb 2026 12:35:28 +0000 Subject: [PATCH 086/636] 8378563: ConnectionRefusedMessage::testFinishConnect fails when jdk.includeInExceptions contains hostInfo Reviewed-by: mdoerr, mbaesken, dfuchs --- .../ConnectionRefusedMessage.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) rename test/jdk/java/nio/channels/{Selector => SocketChannel}/ConnectionRefusedMessage.java (92%) diff --git a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java b/test/jdk/java/nio/channels/SocketChannel/ConnectionRefusedMessage.java similarity index 92% rename from test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java rename to test/jdk/java/nio/channels/SocketChannel/ConnectionRefusedMessage.java index 04490f63efe..d71bc6569cb 100644 --- a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java +++ b/test/jdk/java/nio/channels/SocketChannel/ConnectionRefusedMessage.java @@ -42,7 +42,8 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; * @summary Verify that when a SocketChannel is registered with a Selector * with an interest in CONNECT operation, then SocketChannel.finishConnect() * throws the correct exception message, if the connect() fails - * @run junit ${test.main.class} + * @run junit/othervm -Djdk.includeInExceptions=hostInfoExclSocket ${test.main.class} + * @run junit/othervm -Djdk.includeInExceptions=hostInfo -Dcheck.relaxed=true ${test.main.class} */ class ConnectionRefusedMessage { @@ -108,10 +109,14 @@ class ConnectionRefusedMessage { } private static void assertExceptionMessage(final ConnectException ce) { - if (!"Connection refused".equals(ce.getMessage())) { - // propagate the original exception - fail("unexpected exception message: " + ce.getMessage(), ce); + if ("Connection refused".equals(ce.getMessage())) { + return; } + if (Boolean.getBoolean("check.relaxed") && ce.getMessage() != null && ce.getMessage().startsWith("Connection refused")) { + return; + } + // propagate the original exception + fail("unexpected exception message: " + ce.getMessage(), ce); } // Try to find a suitable port to provoke a "Connection Refused" error. From 1fb608e1bcbbc3fd68205ea168f10584cc5c2a62 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Fri, 27 Feb 2026 17:52:24 +0000 Subject: [PATCH 087/636] 8378792: ObjectMethods.bootstrap missing getter validation Reviewed-by: rriggs, jvernee --- .../java/lang/runtime/ObjectMethods.java | 23 +++--- .../java/lang/runtime/ObjectMethodsTest.java | 71 ++++++++++++++----- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java index 18aa6f29f1f..e4b2886404f 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -363,9 +363,9 @@ public final class ObjectMethods { * @return the method handle */ private static MethodHandle makeToString(MethodHandles.Lookup lookup, - Class receiverClass, - MethodHandle[] getters, - List names) { + Class receiverClass, + MethodHandle[] getters, + List names) { assert getters.length == names.size(); if (getters.length == 0) { // special case @@ -516,8 +516,8 @@ public final class ObjectMethods { requireNonNull(type); requireNonNull(recordClass); requireNonNull(names); - requireNonNull(getters); - Arrays.stream(getters).forEach(Objects::requireNonNull); + List getterList = List.of(getters); // deep null check + MethodType methodType; if (type instanceof MethodType mt) methodType = mt; @@ -526,7 +526,14 @@ public final class ObjectMethods { if (!MethodHandle.class.equals(type)) throw new IllegalArgumentException(type.toString()); } - List getterList = List.of(getters); + + for (MethodHandle getter : getterList) { + var getterType = getter.type(); + if (getterType.parameterCount() != 1 || getterType.returnType() == void.class || getterType.parameterType(0) != recordClass) { + throw new IllegalArgumentException("Illegal getter type %s for recordClass %s".formatted(getterType, recordClass.getTypeName())); + } + } + MethodHandle handle = switch (methodName) { case "equals" -> { if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class))) @@ -541,7 +548,7 @@ public final class ObjectMethods { case "toString" -> { if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass))) throw new IllegalArgumentException("Bad method type: " + methodType); - List nameList = "".equals(names) ? List.of() : List.of(names.split(";")); + List nameList = names.isEmpty() ? List.of() : List.of(names.split(";")); if (nameList.size() != getterList.size()) throw new IllegalArgumentException("Name list and accessor list do not match"); yield makeToString(lookup, recordClass, getters, nameList); diff --git a/test/jdk/java/lang/runtime/ObjectMethodsTest.java b/test/jdk/java/lang/runtime/ObjectMethodsTest.java index 951d3b68383..d7ca5912273 100644 --- a/test/jdk/java/lang/runtime/ObjectMethodsTest.java +++ b/test/jdk/java/lang/runtime/ObjectMethodsTest.java @@ -36,11 +36,11 @@ import java.lang.invoke.MethodType; import java.lang.runtime.ObjectMethods; import static java.lang.invoke.MethodType.methodType; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; public class ObjectMethodsTest { @@ -144,8 +144,8 @@ public class ObjectMethodsTest { assertEquals("Empty[]", (String)handle.invokeExact(new Empty())); } - Class NPE = NullPointerException.class; - Class IAE = IllegalArgumentException.class; + private static final Class NPE = NullPointerException.class; + private static final Class IAE = IllegalArgumentException.class; @Test public void exceptions() { @@ -157,25 +157,60 @@ public class ObjectMethodsTest { assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.EQUALS_DESC, C.class, "x;y", C.ACCESSORS)); assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "hashCode", C.TO_STRING_DESC, C.class, "x;y", C.ACCESSORS)); assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "equals", C.HASHCODE_DESC, C.class, "x;y", C.ACCESSORS)); + } - record NamePlusType(String mn, MethodType mt) {} - List namePlusTypeList = List.of( + record NamePlusType(String name, MethodType type) {} + + static List namePlusTypeList() { + return List.of( new NamePlusType("toString", C.TO_STRING_DESC), new NamePlusType("equals", C.EQUALS_DESC), new NamePlusType("hashCode", C.HASHCODE_DESC) ); - - for (NamePlusType npt : namePlusTypeList) { - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", null)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", new MethodHandle[]{null})); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, null, C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), null, "x;y", C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), null, C.class, "x;y", C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, npt.mt(), C.class, "x;y", C.ACCESSORS)); - assertThrows(NPE, () -> ObjectMethods.bootstrap(null, npt.mn(), npt.mt(), C.class, "x;y", C.ACCESSORS)); - } } + @MethodSource("namePlusTypeList") + @ParameterizedTest + void commonExceptions(NamePlusType npt) { + String name = npt.name(); + MethodType type = npt.type(); + + // Null checks + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", null)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", new MethodHandle[]{null})); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, null, C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, null, "x;y", C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, name, null, C.class, "x;y", C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, type, C.class, "x;y", C.ACCESSORS)); + assertThrows(NPE, () -> ObjectMethods.bootstrap(null, name, type, C.class, "x;y", C.ACCESSORS)); + + // Bad indy call receiver type - change C to this test class + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type.changeParameterType(0, this.getClass()), C.class, "x;y", C.ACCESSORS)); + + // Bad getter types + var wrongReceiverGetter = assertDoesNotThrow(() -> MethodHandles.lookup().findGetter(this.getClass(), "y", int.class)); + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", + new MethodHandle[]{ + C.ACCESSORS[0], + wrongReceiverGetter, + })); + var extraArgGetter = MethodHandles.dropArguments(C.ACCESSORS[1], 1, int.class); + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", + new MethodHandle[]{ + C.ACCESSORS[0], + extraArgGetter, + })); + var voidReturnGetter = MethodHandles.empty(MethodType.methodType(void.class, C.class)); + assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type, C.class, "x;y", + new MethodHandle[]{ + C.ACCESSORS[0], + voidReturnGetter, + })); + } + + // same field name and type as C::y, for wrongReceiverGetter + private int y; + // Based on the ObjectMethods internal implementation private static int hashCombiner(int x, int y) { return x*31 + y; From a436287c139c88af1b570cc2738e3f33b8ec7fe6 Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Fri, 27 Feb 2026 18:12:05 +0000 Subject: [PATCH 088/636] 8377048: Shenandoah: shenandoahLock related improvments Reviewed-by: kdnilsen, wkemper --- .../shenandoahBarrierSetNMethod.cpp | 4 +- .../gc/shenandoah/shenandoahCodeRoots.cpp | 6 +- .../gc/shenandoah/shenandoahConcurrentGC.cpp | 2 +- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 4 +- .../share/gc/shenandoah/shenandoahHeap.cpp | 10 ++++ .../share/gc/shenandoah/shenandoahHeap.hpp | 20 ++++++- .../share/gc/shenandoah/shenandoahLock.cpp | 28 +++++---- .../share/gc/shenandoah/shenandoahLock.hpp | 58 ++++++------------- .../share/gc/shenandoah/shenandoahNMethod.cpp | 2 +- .../share/gc/shenandoah/shenandoahNMethod.hpp | 16 +++-- .../shenandoah/shenandoahNMethod.inline.hpp | 8 +-- .../share/gc/shenandoah/shenandoahUnload.cpp | 8 +-- 12 files changed, 91 insertions(+), 75 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp index c6e6108fda8..2be5722f7f9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp @@ -41,9 +41,9 @@ bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return true; } - ShenandoahReentrantLock* lock = ShenandoahNMethod::lock_for_nmethod(nm); + ShenandoahNMethodLock* lock = ShenandoahNMethod::lock_for_nmethod(nm); assert(lock != nullptr, "Must be"); - ShenandoahReentrantLocker locker(lock); + ShenandoahNMethodLocker locker(lock); if (!is_armed(nm)) { // Some other thread managed to complete while we were diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 07d339eb32e..64e135e9a4e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -136,13 +136,13 @@ public: assert(!nm_data->is_unregistered(), "Should not see unregistered entry"); if (nm->is_unloading()) { - ShenandoahReentrantLocker locker(nm_data->lock()); + ShenandoahNMethodLocker locker(nm_data->lock()); nm->unlink(); return; } { - ShenandoahReentrantLocker locker(nm_data->lock()); + ShenandoahNMethodLocker locker(nm_data->lock()); // Heal oops if (_bs->is_armed(nm)) { @@ -154,7 +154,7 @@ public: } // Clear compiled ICs and exception caches - ShenandoahReentrantLocker locker(nm_data->ic_lock()); + ShenandoahNMethodLocker locker(nm_data->ic_lock()); nm->unload_nmethod_caches(_unloading_occurred); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 364279deafe..5206a0558e8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1023,7 +1023,7 @@ public: void do_nmethod(nmethod* n) { ShenandoahNMethod* data = ShenandoahNMethod::gc_data(n); - ShenandoahReentrantLocker locker(data->lock()); + ShenandoahNMethodLocker locker(data->lock()); // Setup EvacOOM scope below reentrant lock to avoid deadlock with // nmethod_entry_barrier ShenandoahEvacOOMScope oom; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 91c6024d522..d55a06d5713 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -32,8 +32,8 @@ #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" #include "logging/logStream.hpp" -typedef ShenandoahLock ShenandoahRebuildLock; -typedef ShenandoahLocker ShenandoahRebuildLocker; +typedef ShenandoahLock ShenandoahRebuildLock; +typedef ShenandoahLocker ShenandoahRebuildLocker; // Each ShenandoahHeapRegion is associated with a ShenandoahFreeSetPartitionId. enum class ShenandoahFreeSetPartitionId : uint8_t { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 9dd837b90d2..d78bdae6a51 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -2834,3 +2834,13 @@ void ShenandoahHeap::log_heap_status(const char* msg) const { global_generation()->log_status(msg); } } + +ShenandoahHeapLocker::ShenandoahHeapLocker(ShenandoahHeapLock* lock, bool allow_block_for_safepoint) : _lock(lock) { +#ifdef ASSERT + ShenandoahFreeSet* free_set = ShenandoahHeap::heap()->free_set(); + // free_set is nullptr only at pre-initialized state + assert(free_set == nullptr || !free_set->rebuild_lock()->owned_by_self(), "Dead lock, can't acquire heap lock while holding free-set rebuild lock"); + assert(_lock != nullptr, "Must not"); +#endif + _lock->lock(allow_block_for_safepoint); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 4a4499667ff..85ad339469d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -117,9 +117,23 @@ public: virtual bool is_thread_safe() { return false; } }; -typedef ShenandoahLock ShenandoahHeapLock; -typedef ShenandoahLocker ShenandoahHeapLocker; -typedef Stack ShenandoahScanObjectStack; +typedef ShenandoahLock ShenandoahHeapLock; +// ShenandoahHeapLocker implements locker to assure mutually exclusive access to the global heap data structures. +// Asserts in the implementation detect potential deadlock usage with regards the rebuild lock that is present +// in ShenandoahFreeSet. Whenever both locks are acquired, this lock should be acquired before the +// ShenandoahFreeSet rebuild lock. +class ShenandoahHeapLocker : public StackObj { +private: + ShenandoahHeapLock* _lock; +public: + ShenandoahHeapLocker(ShenandoahHeapLock* lock, bool allow_block_for_safepoint = false); + + ~ShenandoahHeapLocker() { + _lock->unlock(); + } +}; + +typedef Stack ShenandoahScanObjectStack; // Shenandoah GC is low-pause concurrent GC that uses a load reference barrier // for concurent evacuation and a snapshot-at-the-beginning write barrier for diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp index 7eec0b9af64..7e317f53424 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp @@ -93,7 +93,7 @@ ShenandoahSimpleLock::ShenandoahSimpleLock() { assert(os::mutex_init_done(), "Too early!"); } -void ShenandoahSimpleLock::lock() { +void ShenandoahSimpleLock::lock(bool allow_block_for_safepoint) { _lock.lock(); } @@ -101,28 +101,31 @@ void ShenandoahSimpleLock::unlock() { _lock.unlock(); } -ShenandoahReentrantLock::ShenandoahReentrantLock() : - ShenandoahSimpleLock(), _owner(nullptr), _count(0) { - assert(os::mutex_init_done(), "Too early!"); +template +ShenandoahReentrantLock::ShenandoahReentrantLock() : + Lock(), _owner(nullptr), _count(0) { } -ShenandoahReentrantLock::~ShenandoahReentrantLock() { +template +ShenandoahReentrantLock::~ShenandoahReentrantLock() { assert(_count == 0, "Unbalance"); } -void ShenandoahReentrantLock::lock() { +template +void ShenandoahReentrantLock::lock(bool allow_block_for_safepoint) { Thread* const thread = Thread::current(); Thread* const owner = _owner.load_relaxed(); if (owner != thread) { - ShenandoahSimpleLock::lock(); + Lock::lock(allow_block_for_safepoint); _owner.store_relaxed(thread); } _count++; } -void ShenandoahReentrantLock::unlock() { +template +void ShenandoahReentrantLock::unlock() { assert(owned_by_self(), "Invalid owner"); assert(_count > 0, "Invalid count"); @@ -130,12 +133,17 @@ void ShenandoahReentrantLock::unlock() { if (_count == 0) { _owner.store_relaxed((Thread*)nullptr); - ShenandoahSimpleLock::unlock(); + Lock::unlock(); } } -bool ShenandoahReentrantLock::owned_by_self() const { +template +bool ShenandoahReentrantLock::owned_by_self() const { Thread* const thread = Thread::current(); Thread* const owner = _owner.load_relaxed(); return owner == thread; } + +// Explicit template instantiation +template class ShenandoahReentrantLock; +template class ShenandoahReentrantLock; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp index 2e44810cd5d..7c91df191e5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp @@ -31,7 +31,7 @@ #include "runtime/javaThread.hpp" #include "runtime/safepoint.hpp" -class ShenandoahLock { +class ShenandoahLock { private: enum LockState { unlocked = 0, locked = 1 }; @@ -48,7 +48,7 @@ private: public: ShenandoahLock() : _state(unlocked), _owner(nullptr) {}; - void lock(bool allow_block_for_safepoint) { + void lock(bool allow_block_for_safepoint = false) { assert(_owner.load_relaxed() != Thread::current(), "reentrant locking attempt, would deadlock"); if ((allow_block_for_safepoint && SafepointSynchronize::is_synchronizing()) || @@ -83,34 +83,19 @@ public: } }; -class ShenandoahLocker : public StackObj { -private: - ShenandoahLock* const _lock; -public: - ShenandoahLocker(ShenandoahLock* lock, bool allow_block_for_safepoint = false) : _lock(lock) { - if (_lock != nullptr) { - _lock->lock(allow_block_for_safepoint); - } - } - - ~ShenandoahLocker() { - if (_lock != nullptr) { - _lock->unlock(); - } - } -}; - +// Simple lock using PlatformMonitor class ShenandoahSimpleLock { private: PlatformMonitor _lock; // native lock public: ShenandoahSimpleLock(); - - virtual void lock(); - virtual void unlock(); + void lock(bool allow_block_for_safepoint = false); + void unlock(); }; -class ShenandoahReentrantLock : public ShenandoahSimpleLock { +// templated reentrant lock +template +class ShenandoahReentrantLock : public Lock { private: Atomic _owner; uint64_t _count; @@ -119,30 +104,25 @@ public: ShenandoahReentrantLock(); ~ShenandoahReentrantLock(); - virtual void lock(); - virtual void unlock(); + void lock(bool allow_block_for_safepoint = false); + void unlock(); // If the lock already owned by this thread bool owned_by_self() const ; }; -class ShenandoahReentrantLocker : public StackObj { -private: - ShenandoahReentrantLock* const _lock; - +// template based ShenandoahLocker +template +class ShenandoahLocker : public StackObj { + Lock* const _lock; public: - ShenandoahReentrantLocker(ShenandoahReentrantLock* lock) : - _lock(lock) { - if (_lock != nullptr) { - _lock->lock(); - } + ShenandoahLocker(Lock* lock, bool allow_block_for_safepoint = false) : _lock(lock) { + assert(_lock != nullptr, "Must not"); + _lock->lock(allow_block_for_safepoint); } - ~ShenandoahReentrantLocker() { - if (_lock != nullptr) { - assert(_lock->owned_by_self(), "Must be owner"); - _lock->unlock(); - } + ~ShenandoahLocker() { + _lock->unlock(); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index facaefd4b62..594ad614d90 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -241,7 +241,7 @@ void ShenandoahNMethodTable::register_nmethod(nmethod* nm) { assert(nm == data->nm(), "Must be same nmethod"); // Prevent updating a nmethod while concurrent iteration is in progress. wait_until_concurrent_iteration_done(); - ShenandoahReentrantLocker data_locker(data->lock()); + ShenandoahNMethodLocker data_locker(data->lock()); data->update(); } else { // For a new nmethod, we can safely append it to the list, because diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index 77faf6c0dcb..2686b4f4985 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -33,6 +33,10 @@ #include "runtime/atomic.hpp" #include "utilities/growableArray.hpp" +// Use ShenandoahReentrantLock as ShenandoahNMethodLock +typedef ShenandoahReentrantLock ShenandoahNMethodLock; +typedef ShenandoahLocker ShenandoahNMethodLocker; + // ShenandoahNMethod tuple records the internal locations of oop slots within reclocation stream in // the nmethod. This allows us to quickly scan the oops without doing the nmethod-internal scans, // that sometimes involves parsing the machine code. Note it does not record the oops themselves, @@ -44,16 +48,16 @@ private: int _oops_count; bool _has_non_immed_oops; bool _unregistered; - ShenandoahReentrantLock _lock; - ShenandoahReentrantLock _ic_lock; + ShenandoahNMethodLock _lock; + ShenandoahNMethodLock _ic_lock; public: ShenandoahNMethod(nmethod *nm, GrowableArray& oops, bool has_non_immed_oops); ~ShenandoahNMethod(); inline nmethod* nm() const; - inline ShenandoahReentrantLock* lock(); - inline ShenandoahReentrantLock* ic_lock(); + inline ShenandoahNMethodLock* lock(); + inline ShenandoahNMethodLock* ic_lock(); inline void oops_do(OopClosure* oops, bool fix_relocations = false); // Update oops when the nmethod is re-registered void update(); @@ -61,8 +65,8 @@ public: inline bool is_unregistered() const; static ShenandoahNMethod* for_nmethod(nmethod* nm); - static inline ShenandoahReentrantLock* lock_for_nmethod(nmethod* nm); - static inline ShenandoahReentrantLock* ic_lock_for_nmethod(nmethod* nm); + static inline ShenandoahNMethodLock* lock_for_nmethod(nmethod* nm); + static inline ShenandoahNMethodLock* ic_lock_for_nmethod(nmethod* nm); static void heal_nmethod(nmethod* nm); static inline void heal_nmethod_metadata(ShenandoahNMethod* nmethod_data); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp index 6758298675b..ef9e347b821 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp @@ -35,11 +35,11 @@ nmethod* ShenandoahNMethod::nm() const { return _nm; } -ShenandoahReentrantLock* ShenandoahNMethod::lock() { +ShenandoahNMethodLock* ShenandoahNMethod::lock() { return &_lock; } -ShenandoahReentrantLock* ShenandoahNMethod::ic_lock() { +ShenandoahNMethodLock* ShenandoahNMethod::ic_lock() { return &_ic_lock; } @@ -85,11 +85,11 @@ void ShenandoahNMethod::attach_gc_data(nmethod* nm, ShenandoahNMethod* gc_data) nm->set_gc_data(gc_data); } -ShenandoahReentrantLock* ShenandoahNMethod::lock_for_nmethod(nmethod* nm) { +ShenandoahNMethodLock* ShenandoahNMethod::lock_for_nmethod(nmethod* nm) { return gc_data(nm)->lock(); } -ShenandoahReentrantLock* ShenandoahNMethod::ic_lock_for_nmethod(nmethod* nm) { +ShenandoahNMethodLock* ShenandoahNMethod::ic_lock_for_nmethod(nmethod* nm) { return gc_data(nm)->ic_lock(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp index b248fab7958..ac7fe1f9a3a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp @@ -80,7 +80,7 @@ public: virtual bool has_dead_oop(nmethod* nm) const { assert(ShenandoahHeap::heap()->is_concurrent_weak_root_in_progress(), "Only for this phase"); ShenandoahNMethod* data = ShenandoahNMethod::gc_data(nm); - ShenandoahReentrantLocker locker(data->lock()); + ShenandoahNMethodLocker locker(data->lock()); ShenandoahIsUnloadingOopClosure cl; data->oops_do(&cl); return cl.is_unloading(); @@ -90,14 +90,14 @@ public: class ShenandoahCompiledICProtectionBehaviour : public CompiledICProtectionBehaviour { public: virtual bool lock(nmethod* nm) { - ShenandoahReentrantLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); + ShenandoahNMethodLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); assert(lock != nullptr, "Not yet registered?"); lock->lock(); return true; } virtual void unlock(nmethod* nm) { - ShenandoahReentrantLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); + ShenandoahNMethodLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); assert(lock != nullptr, "Not yet registered?"); lock->unlock(); } @@ -107,7 +107,7 @@ public: return true; } - ShenandoahReentrantLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); + ShenandoahNMethodLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); assert(lock != nullptr, "Not yet registered?"); return lock->owned_by_self(); } From 3144b572d33713cd3311352f0bbaac8b69408fe4 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Fri, 27 Feb 2026 19:52:06 +0000 Subject: [PATCH 089/636] 8378388: Add missing @Override annotations in "javax.print.attribute.standard" package part 1 Reviewed-by: prr, azvegint --- .../javax/print/attribute/standard/Chromaticity.java | 6 +++++- .../javax/print/attribute/standard/ColorSupported.java | 6 +++++- .../javax/print/attribute/standard/Compression.java | 6 +++++- .../classes/javax/print/attribute/standard/Copies.java | 5 ++++- .../javax/print/attribute/standard/CopiesSupported.java | 5 ++++- .../print/attribute/standard/DateTimeAtCompleted.java | 5 ++++- .../javax/print/attribute/standard/DateTimeAtCreation.java | 5 ++++- .../print/attribute/standard/DateTimeAtProcessing.java | 5 ++++- .../javax/print/attribute/standard/Destination.java | 5 ++++- .../javax/print/attribute/standard/DialogOwner.java | 5 ++++- .../print/attribute/standard/DialogTypeSelection.java | 6 +++++- .../javax/print/attribute/standard/DocumentName.java | 5 ++++- .../classes/javax/print/attribute/standard/Fidelity.java | 6 +++++- .../classes/javax/print/attribute/standard/Finishings.java | 7 ++++++- .../javax/print/attribute/standard/JobHoldUntil.java | 5 ++++- .../javax/print/attribute/standard/JobImpressions.java | 5 ++++- .../print/attribute/standard/JobImpressionsCompleted.java | 5 ++++- .../print/attribute/standard/JobImpressionsSupported.java | 5 ++++- .../classes/javax/print/attribute/standard/JobKOctets.java | 5 ++++- .../print/attribute/standard/JobKOctetsProcessed.java | 5 ++++- .../print/attribute/standard/JobKOctetsSupported.java | 5 ++++- .../javax/print/attribute/standard/JobMediaSheets.java | 5 ++++- .../print/attribute/standard/JobMediaSheetsCompleted.java | 5 ++++- .../print/attribute/standard/JobMediaSheetsSupported.java | 5 ++++- .../print/attribute/standard/JobMessageFromOperator.java | 5 ++++- .../classes/javax/print/attribute/standard/JobName.java | 5 ++++- .../print/attribute/standard/JobOriginatingUserName.java | 5 ++++- .../javax/print/attribute/standard/JobPriority.java | 5 ++++- .../print/attribute/standard/JobPrioritySupported.java | 5 ++++- .../classes/javax/print/attribute/standard/JobSheets.java | 6 +++++- .../classes/javax/print/attribute/standard/JobState.java | 6 +++++- .../javax/print/attribute/standard/JobStateReason.java | 6 +++++- .../javax/print/attribute/standard/JobStateReasons.java | 5 ++++- 33 files changed, 142 insertions(+), 33 deletions(-) diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.java index 0481060f73f..25dbb7ddaca 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Chromaticity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -114,6 +114,7 @@ public final class Chromaticity extends EnumSyntax /** * Returns the string table for class {@code Chromaticity}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -121,6 +122,7 @@ public final class Chromaticity extends EnumSyntax /** * Returns the enumeration value table for class {@code Chromaticity}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -135,6 +137,7 @@ public final class Chromaticity extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Chromaticity.class; } @@ -148,6 +151,7 @@ public final class Chromaticity extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "chromaticity"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.java index 6affb3e28dc..8ce2c5eef7c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/ColorSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -104,6 +104,7 @@ public final class ColorSupported extends EnumSyntax /** * Returns the string table for class {@code ColorSupported}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -111,6 +112,7 @@ public final class ColorSupported extends EnumSyntax /** * Returns the enumeration value table for class {@code ColorSupported}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -125,6 +127,7 @@ public final class ColorSupported extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return ColorSupported.class; } @@ -138,6 +141,7 @@ public final class ColorSupported extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "color-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.java index b1e7f1e89fc..9eeab5d9688 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Compression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -106,6 +106,7 @@ public class Compression extends EnumSyntax implements DocAttribute { /** * Returns the string table for class {@code Compression}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -113,6 +114,7 @@ public class Compression extends EnumSyntax implements DocAttribute { /** * Returns the enumeration value table for class {@code Compression}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -127,6 +129,7 @@ public class Compression extends EnumSyntax implements DocAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Compression.class; } @@ -140,6 +143,7 @@ public class Compression extends EnumSyntax implements DocAttribute { * * @return attribute category name */ + @Override public final String getName() { return "compression"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.java index 33a98c035be..6292cc4c8c2 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Copies.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -97,6 +97,7 @@ public final class Copies extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this copies * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals (object) && object instanceof Copies; } @@ -110,6 +111,7 @@ public final class Copies extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Copies.class; } @@ -122,6 +124,7 @@ public final class Copies extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "copies"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.java index e7b411d8e55..3c2cf4bb550 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/CopiesSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -105,6 +105,7 @@ public final class CopiesSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this copies * supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals (object) && object instanceof CopiesSupported; } @@ -119,6 +120,7 @@ public final class CopiesSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return CopiesSupported.class; } @@ -132,6 +134,7 @@ public final class CopiesSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "copies-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.java index 4e0a3b27259..a7a31dacd8d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCompleted.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -88,6 +88,7 @@ public final class DateTimeAtCompleted extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this date-time at * completed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof DateTimeAtCompleted); @@ -105,6 +106,7 @@ public final class DateTimeAtCompleted extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DateTimeAtCompleted.class; } @@ -118,6 +120,7 @@ public final class DateTimeAtCompleted extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "date-time-at-completed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.java index fc09f0672c2..53b85b7cf5c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtCreation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -88,6 +88,7 @@ public final class DateTimeAtCreation extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this date-time at * creation attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof DateTimeAtCreation); @@ -103,6 +104,7 @@ public final class DateTimeAtCreation extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DateTimeAtCreation.class; } @@ -116,6 +118,7 @@ public final class DateTimeAtCreation extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "date-time-at-creation"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.java index 8b1f3efdc0f..310f3f756c7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DateTimeAtProcessing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -89,6 +89,7 @@ public final class DateTimeAtProcessing extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this date-time at * processing attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof DateTimeAtProcessing); @@ -104,6 +105,7 @@ public final class DateTimeAtProcessing extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DateTimeAtProcessing.class; } @@ -117,6 +119,7 @@ public final class DateTimeAtProcessing extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "date-time-at-processing"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.java index bc1d240c9c1..655a314b136 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Destination.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -89,6 +89,7 @@ public final class Destination extends URISyntax * @return {@code true} if {@code object} is equivalent to this destination * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof Destination); @@ -104,6 +105,7 @@ public final class Destination extends URISyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Destination.class; } @@ -117,6 +119,7 @@ public final class Destination extends URISyntax * * @return attribute category name */ + @Override public final String getName() { return "spool-data-destination"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.java index 593e656cf6b..01a410d075b 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogOwner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -50,6 +50,7 @@ public final class DialogOwner implements PrintRequestAttribute { private static class Accessor extends DialogOwnerAccessor { + @Override public long getOwnerID(DialogOwner owner) { return owner.getID(); } @@ -133,6 +134,7 @@ public final class DialogOwner implements PrintRequestAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DialogOwner.class; } @@ -145,6 +147,7 @@ public final class DialogOwner implements PrintRequestAttribute { * {@code "dialog-owner"}. * */ + @Override public final String getName() { return "dialog-owner"; diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java index ae3195d0280..2cf003060b1 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DialogTypeSelection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, 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 @@ -98,6 +98,7 @@ public final class DialogTypeSelection extends EnumSyntax /** * Returns the string table for class {@code DialogTypeSelection}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -106,6 +107,7 @@ public final class DialogTypeSelection extends EnumSyntax * Returns the enumeration value table for class * {@code DialogTypeSelection}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -120,6 +122,7 @@ public final class DialogTypeSelection extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DialogTypeSelection.class; } @@ -133,6 +136,7 @@ public final class DialogTypeSelection extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "dialog-type-selection"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.java index eb2bd7a870d..bd1f574e4ba 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/DocumentName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -86,6 +86,7 @@ public final class DocumentName extends TextSyntax implements DocAttribute { * @return {@code true} if {@code object} is equivalent to this document * name attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof DocumentName); } @@ -100,6 +101,7 @@ public final class DocumentName extends TextSyntax implements DocAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return DocumentName.class; } @@ -113,6 +115,7 @@ public final class DocumentName extends TextSyntax implements DocAttribute { * * @return attribute category name */ + @Override public final String getName() { return "document-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.java index b0e4b70c87c..8c8941c76c4 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Fidelity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -101,6 +101,7 @@ public final class Fidelity extends EnumSyntax /** * Returns the string table for class {@code Fidelity}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -108,6 +109,7 @@ public final class Fidelity extends EnumSyntax /** * Returns the enumeration value table for class {@code Fidelity}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -122,6 +124,7 @@ public final class Fidelity extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Fidelity.class; } @@ -135,6 +138,7 @@ public final class Fidelity extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "ipp-attribute-fidelity"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.java b/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.java index 46d520ecb48..7d9d042b3e7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/Finishings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -344,6 +344,7 @@ public class Finishings extends EnumSyntax /** * Returns the string table for class {@code Finishings}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -351,6 +352,7 @@ public class Finishings extends EnumSyntax /** * Returns the enumeration value table for class {@code Finishings}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -358,6 +360,7 @@ public class Finishings extends EnumSyntax /** * Returns the lowest integer value used by class {@code Finishings}. */ + @Override protected int getOffset() { return 3; } @@ -372,6 +375,7 @@ public class Finishings extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return Finishings.class; } @@ -385,6 +389,7 @@ public class Finishings extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "finishings"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.java index d4e2c2625df..2b9ede6940f 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobHoldUntil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -123,6 +123,7 @@ public final class JobHoldUntil extends DateTimeSyntax * @return {@code true} if {@code object} is equivalent to this job hold * until attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof JobHoldUntil); } @@ -137,6 +138,7 @@ public final class JobHoldUntil extends DateTimeSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobHoldUntil.class; } @@ -150,6 +152,7 @@ public final class JobHoldUntil extends DateTimeSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-hold-until"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.java index ed9600838be..70d7c7e5e46 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -110,6 +110,7 @@ public final class JobImpressions extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job * impressions attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals (object) && object instanceof JobImpressions; } @@ -124,6 +125,7 @@ public final class JobImpressions extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobImpressions.class; } @@ -137,6 +139,7 @@ public final class JobImpressions extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-impressions"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.java index 4974f9e13c9..e6fdeb93d05 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsCompleted.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -94,6 +94,7 @@ public final class JobImpressionsCompleted extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job * impressions completed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof JobImpressionsCompleted); @@ -109,6 +110,7 @@ public final class JobImpressionsCompleted extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobImpressionsCompleted.class; } @@ -122,6 +124,7 @@ public final class JobImpressionsCompleted extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-impressions-completed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.java index 131b19f5ad2..82b3049b8bd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobImpressionsSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -94,6 +94,7 @@ public final class JobImpressionsSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this job * impressions supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobImpressionsSupported); @@ -109,6 +110,7 @@ public final class JobImpressionsSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobImpressionsSupported.class; } @@ -122,6 +124,7 @@ public final class JobImpressionsSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-impressions-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.java index 454a30a3a28..acd04fab21d 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -159,6 +159,7 @@ public final class JobKOctets extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job K octets * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals(object) && object instanceof JobKOctets; } @@ -173,6 +174,7 @@ public final class JobKOctets extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobKOctets.class; } @@ -186,6 +188,7 @@ public final class JobKOctets extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-k-octets"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.java index 9acc44f5777..efd9cac2441 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsProcessed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -104,6 +104,7 @@ public final class JobKOctetsProcessed extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job K octets * processed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return(super.equals (object) && object instanceof JobKOctetsProcessed); @@ -119,6 +120,7 @@ public final class JobKOctetsProcessed extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobKOctetsProcessed.class; } @@ -132,6 +134,7 @@ public final class JobKOctetsProcessed extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-k-octets-processed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.java index 032d172d6e2..221328caeb8 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobKOctetsSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -93,6 +93,7 @@ public final class JobKOctetsSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this job K octets * supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobKOctetsSupported); @@ -108,6 +109,7 @@ public final class JobKOctetsSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobKOctetsSupported.class; } @@ -121,6 +123,7 @@ public final class JobKOctetsSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-k-octets-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.java index a3720748f68..181bda79826 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -101,6 +101,7 @@ public class JobMediaSheets extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job media * sheets attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return super.equals(object) && object instanceof JobMediaSheets; } @@ -115,6 +116,7 @@ public class JobMediaSheets extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMediaSheets.class; } @@ -128,6 +130,7 @@ public class JobMediaSheets extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-media-sheets"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.java index e6f1eb9b4d8..fea87dc8ebd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsCompleted.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -94,6 +94,7 @@ public final class JobMediaSheetsCompleted extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job media * sheets completed attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobMediaSheetsCompleted); @@ -109,6 +110,7 @@ public final class JobMediaSheetsCompleted extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMediaSheetsCompleted.class; } @@ -122,6 +124,7 @@ public final class JobMediaSheetsCompleted extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-media-sheets-completed"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.java index a942f277175..8feeafef18a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMediaSheetsSupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -94,6 +94,7 @@ public final class JobMediaSheetsSupported extends SetOfIntegerSyntax * @return {@code true} if {@code object} is equivalent to this job media * sheets supported attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobMediaSheetsSupported); @@ -109,6 +110,7 @@ public final class JobMediaSheetsSupported extends SetOfIntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMediaSheetsSupported.class; } @@ -122,6 +124,7 @@ public final class JobMediaSheetsSupported extends SetOfIntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-media-sheets-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.java index cd944103d23..796fe37774c 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobMessageFromOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -94,6 +94,7 @@ public final class JobMessageFromOperator extends TextSyntax * @return {@code true} if {@code object} is equivalent to this job message * from operator attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobMessageFromOperator); @@ -109,6 +110,7 @@ public final class JobMessageFromOperator extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobMessageFromOperator.class; } @@ -122,6 +124,7 @@ public final class JobMessageFromOperator extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-message-from-operator"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.java index cceabd8b8f0..4fa9eaee6fd 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -92,6 +92,7 @@ public final class JobName extends TextSyntax * @return {@code true} if {@code object} is equivalent to this job name * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals(object) && object instanceof JobName); } @@ -105,6 +106,7 @@ public final class JobName extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobName.class; } @@ -117,6 +119,7 @@ public final class JobName extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.java index d8dfd56a920..eda27d142f9 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobOriginatingUserName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -91,6 +91,7 @@ public final class JobOriginatingUserName extends TextSyntax * @return {@code true} if {@code object} is equivalent to this job * originating user name attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobOriginatingUserName); @@ -106,6 +107,7 @@ public final class JobOriginatingUserName extends TextSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobOriginatingUserName.class; } @@ -119,6 +121,7 @@ public final class JobOriginatingUserName extends TextSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-originating-user-name"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.java index 7c77a504673..5ca9990ec13 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPriority.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -94,6 +94,7 @@ public final class JobPriority extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job priority * attribute, {@code false} otherwise */ + @Override public boolean equals(Object object) { return (super.equals (object) && object instanceof JobPriority); } @@ -108,6 +109,7 @@ public final class JobPriority extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobPriority.class; } @@ -121,6 +123,7 @@ public final class JobPriority extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-priority"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.java index 6df729a9813..422d823bff7 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobPrioritySupported.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -86,6 +86,7 @@ public final class JobPrioritySupported extends IntegerSyntax * @return {@code true} if {@code object} is equivalent to this job priority * supported attribute, {@code false} otherwise */ + @Override public boolean equals (Object object) { return (super.equals(object) && @@ -102,6 +103,7 @@ public final class JobPrioritySupported extends IntegerSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobPrioritySupported.class; } @@ -115,6 +117,7 @@ public final class JobPrioritySupported extends IntegerSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-priority-supported"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.java index 130b8eb27a3..59a31096d2a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobSheets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -101,6 +101,7 @@ public class JobSheets extends EnumSyntax /** * Returns the string table for class {@code JobSheets}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -108,6 +109,7 @@ public class JobSheets extends EnumSyntax /** * Returns the enumeration value table for class {@code JobSheets}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -122,6 +124,7 @@ public class JobSheets extends EnumSyntax * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobSheets.class; } @@ -135,6 +138,7 @@ public class JobSheets extends EnumSyntax * * @return attribute category name */ + @Override public final String getName() { return "job-sheets"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.java index 13499a9b2a7..0b0ba9d3276 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -206,6 +206,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { /** * Returns the string table for class {@code JobState}. */ + @Override protected String[] getStringTable() { return myStringTable; } @@ -213,6 +214,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { /** * Returns the enumeration value table for class {@code JobState}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return myEnumValueTable; } @@ -227,6 +229,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobState.class; } @@ -240,6 +243,7 @@ public class JobState extends EnumSyntax implements PrintJobAttribute { * * @return attribute category name */ + @Override public final String getName() { return "job-state"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java index 2f8526cb7b1..aba72ec9c1a 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReason.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -436,6 +436,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { /** * Returns the string table for class {@code JobStateReason}. */ + @Override protected String[] getStringTable() { return myStringTable.clone(); } @@ -443,6 +444,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { /** * Returns the enumeration value table for class {@code JobStateReason}. */ + @Override protected EnumSyntax[] getEnumValueTable() { return (EnumSyntax[])myEnumValueTable.clone(); } @@ -457,6 +459,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobStateReason.class; } @@ -470,6 +473,7 @@ public class JobStateReason extends EnumSyntax implements Attribute { * * @return attribute category name */ + @Override public final String getName() { return "job-state-reason"; } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.java b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.java index 22b438c02eb..4e3e66d0ea8 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/JobStateReasons.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -140,6 +140,7 @@ public final class JobStateReasons * class {@link JobStateReason JobStateReason} * @since 1.5 */ + @Override public boolean add(JobStateReason o) { if (o == null) { throw new NullPointerException(); @@ -157,6 +158,7 @@ public final class JobStateReasons * @return printing attribute class (category), an instance of class * {@link Class java.lang.Class} */ + @Override public final Class getCategory() { return JobStateReasons.class; } @@ -170,6 +172,7 @@ public final class JobStateReasons * * @return attribute category name */ + @Override public final String getName() { return "job-state-reasons"; } From e0b040a6c6713827033e9ba51c9ded920dd0203b Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Fri, 27 Feb 2026 20:34:16 +0000 Subject: [PATCH 090/636] 8331994: Adapt MAKEFLAGS check for GNU Make 4.4.1 Reviewed-by: jpai, dholmes --- make/PreInit.gmk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/make/PreInit.gmk b/make/PreInit.gmk index 3df44308dd9..8152587781c 100644 --- a/make/PreInit.gmk +++ b/make/PreInit.gmk @@ -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. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -66,7 +66,8 @@ CALLED_SPEC_TARGETS := $(filter-out $(ALL_GLOBAL_TARGETS), $(CALLED_TARGETS)) ifeq ($(CALLED_SPEC_TARGETS), ) SKIP_SPEC := true endif -ifeq ($(findstring p, $(MAKEFLAGS))$(findstring q, $(MAKEFLAGS)), pq) +MFLAGS_SINGLE := $(filter-out --%, $(MFLAGS)) +ifeq ($(findstring p, $(MFLAGS_SINGLE))$(findstring q, $(MFLAGS_SINGLE)), pq) SKIP_SPEC := true endif From 4bee207d0ad948734e04516dd8d7c9504cb83665 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 27 Feb 2026 21:49:55 +0000 Subject: [PATCH 091/636] 8377554: Load card table base and other values via AOTRuntimeConstants in AOT code Reviewed-by: kvn, asmehra --- src/hotspot/cpu/aarch64/aarch64.ad | 30 +++++++++- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 10 ++++ .../gc/g1/g1BarrierSetAssembler_aarch64.cpp | 23 +++++++- .../cpu/aarch64/macroAssembler_aarch64.cpp | 22 ++++++++ .../cpu/aarch64/macroAssembler_aarch64.hpp | 3 + src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp | 10 ++++ .../x86/gc/g1/g1BarrierSetAssembler_x86.cpp | 41 ++++++++++++-- .../cardTableBarrierSetAssembler_x86.cpp | 24 ++++++-- .../cardTableBarrierSetAssembler_x86.hpp | 2 +- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 14 +++++ src/hotspot/cpu/x86/macroAssembler_x86.hpp | 1 + src/hotspot/cpu/x86/x86.ad | 25 +++++++++ src/hotspot/share/adlc/main.cpp | 2 + src/hotspot/share/code/aotCodeCache.cpp | 55 +++++++++++++++++++ src/hotspot/share/code/aotCodeCache.hpp | 33 +++++++++++ src/hotspot/share/gc/g1/g1BarrierSet.hpp | 3 + .../gc/shared/c1/cardTableBarrierSetC1.cpp | 13 +++++ .../gc/shared/c2/cardTableBarrierSetC2.cpp | 14 ++++- .../gc/shared/c2/cardTableBarrierSetC2.hpp | 2 +- .../share/gc/shared/cardTableBarrierSet.hpp | 4 ++ src/hotspot/share/opto/idealKit.cpp | 11 ++++ src/hotspot/share/opto/idealKit.hpp | 3 + src/hotspot/share/opto/type.cpp | 2 +- 23 files changed, 329 insertions(+), 18 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index a9ca91d9309..9734c6845ea 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -3403,11 +3403,13 @@ encode %{ } else if (rtype == relocInfo::metadata_type) { __ mov_metadata(dst_reg, (Metadata*)con); } else { - assert(rtype == relocInfo::none, "unexpected reloc type"); + assert(rtype == relocInfo::none || rtype == relocInfo::external_word_type, "unexpected reloc type"); + // load fake address constants using a normal move if (! __ is_valid_AArch64_address(con) || con < (address)(uintptr_t)os::vm_page_size()) { __ mov(dst_reg, con); } else { + // no reloc so just use adrp and add uint64_t offset; __ adrp(dst_reg, con, offset); __ add(dst_reg, dst_reg, offset); @@ -4535,6 +4537,18 @@ operand immP_1() interface(CONST_INTER); %} +// AOT Runtime Constants Address +operand immAOTRuntimeConstantsAddress() +%{ + // Check if the address is in the range of AOT Runtime Constants + predicate(AOTRuntimeConstants::contains((address)(n->get_ptr()))); + match(ConP); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // Float and Double operands // Double Immediate operand immD() @@ -6898,6 +6912,20 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con) ins_pipe(ialu_imm); %} +instruct loadAOTRCAddress(iRegPNoSp dst, immAOTRuntimeConstantsAddress con) +%{ + match(Set dst con); + + ins_cost(INSN_COST); + format %{ "adr $dst, $con\t# AOT Runtime Constants Address" %} + + ins_encode %{ + __ load_aotrc_address($dst$$Register, (address)$con$$constant); + %} + + ins_pipe(ialu_imm); +%} + // Load Narrow Pointer Constant instruct loadConN(iRegNNoSp dst, immN con) diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index c0621cbd5c2..30048a2079d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -33,6 +33,7 @@ #include "c1/c1_ValueStack.hpp" #include "ci/ciArrayKlass.hpp" #include "ci/ciInstance.hpp" +#include "code/aotCodeCache.hpp" #include "code/compiledIC.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" @@ -532,6 +533,15 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod case T_LONG: { assert(patch_code == lir_patch_none, "no patching handled here"); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address b = c->as_pointer(); + if (AOTRuntimeConstants::contains(b)) { + __ load_aotrc_address(dest->as_register_lo(), b); + break; + } + } +#endif __ mov(dest->as_register_lo(), (intptr_t)c->as_jlong()); break; } diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp index d7884c27a2c..68291720208 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" @@ -243,9 +244,25 @@ static void generate_post_barrier(MacroAssembler* masm, assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg, rscratch1); // Does store cross heap regions? - __ eor(tmp1, store_addr, new_val); // tmp1 := store address ^ new value - __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) - __ cbz(tmp1, done); + #if INCLUDE_CDS + // AOT code needs to load the barrier grain shift from the aot + // runtime constants area in the code cache otherwise we can compile + // it as an immediate operand + if (AOTCodeCache::is_on_for_dump()) { + address grain_shift_address = (address)AOTRuntimeConstants::grain_shift_address(); + __ eor(tmp1, store_addr, new_val); + __ lea(tmp2, ExternalAddress(grain_shift_address)); + __ ldrb(tmp2, tmp2); + __ lsrv(tmp1, tmp1, tmp2); + __ cbz(tmp1, done); + } else +#endif + { + __ eor(tmp1, store_addr, new_val); // tmp1 := store address ^ new value + __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) + __ cbz(tmp1, done); + } + // Crosses regions, storing null? if (new_val_may_be_null) { __ cbz(new_val, done); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 35e90d296c9..3e3e95be07e 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5754,6 +5754,14 @@ void MacroAssembler::adrp(Register reg1, const Address &dest, uint64_t &byte_off } void MacroAssembler::load_byte_map_base(Register reg) { +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address byte_map_base_adr = AOTRuntimeConstants::card_table_base_address(); + lea(reg, ExternalAddress(byte_map_base_adr)); + ldr(reg, Address(reg)); + return; + } +#endif CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); // Strictly speaking the card table base isn't an address at all, and it might @@ -5761,6 +5769,20 @@ void MacroAssembler::load_byte_map_base(Register reg) { mov(reg, (uint64_t)ctbs->card_table_base_const()); } +void MacroAssembler::load_aotrc_address(Register reg, address a) { +#if INCLUDE_CDS + assert(AOTRuntimeConstants::contains(a), "address out of range for data area"); + if (AOTCodeCache::is_on_for_dump()) { + // all aotrc field addresses should be registered in the AOTCodeCache address table + lea(reg, ExternalAddress(a)); + } else { + mov(reg, (uint64_t)a); + } +#else + ShouldNotReachHere(); +#endif +} + void MacroAssembler::build_frame(int framesize) { assert(framesize >= 2 * wordSize, "framesize must include space for FP/LR"); assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment"); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 7b5af532ca1..fa32f3055b9 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1476,6 +1476,9 @@ public: // Load the base of the cardtable byte map into reg. void load_byte_map_base(Register reg); + // Load a constant address in the AOT Runtime Constants area + void load_aotrc_address(Register reg, address a); + // Prolog generator routines to support switch between x86 code and // generated ARM code diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 37ee9451405..d9be0fdcc8d 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -32,6 +32,7 @@ #include "c1/c1_ValueStack.hpp" #include "ci/ciArrayKlass.hpp" #include "ci/ciInstance.hpp" +#include "code/aotCodeCache.hpp" #include "compiler/oopMap.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" @@ -535,6 +536,15 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod case T_LONG: { assert(patch_code == lir_patch_none, "no patching handled here"); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address b = c->as_pointer(); + if (AOTRuntimeConstants::contains(b)) { + __ load_aotrc_address(dest->as_register_lo(), b); + break; + } + } +#endif __ movptr(dest->as_register_lo(), (intptr_t)c->as_jlong()); break; } diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp index 34de9403ccf..b20d7b5cd07 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" @@ -268,6 +269,16 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, __ bind(done); } +#if INCLUDE_CDS +// return a register that differs from reg1, reg2, reg3 and reg4 + +static Register pick_different_reg(Register reg1, Register reg2 = noreg, Register reg3= noreg, Register reg4 = noreg) { + RegSet available = (RegSet::of(rscratch1, rscratch2, rax, rbx) + rdx - + RegSet::of(reg1, reg2, reg3, reg4)); + return *(available.begin()); +} +#endif // INCLUDE_CDS + static void generate_post_barrier(MacroAssembler* masm, const Register store_addr, const Register new_val, @@ -280,10 +291,32 @@ static void generate_post_barrier(MacroAssembler* masm, Label L_done; // Does store cross heap regions? - __ movptr(tmp1, store_addr); // tmp1 := store address - __ xorptr(tmp1, new_val); // tmp1 := store address ^ new value - __ shrptr(tmp1, G1HeapRegion::LogOfHRGrainBytes); // ((store address ^ new value) >> LogOfHRGrainBytes) == 0? - __ jccb(Assembler::equal, L_done); +#if INCLUDE_CDS + // AOT code needs to load the barrier grain shift from the aot + // runtime constants area in the code cache otherwise we can compile + // it as an immediate operand + + if (AOTCodeCache::is_on_for_dump()) { + address grain_shift_addr = AOTRuntimeConstants::grain_shift_address(); + Register save = pick_different_reg(rcx, tmp1, new_val, store_addr); + __ push(save); + __ movptr(save, store_addr); + __ xorptr(save, new_val); + __ push(rcx); + __ lea(rcx, ExternalAddress(grain_shift_addr)); + __ movl(rcx, Address(rcx, 0)); + __ shrptr(save); + __ pop(rcx); + __ pop(save); + __ jcc(Assembler::equal, L_done); + } else +#endif // INCLUDE_CDS + { + __ movptr(tmp1, store_addr); // tmp1 := store address + __ xorptr(tmp1, new_val); // tmp1 := store address ^ new value + __ shrptr(tmp1, G1HeapRegion::LogOfHRGrainBytes); // ((store address ^ new value) >> LogOfHRGrainBytes) == 0? + __ jccb(Assembler::equal, L_done); + } // Crosses regions, storing null? if (new_val_may_be_null) { diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp index 65e6b4e01fc..0ea769dd488 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" @@ -111,7 +112,15 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ shrptr(end, CardTable::card_shift()); __ subptr(end, addr); // end --> cards count - __ mov64(tmp, (intptr_t)ctbs->card_table_base_const()); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + __ lea(tmp, ExternalAddress(AOTRuntimeConstants::card_table_base_address())); + __ movq(tmp, Address(tmp, 0)); + } else +#endif + { + __ mov64(tmp, (intptr_t)ctbs->card_table_base_const()); + } __ addptr(addr, tmp); __ BIND(L_loop); __ movb(Address(addr, count, Address::times_1), 0); @@ -121,7 +130,7 @@ __ BIND(L_loop); __ BIND(L_done); } -void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst) { +void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch) { // Does a store check for the oop in register obj. The content of // register obj is destroyed afterwards. CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); @@ -136,6 +145,13 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob // never need to be relocated. On 64bit however the value may be too // large for a 32bit displacement. intptr_t byte_map_base = (intptr_t)ctbs->card_table_base_const(); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + __ lea(rscratch, ExternalAddress(AOTRuntimeConstants::card_table_base_address())); + __ movq(rscratch, Address(rscratch, 0)); + card_addr = Address(rscratch, obj, Address::times_1, 0); + } else +#endif if (__ is_simm32(byte_map_base)) { card_addr = Address(noreg, obj, Address::times_1, byte_map_base); } else { @@ -174,10 +190,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS if (needs_post_barrier) { // flatten object address if needed if (!precise || (dst.index() == noreg && dst.disp() == 0)) { - store_check(masm, dst.base(), dst); + store_check(masm, dst.base(), dst, tmp2); } else { __ lea(tmp1, dst); - store_check(masm, tmp1, dst); + store_check(masm, tmp1, dst, tmp2); } } } diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp index 0a36571c757..201c11062f2 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp @@ -33,7 +33,7 @@ protected: virtual void gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count) {} - void store_check(MacroAssembler* masm, Register obj, Address dst); + void store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch); virtual void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 83169df3456..b54f6adc263 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -10034,6 +10034,20 @@ void MacroAssembler::restore_legacy_gprs() { addq(rsp, 16 * wordSize); } +void MacroAssembler::load_aotrc_address(Register reg, address a) { +#if INCLUDE_CDS + assert(AOTRuntimeConstants::contains(a), "address out of range for data area"); + if (AOTCodeCache::is_on_for_dump()) { + // all aotrc field addresses should be registered in the AOTCodeCache address table + lea(reg, ExternalAddress(a)); + } else { + mov64(reg, (uint64_t)a); + } +#else + ShouldNotReachHere(); +#endif +} + void MacroAssembler::setcc(Assembler::Condition comparison, Register dst) { if (VM_Version::supports_apx_f()) { esetzucc(comparison, dst); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index eb23199ca63..5c049f710e2 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -2070,6 +2070,7 @@ public: void save_legacy_gprs(); void restore_legacy_gprs(); + void load_aotrc_address(Register reg, address a); void setcc(Assembler::Condition comparison, Register dst); }; diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index aed54fe93d4..0ffa4c2031c 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -5187,6 +5187,18 @@ operand immL_65535() interface(CONST_INTER); %} +// AOT Runtime Constants Address +operand immAOTRuntimeConstantsAddress() +%{ + // Check if the address is in the range of AOT Runtime Constants + predicate(AOTRuntimeConstants::contains((address)(n->get_ptr()))); + match(ConP); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + operand kReg() %{ constraint(ALLOC_IN_RC(vectmask_reg)); @@ -7332,6 +7344,19 @@ instruct loadD(regD dst, memory mem) ins_pipe(pipe_slow); // XXX %} +instruct loadAOTRCAddress(rRegP dst, immAOTRuntimeConstantsAddress con) +%{ + match(Set dst con); + + format %{ "leaq $dst, $con\t# AOT Runtime Constants Address" %} + + ins_encode %{ + __ load_aotrc_address($dst$$Register, (address)$con$$constant); + %} + + ins_pipe(ialu_reg_fat); +%} + // max = java.lang.Math.max(float a, float b) instruct maxF_reg_avx10_2(regF dst, regF a, regF b) %{ predicate(VM_Version::supports_avx10_2()); diff --git a/src/hotspot/share/adlc/main.cpp b/src/hotspot/share/adlc/main.cpp index 4e8a96617e8..8e6ea5bbec9 100644 --- a/src/hotspot/share/adlc/main.cpp +++ b/src/hotspot/share/adlc/main.cpp @@ -213,6 +213,7 @@ int main(int argc, char *argv[]) AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._VM_file._name)); AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._CPP_file, "memory/allocation.inline.hpp"); + AD.addInclude(AD._CPP_file, "code/aotCodeCache.hpp"); AD.addInclude(AD._CPP_file, "code/codeCache.hpp"); AD.addInclude(AD._CPP_file, "code/compiledIC.hpp"); AD.addInclude(AD._CPP_file, "code/nativeInst.hpp"); @@ -257,6 +258,7 @@ int main(int argc, char *argv[]) AD.addInclude(AD._CPP_PEEPHOLE_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._CPP_PIPELINE_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._DFA_file, "adfiles", get_basename(AD._HPP_file._name)); + AD.addInclude(AD._DFA_file, "code/aotCodeCache.hpp"); AD.addInclude(AD._DFA_file, "oops/compressedOops.hpp"); AD.addInclude(AD._DFA_file, "opto/cfgnode.hpp"); // Use PROB_MAX in predicate. AD.addInclude(AD._DFA_file, "opto/intrinsicnode.hpp"); diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 32a53691f3f..e5f68afc51d 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -29,9 +29,11 @@ #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.hpp" +#include "ci/ciUtilities.hpp" #include "classfile/javaAssertions.hpp" #include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" +#include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/gcConfig.hpp" #include "logging/logStream.hpp" #include "memory/memoryReserver.hpp" @@ -53,6 +55,7 @@ #endif #if INCLUDE_G1GC #include "gc/g1/g1BarrierSetRuntime.hpp" +#include "gc/g1/g1HeapRegion.hpp" #endif #if INCLUDE_SHENANDOAHGC #include "gc/shenandoah/shenandoahRuntime.hpp" @@ -258,6 +261,9 @@ void AOTCodeCache::init2() { return; } + // initialize aot runtime constants as appropriate to this runtime + AOTRuntimeConstants::initialize_from_runtime(); + // initialize the table of external routines so we can save // generated code blobs that reference them AOTCodeAddressTable* table = opened_cache->_table; @@ -1447,6 +1453,12 @@ void AOTCodeAddressTable::init_extrs() { #endif #endif // ZERO + // addresses of fields in AOT runtime constants area + address* p = AOTRuntimeConstants::field_addresses_list(); + while (*p != nullptr) { + SET_ADDRESS(_extrs, *p++); + } + _extrs_complete = true; log_debug(aot, codecache, init)("External addresses recorded"); } @@ -1729,6 +1741,11 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB if (addr == (address)-1) { // Static call stub has jump to itself return id; } + // Check card_table_base address first since it can point to any address + BarrierSet* bs = BarrierSet::barrier_set(); + bool is_const_card_table_base = !UseG1GC && !UseShenandoahGC && bs->is_a(BarrierSet::CardTableBarrierSet); + guarantee(!is_const_card_table_base || addr != ci_card_table_address_const(), "sanity"); + // Seach for C string id = id_for_C_string(addr); if (id >= 0) { @@ -1798,6 +1815,44 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB return id; } +AOTRuntimeConstants AOTRuntimeConstants::_aot_runtime_constants; + +void AOTRuntimeConstants::initialize_from_runtime() { + BarrierSet* bs = BarrierSet::barrier_set(); + address card_table_base = nullptr; + uint grain_shift = 0; +#if INCLUDE_G1GC + if (bs->is_a(BarrierSet::G1BarrierSet)) { + grain_shift = G1HeapRegion::LogOfHRGrainBytes; + } else +#endif +#if INCLUDE_SHENANDOAHGC + if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + grain_shift = 0; + } else +#endif + if (bs->is_a(BarrierSet::CardTableBarrierSet)) { + CardTable::CardValue* base = ci_card_table_address_const(); + assert(base != nullptr, "unexpected byte_map_base"); + card_table_base = base; + CardTableBarrierSet* ctbs = barrier_set_cast(bs); + grain_shift = ctbs->grain_shift(); + } + _aot_runtime_constants._card_table_base = card_table_base; + _aot_runtime_constants._grain_shift = grain_shift; +} + +address AOTRuntimeConstants::_field_addresses_list[] = { + ((address)&_aot_runtime_constants._card_table_base), + ((address)&_aot_runtime_constants._grain_shift), + nullptr +}; + +address AOTRuntimeConstants::card_table_base_address() { + assert(UseSerialGC || UseParallelGC, "Only these GCs have constant card table base"); + return (address)&_aot_runtime_constants._card_table_base; +} + // This is called after initialize() but before init2() // and _cache is not set yet. void AOTCodeCache::print_on(outputStream* st) { diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 45b4a15510d..85f8b47920f 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_CODE_AOTCODECACHE_HPP #define SHARE_CODE_AOTCODECACHE_HPP +#include "gc/shared/gc_globals.hpp" #include "runtime/stubInfo.hpp" /* @@ -422,4 +423,36 @@ public: #endif // PRODUCT }; +// code cache internal runtime constants area used by AOT code +class AOTRuntimeConstants { + friend class AOTCodeCache; + private: + address _card_table_base; + uint _grain_shift; + static address _field_addresses_list[]; + static AOTRuntimeConstants _aot_runtime_constants; + // private constructor for unique singleton + AOTRuntimeConstants() { } + // private for use by friend class AOTCodeCache + static void initialize_from_runtime(); + public: +#if INCLUDE_CDS + static bool contains(address adr) { + address base = (address)&_aot_runtime_constants; + address hi = base + sizeof(AOTRuntimeConstants); + return (base <= adr && adr < hi); + } + static address card_table_base_address(); + static address grain_shift_address() { return (address)&_aot_runtime_constants._grain_shift; } + static address* field_addresses_list() { + return _field_addresses_list; + } +#else + static bool contains(address adr) { return false; } + static address card_table_base_address() { return nullptr; } + static address grain_shift_address() { return nullptr; } + static address* field_addresses_list() { return nullptr; } +#endif +}; + #endif // SHARE_CODE_AOTCODECACHE_HPP diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index 406096acf10..c5c7058471c 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_G1_G1BARRIERSET_HPP #define SHARE_GC_G1_G1BARRIERSET_HPP +#include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" #include "gc/shared/bufferNode.hpp" #include "gc/shared/cardTable.hpp" @@ -116,6 +117,8 @@ class G1BarrierSet: public CardTableBarrierSet { virtual void print_on(outputStream* st) const; + virtual uint grain_shift() { return G1HeapRegion::LogOfHRGrainBytes; } + // Callbacks for runtime accesses. template class AccessBarrier: public CardTableBarrierSet::AccessBarrier { diff --git a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp index 914358760aa..86b74aa5736 100644 --- a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp @@ -22,6 +22,7 @@ * */ +#include "code/aotCodeCache.hpp" #include "gc/shared/c1/cardTableBarrierSetC1.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" @@ -123,6 +124,7 @@ void CardTableBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Op assert(addr->is_register(), "must be a register at this point"); #ifdef CARDTABLEBARRIERSET_POST_BARRIER_HELPER + assert(!AOTCodeCache::is_on(), "this path is not implemented"); gen->CardTableBarrierSet_post_barrier_helper(addr, card_table_base); #else LIR_Opr tmp = gen->new_pointer_register(); @@ -135,6 +137,17 @@ void CardTableBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Op } LIR_Address* card_addr; +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + // load the card table address from the AOT Runtime Constants area + LIR_Opr byte_map_base_adr = LIR_OprFact::intptrConst(AOTRuntimeConstants::card_table_base_address()); + LIR_Opr byte_map_base_reg = gen->new_pointer_register(); + __ move(byte_map_base_adr, byte_map_base_reg); + LIR_Address* byte_map_base_indirect = new LIR_Address(byte_map_base_reg, 0, T_LONG); + __ move(byte_map_base_indirect, byte_map_base_reg); + card_addr = new LIR_Address(tmp, byte_map_base_reg, T_BYTE); + } else +#endif if (gen->can_inline_as_constant(card_table_base)) { card_addr = new LIR_Address(tmp, card_table_base->as_jint(), T_BYTE); } else { diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp index 42af77ebdf4..f7445ff254f 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp @@ -23,6 +23,7 @@ */ #include "ci/ciUtilities.hpp" +#include "code/aotCodeCache.hpp" #include "gc/shared/c2/cardTableBarrierSetC2.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" @@ -114,13 +115,20 @@ Node* CardTableBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access return result; } -Node* CardTableBarrierSetC2::byte_map_base_node(GraphKit* kit) const { +Node* CardTableBarrierSetC2::byte_map_base_node(IdealKit* kit) const { // Get base of card map +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + // load the card table address from the AOT Runtime Constants area + Node* byte_map_base_adr = kit->makecon(TypeRawPtr::make(AOTRuntimeConstants::card_table_base_address())); + return kit->load_aot_const(byte_map_base_adr, TypeRawPtr::NOTNULL); + } +#endif CardTable::CardValue* card_table_base = ci_card_table_address_const(); if (card_table_base != nullptr) { return kit->makecon(TypeRawPtr::make((address)card_table_base)); } else { - return kit->null(); + return kit->makecon(Type::get_zero_type(T_ADDRESS)); } } @@ -168,7 +176,7 @@ void CardTableBarrierSetC2::post_barrier(GraphKit* kit, Node* card_offset = __ URShiftX(cast, __ ConI(CardTable::card_shift())); // Combine card table base and card offset - Node* card_adr = __ AddP(__ top(), byte_map_base_node(kit), card_offset); + Node* card_adr = __ AddP(__ top(), byte_map_base_node(&ideal), card_offset); // Get the alias_index for raw card-mark memory int adr_type = Compile::AliasIdxRaw; diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp index 84876808f0d..8f5bae8c6dd 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp @@ -43,7 +43,7 @@ protected: Node* new_val, const Type* value_type) const; virtual Node* atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const; - Node* byte_map_base_node(GraphKit* kit) const; + Node* byte_map_base_node(IdealKit* kit) const; public: virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const; diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index 3a9b46d9df8..5d355318b21 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -103,6 +103,10 @@ public: virtual void print_on(outputStream* st) const; + // The AOT code cache manager needs to know the region grain size + // shift for some barrier sets. + virtual uint grain_shift() { return 0; } + template class AccessBarrier: public BarrierSet::AccessBarrier { typedef BarrierSet::AccessBarrier Raw; diff --git a/src/hotspot/share/opto/idealKit.cpp b/src/hotspot/share/opto/idealKit.cpp index dd7e9ae52b7..fbb61bfdf72 100644 --- a/src/hotspot/share/opto/idealKit.cpp +++ b/src/hotspot/share/opto/idealKit.cpp @@ -360,6 +360,17 @@ Node* IdealKit::load(Node* ctl, return transform(ld); } +// Load AOT runtime constant +Node* IdealKit::load_aot_const(Node* adr, const Type* t) { + BasicType bt = t->basic_type(); + const TypePtr* adr_type = nullptr; // debug-mode-only argument + DEBUG_ONLY(adr_type = C->get_adr_type(Compile::AliasIdxRaw)); + Node* ctl = (Node*)C->root(); // Raw memory access needs control + Node* ld = LoadNode::make(_gvn, ctl, C->immutable_memory(), adr, adr_type, t, bt, MemNode::unordered, + LoadNode::DependsOnlyOnTest, false, false, false, false, 0); + return transform(ld); +} + Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx, MemNode::MemOrd mo, bool require_atomic_access, diff --git a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp index 280f61fd9a9..518c3b92136 100644 --- a/src/hotspot/share/opto/idealKit.hpp +++ b/src/hotspot/share/opto/idealKit.hpp @@ -224,6 +224,9 @@ class IdealKit: public StackObj { MemNode::MemOrd mo = MemNode::unordered, LoadNode::ControlDependency control_dependency = LoadNode::DependsOnlyOnTest); + // Load AOT runtime constant + Node* load_aot_const(Node* adr, const Type* t); + // Return the new StoreXNode Node* store(Node* ctl, Node* adr, diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index c637737eef9..1a0872ee0e6 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -97,7 +97,7 @@ const Type::TypeInfo Type::_type_info[Type::lastype] = { { Bad, T_ILLEGAL, "vectorz:", false, Op_VecZ, relocInfo::none }, // VectorZ #endif { Bad, T_ADDRESS, "anyptr:", false, Op_RegP, relocInfo::none }, // AnyPtr - { Bad, T_ADDRESS, "rawptr:", false, Op_RegP, relocInfo::none }, // RawPtr + { Bad, T_ADDRESS, "rawptr:", false, Op_RegP, relocInfo::external_word_type }, // RawPtr { Bad, T_OBJECT, "oop:", true, Op_RegP, relocInfo::oop_type }, // OopPtr { Bad, T_OBJECT, "inst:", true, Op_RegP, relocInfo::oop_type }, // InstPtr { Bad, T_OBJECT, "ary:", true, Op_RegP, relocInfo::oop_type }, // AryPtr From b0318fee71926b424e770ff1c85add0d96cc85c0 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 27 Feb 2026 22:24:02 +0000 Subject: [PATCH 092/636] 8378865: After fix for JDK-8378385 two tests are failing on windows Reviewed-by: kizune --- .../Dialog/CloseDialog/CloseDialogTest.java | 119 ---------------- .../DisplayChangesException.java | 133 ------------------ 2 files changed, 252 deletions(-) delete mode 100644 test/jdk/java/awt/Dialog/CloseDialog/CloseDialogTest.java delete mode 100644 test/jdk/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java diff --git a/test/jdk/java/awt/Dialog/CloseDialog/CloseDialogTest.java b/test/jdk/java/awt/Dialog/CloseDialog/CloseDialogTest.java deleted file mode 100644 index 1201e5c835c..00000000000 --- a/test/jdk/java/awt/Dialog/CloseDialog/CloseDialogTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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. - */ - -import java.awt.Dialog; -import java.awt.Frame; -import java.io.*; -import javax.swing.*; -import sun.awt.SunToolkit; -import java.util.concurrent.atomic.AtomicReference; - -/** - * @test - * @key headful - * @bug 8043705 - * @summary Can't exit color chooser dialog when running in non-default AppContext - * @modules java.desktop/sun.awt - * @run main CloseDialogTest - */ - -public class CloseDialogTest { - - private static volatile Frame frame; - private static volatile Dialog dialog; - private static volatile InputStream testErrorStream; - private static final PrintStream systemErrStream = System.err; - private static final AtomicReference caughtException - = new AtomicReference<>(); - - public static void main(String[] args) throws Exception { - - // redirect System err - PipedOutputStream errorOutputStream = new PipedOutputStream(); - testErrorStream = new PipedInputStream(errorOutputStream); - System.setErr(new PrintStream(errorOutputStream)); - - ThreadGroup swingTG = new ThreadGroup(getRootThreadGroup(), "SwingTG"); - try { - new Thread(swingTG, () -> { - SunToolkit.createNewAppContext(); - SwingUtilities.invokeLater(() -> { - frame = new Frame(); - frame.setSize(300, 300); - frame.setVisible(true); - - dialog = new Dialog(frame); - dialog.setSize(200, 200); - dialog.setModal(true); - dialog.setVisible(true); - }); - }).start(); - - Thread.sleep(400); - - Thread disposeThread = new Thread(swingTG, () -> - SwingUtilities.invokeLater(() -> { - try { - while (dialog == null || !dialog.isVisible()) { - Thread.sleep(100); - } - dialog.setVisible(false); - dialog.dispose(); - frame.dispose(); - } catch (Exception e) { - caughtException.set(e); - } - })); - disposeThread.start(); - disposeThread.join(); - Thread.sleep(500); - - // read System err - final char[] buffer = new char[2048]; - System.err.print("END"); - System.setErr(systemErrStream); - try (Reader in = new InputStreamReader(testErrorStream, "UTF-8")) { - int size = in.read(buffer, 0, buffer.length); - String errorString = new String(buffer, 0, size); - if (!errorString.startsWith("END")) { - System.err.println(errorString. - substring(0, errorString.length() - 4)); - throw new RuntimeException("Error output is not empty!"); - } - } - } finally { - if (caughtException.get() != null) { - throw new RuntimeException("Failed. Caught exception!", - caughtException.get()); - } - } - } - - private static ThreadGroup getRootThreadGroup() { - ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); - while (threadGroup.getParent() != null) { - threadGroup = threadGroup.getParent(); - } - return threadGroup; - } -} diff --git a/test/jdk/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java b/test/jdk/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java deleted file mode 100644 index 0f13265ac58..00000000000 --- a/test/jdk/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2018, 2020, 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. - */ - -import java.awt.EventQueue; -import java.awt.GraphicsEnvironment; -import java.awt.Toolkit; -import java.lang.reflect.Method; -import java.util.concurrent.CountDownLatch; - -import javax.swing.JButton; -import javax.swing.JFrame; - -import sun.awt.DisplayChangedListener; -import sun.awt.SunToolkit; - -/** - * @test - * @key headful - * @bug 8207070 - * @modules java.desktop/sun.java2d - * java.desktop/sun.awt - * java.desktop/sun.awt.windows:open - */ -public final class DisplayChangesException { - - private static boolean fail; - private static CountDownLatch go = new CountDownLatch(1); - - static final class TestThread extends Thread { - - private JFrame frame; - - private TestThread(ThreadGroup tg, String threadName) { - super(tg, threadName); - } - - public void run() { - try { - test(); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - - private void test() throws Exception { - SunToolkit.createNewAppContext(); - EventQueue.invokeAndWait(() -> { - frame = new JFrame(); - final JButton b = new JButton(); - b.addPropertyChangeListener(evt -> { - if (!SunToolkit.isDispatchThreadForAppContext(b)) { - System.err.println("Wrong thread:" + currentThread()); - fail = true; - } - }); - frame.add(b); - frame.setSize(100, 100); - frame.setLocationRelativeTo(null); - frame.pack(); - }); - go.await(); - EventQueue.invokeAndWait(() -> { - frame.dispose(); - }); - } - } - - public static void main(final String[] args) throws Exception { - ThreadGroup tg0 = new ThreadGroup("ThreadGroup0"); - ThreadGroup tg1 = new ThreadGroup("ThreadGroup1"); - - TestThread t0 = new TestThread(tg0, "TestThread 0"); - TestThread t1 = new TestThread(tg1, "TestThread 1"); - - t0.start(); - t1.start(); - Thread.sleep(1500); // Cannot use Robot.waitForIdle - testToolkit(); - Thread.sleep(1500); - testGE(); - Thread.sleep(1500); - go.countDown(); - - if (fail) { - throw new RuntimeException(); - } - } - - private static void testGE() { - Object ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - if (!(ge instanceof DisplayChangedListener)) { - return; - } - ((DisplayChangedListener) ge).displayChanged(); - } - - private static void testToolkit() { - final Class toolkit; - try { - toolkit = Class.forName("sun.awt.windows.WToolkit"); - } catch (final ClassNotFoundException ignored) { - return; - } - try { - final Method displayChanged = toolkit.getMethod("displayChanged"); - displayChanged.invoke(Toolkit.getDefaultToolkit()); - } catch (final Exception e) { - e.printStackTrace(); - fail = true; - } - } -} - From 58a7ccfca41bf06f6da98ff8b8d58c4ff0f16551 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 27 Feb 2026 23:13:19 +0000 Subject: [PATCH 093/636] 8378798: Test tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java failed: missing IOException Reviewed-by: almatvee --- .../jdk/jpackage/internal/util/CommandOutputControlTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java index d71cf7c4d41..b179f32447f 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java @@ -458,7 +458,7 @@ public class CommandOutputControlTest { processDestroyer.get().join(); } - @DisabledOnOs(value = OS.MAC, disabledReason = "Closing a stream doesn't consistently cause a trouble as it should") + @DisabledOnOs(value = {OS.MAC, OS.LINUX}, disabledReason = "Closing a stream doesn't consistently cause a trouble as expected") @ParameterizedTest @EnumSource(OutputStreams.class) public void test_close_streams(OutputStreams action) throws InterruptedException, IOException { From e5aac0961d53611fee57333c0a959e3d5b42ede8 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Sat, 28 Feb 2026 00:33:11 +0000 Subject: [PATCH 094/636] 8378752: Enable some subword vector casts IR matching tests for RISC-V Reviewed-by: fyang, jkarthikeyan --- .../TestCompatibleUseDefTypeSize.java | 28 ++++----- .../loopopts/superword/TestReductions.java | 60 +++++++++---------- .../vectorization/TestSubwordTruncation.java | 16 ++--- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java index 7399a1ec411..873533aaba8 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestCompatibleUseDefTypeSize.java @@ -514,7 +514,7 @@ public class TestCompatibleUseDefTypeSize { // Narrowing @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" }) public Object[] testIntToShort(int[] ints, short[] res) { @@ -527,7 +527,7 @@ public class TestCompatibleUseDefTypeSize { @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_char)", ">0" }) public Object[] testIntToChar(int[] ints, char[] res) { @@ -539,7 +539,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" }) public Object[] testIntToByte(int[] ints, byte[] res) { @@ -551,7 +551,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_S2B, IRNode.VECTOR_SIZE + "min(max_short, max_byte)", ">0" }) public Object[] testShortToByte(short[] shorts, byte[] res) { @@ -575,7 +575,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_L2S, IRNode.VECTOR_SIZE + "min(max_long, max_short)", ">0" }) public Object[] testLongToShort(long[] longs, short[] res) { @@ -587,7 +587,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_L2S, IRNode.VECTOR_SIZE + "min(max_long, max_char)", ">0" }) public Object[] testLongToChar(long[] longs, char[] res) { @@ -599,7 +599,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_L2I, IRNode.VECTOR_SIZE + "min(max_long, max_int)", ">0" }) public Object[] testLongToInt(long[] longs, int[] res) { @@ -611,7 +611,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.STORE_VECTOR, ">0" }) public Object[] testShortToChar(short[] shorts, char[] res) { @@ -625,7 +625,7 @@ public class TestCompatibleUseDefTypeSize { // Widening @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_S2I, IRNode.VECTOR_SIZE + "min(max_short, max_int)", ">0" }) public Object[] testShortToInt(short[] shorts, int[] res) { @@ -637,7 +637,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_B2I, IRNode.VECTOR_SIZE + "min(max_byte, max_int)", ">0" }) public Object[] testByteToInt(byte[] bytes, int[] res) { @@ -649,7 +649,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_byte, max_short)", ">0" }) public Object[] testByteToShort(byte[] bytes, short[] res) { @@ -661,7 +661,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_byte, max_char)", ">0" }) public Object[] testByteToChar(byte[] bytes, char[] res) { @@ -685,7 +685,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_S2L, IRNode.VECTOR_SIZE + "min(max_short, max_long)", ">0" }) public Object[] testShortToLong(short[] shorts, long[] res) { @@ -697,7 +697,7 @@ public class TestCompatibleUseDefTypeSize { } @Test - @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true" }, + @IR(applyIfCPUFeatureOr = { "avx", "true", "asimd", "true", "rvv", "true" }, applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"}, counts = { IRNode.VECTOR_CAST_I2L, IRNode.VECTOR_SIZE + "min(max_int, max_long)", ">0" }) public Object[] testIntToLong(int[] ints, long[] res) { diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java index e0f44bcdb3b..9d674950499 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java @@ -458,7 +458,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -475,7 +475,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -492,7 +492,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -531,7 +531,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -548,7 +548,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -566,7 +566,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -583,7 +583,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -600,7 +600,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -639,7 +639,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -656,7 +656,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -674,7 +674,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -691,7 +691,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -708,7 +708,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -747,7 +747,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -764,7 +764,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_B, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1016,7 +1016,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1033,7 +1033,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1050,7 +1050,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1089,7 +1089,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1106,7 +1106,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1124,7 +1124,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1141,7 +1141,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1158,7 +1158,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1197,7 +1197,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1214,7 +1214,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1232,7 +1232,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1249,7 +1249,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1266,7 +1266,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1305,7 +1305,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1322,7 +1322,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_S, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java index 29331cc1845..2f6296e41d2 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java @@ -74,7 +74,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortLeadingZeros(short[] in) { short[] res = new short[SIZE]; @@ -100,7 +100,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortTrailingZeros(short[] in) { short[] res = new short[SIZE]; @@ -126,7 +126,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortReverse(short[] in) { short[] res = new short[SIZE]; @@ -152,7 +152,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupShortArray") public Object[] testShortBitCount(short[] in) { short[] res = new short[SIZE]; @@ -282,7 +282,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteLeadingZeros(byte[] in) { byte[] res = new byte[SIZE]; @@ -308,7 +308,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteTrailingZeros(byte[] in) { byte[] res = new byte[SIZE]; @@ -334,7 +334,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteReverse(byte[] in) { byte[] res = new byte[SIZE]; @@ -411,7 +411,7 @@ public class TestSubwordTruncation { @Test @IR(counts = { IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", "> 0" }, - applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true" }) + applyIfCPUFeatureOr = { "avx2", "true", "asimd", "true", "zvbb", "true" }) @Arguments(setup = "setupByteArray") public Object[] testByteBitCount(byte[] in) { byte[] res = new byte[SIZE]; From 357f29dc864552a2c41e61b18ea73d9dda1c9825 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sat, 28 Feb 2026 03:28:47 +0000 Subject: [PATCH 095/636] 8378312: [VectorAPI] libraryUnaryOp/libraryBinaryOp failed to intrinsify Reviewed-by: psandoz, sherman --- src/hotspot/share/classfile/vmIntrinsics.hpp | 4 +- .../jdk/internal/vm/vector/VectorSupport.java | 4 +- .../jdk/incubator/vector/DoubleVector.java | 4 +- .../jdk/incubator/vector/FloatVector.java | 4 +- .../incubator/vector/VectorMathLibrary.java | 10 +-- .../compiler/lib/ir_framework/IRNode.java | 6 ++ .../TestVectorLibraryUnaryOpAndBinaryOp.java | 62 +++++++++++++++++++ 7 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 29cb5d737d8..67817682ced 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -1029,7 +1029,7 @@ class methodHandle; do_intrinsic(_VectorUnaryLibOp, jdk_internal_vm_vector_VectorSupport, vector_unary_lib_op_name, vector_unary_lib_op_sig, F_S) \ do_signature(vector_unary_lib_op_sig,"(J" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/String;" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ @@ -1040,7 +1040,7 @@ class methodHandle; do_intrinsic(_VectorBinaryLibOp, jdk_internal_vm_vector_VectorSupport, vector_binary_lib_op_name, vector_binary_lib_op_sig, F_S) \ do_signature(vector_binary_lib_op_sig,"(J" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/String;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ diff --git a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java index 03f95222a52..23a787971c0 100644 --- a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java @@ -336,7 +336,7 @@ public class VectorSupport { @IntrinsicCandidate public static , E> - V libraryUnaryOp(long addr, Class vClass, Class eClass, int length, String debugName, + V libraryUnaryOp(long addr, Class vClass, int laneType, int length, String debugName, V v, UnaryOperation defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; @@ -374,7 +374,7 @@ public class VectorSupport { @IntrinsicCandidate public static - V libraryBinaryOp(long addr, Class vClass, Class eClass, int length, String debugName, + V libraryBinaryOp(long addr, Class vClass, int laneType, int length, String debugName, V v1, V v2, BinaryOperation defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 519122b4d28..5e7c97dc56d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -724,7 +724,7 @@ public abstract class DoubleVector extends AbstractVector { @ForceInline final DoubleVector unaryMathOp(VectorOperators.Unary op) { - return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), DoubleVector::unaryOperations, + return VectorMathLibrary.unaryMathOp(op, opCode(op), vspecies(), DoubleVector::unaryOperations, this); } @@ -851,7 +851,7 @@ public abstract class DoubleVector extends AbstractVector { @ForceInline final DoubleVector binaryMathOp(VectorOperators.Binary op, DoubleVector that) { - return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), DoubleVector::binaryOperations, + return VectorMathLibrary.binaryMathOp(op, opCode(op), vspecies(), DoubleVector::binaryOperations, this, that); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 9950abf696d..5862a295fa3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -724,7 +724,7 @@ public abstract class FloatVector extends AbstractVector { @ForceInline final FloatVector unaryMathOp(VectorOperators.Unary op) { - return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), FloatVector::unaryOperations, + return VectorMathLibrary.unaryMathOp(op, opCode(op), vspecies(), FloatVector::unaryOperations, this); } @@ -851,7 +851,7 @@ public abstract class FloatVector extends AbstractVector { @ForceInline final FloatVector binaryMathOp(VectorOperators.Binary op, FloatVector that) { - return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), FloatVector::binaryOperations, + return VectorMathLibrary.binaryMathOp(op, opCode(op), vspecies(), FloatVector::binaryOperations, this, that); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java index 4898cb70884..1c1cfcc78c7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.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 @@ -283,7 +283,7 @@ import static jdk.internal.vm.vector.Utils.debug; @ForceInline /*package-private*/ static > - V unaryMathOp(Unary op, int opc, VectorSpecies vspecies, + V unaryMathOp(Unary op, int opc, AbstractSpecies vspecies, IntFunction> implSupplier, V v) { var entry = lookup(op, opc, vspecies, implSupplier); @@ -293,7 +293,7 @@ import static jdk.internal.vm.vector.Utils.debug; @SuppressWarnings({"unchecked"}) Class vt = (Class)vspecies.vectorType(); return VectorSupport.libraryUnaryOp( - entry.entry.address(), vt, vspecies.elementType(), vspecies.length(), entry.name, + entry.entry.address(), vt, vspecies.laneTypeOrdinal(), vspecies.length(), entry.name, v, entry.impl); } else { @@ -304,7 +304,7 @@ import static jdk.internal.vm.vector.Utils.debug; @ForceInline /*package-private*/ static > - V binaryMathOp(Binary op, int opc, VectorSpecies vspecies, + V binaryMathOp(Binary op, int opc, AbstractSpecies vspecies, IntFunction> implSupplier, V v1, V v2) { var entry = lookup(op, opc, vspecies, implSupplier); @@ -314,7 +314,7 @@ import static jdk.internal.vm.vector.Utils.debug; @SuppressWarnings({"unchecked"}) Class vt = (Class)vspecies.vectorType(); return VectorSupport.libraryBinaryOp( - entry.entry.address(), vt, vspecies.elementType(), vspecies.length(), entry.name, + entry.entry.address(), vt, vspecies.laneTypeOrdinal(), vspecies.length(), entry.name, v1, v2, entry.impl); } else { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index c31ce198644..3508c06ad0a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1488,6 +1488,12 @@ public class IRNode { beforeMatchingNameRegex(VECTOR_MASK_FIRST_TRUE, "VectorMaskFirstTrue"); } + // Can only be used if libjsvml or libsleef is available + public static final String CALL_LEAF_VECTOR = PREFIX + "CALL_LEAF_VECTOR" + POSTFIX; + static { + beforeMatchingNameRegex(CALL_LEAF_VECTOR, "CallLeafVector"); + } + // Can only be used if avx512_vnni is available. public static final String MUL_ADD_VS2VI_VNNI = PREFIX + "MUL_ADD_VS2VI_VNNI" + POSTFIX; static { diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java new file mode 100644 index 00000000000..f7837e1abfa --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * 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.vectorapi; + +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; + +/** + * @test + * @bug 8378312 + * @library /test/lib / + * @summary VectorAPI: libraryUnaryOp and libraryBinaryOp should be intrinsified. + * @modules jdk.incubator.vector + * + * @run driver compiler.vectorapi.TestVectorLibraryUnaryOpAndBinaryOp + */ + +public class TestVectorLibraryUnaryOpAndBinaryOp { + + @Test + @IR(counts = { IRNode.CALL_LEAF_VECTOR, "= 1" }, applyIfCPUFeatureOr = { "asimd", "true", "avx", "true" }) + public static void testUnary() { + var vec = FloatVector.broadcast(FloatVector.SPECIES_128, 3.14f); + vec.lanewise(VectorOperators.COS); + } + + @Test + @IR(counts = { IRNode.CALL_LEAF_VECTOR, "= 1" }, applyIfCPUFeatureOr = { "asimd", "true", "avx", "true" }) + public static void testBinary() { + var vec = FloatVector.broadcast(FloatVector.SPECIES_128, 2.0f); + vec.lanewise(VectorOperators.HYPOT, 1.0f); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.addFlags("--add-modules=jdk.incubator.vector") + .start(); + } + +} From 20ac1b182eef2af02d017f9e7724fabd60724a33 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Sat, 28 Feb 2026 04:33:20 +0000 Subject: [PATCH 096/636] 8378871: CPU feature flags are not properly set in vm_version_windows_aarch64.cpp Reviewed-by: kvn --- .../vm_version_windows_aarch64.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp index a20feadcba4..93beb549366 100644 --- a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp @@ -27,22 +27,30 @@ #include "runtime/vm_version.hpp" int VM_Version::get_current_sve_vector_length() { - assert(_features & CPU_SVE, "should not call this"); + assert(VM_Version::supports_sve(), "should not call this"); ShouldNotReachHere(); return 0; } int VM_Version::set_and_get_current_sve_vector_length(int length) { - assert(_features & CPU_SVE, "should not call this"); + assert(VM_Version::supports_sve(), "should not call this"); ShouldNotReachHere(); return 0; } void VM_Version::get_os_cpu_info() { - if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) _features |= CPU_CRC32; - if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) _features |= CPU_AES | CPU_SHA1 | CPU_SHA2; - if (IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE)) _features |= CPU_ASIMD; + if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_CRC32); + } + if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) { + set_feature(CPU_AES); + set_feature(CPU_SHA1); + set_feature(CPU_SHA2); + } + if (IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE)) { + set_feature(CPU_ASIMD); + } // No check for CPU_PMULL, CPU_SVE, CPU_SVE2 __int64 dczid_el0 = _ReadStatusReg(0x5807 /* ARM64_DCZID_EL0 */); From d62b9f78ca4a35bb6c5f665172c7abce4dac56ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Sat, 28 Feb 2026 05:41:32 +0000 Subject: [PATCH 097/636] 8377992: (zipfs) Align ZipFileSystem END header validation with the ZipFile implementation Reviewed-by: lancea --- .../share/classes/java/util/zip/ZipFile.java | 16 +- .../classes/jdk/nio/zipfs/ZipFileSystem.java | 40 ++- .../util/zip/ZipFile/EndOfCenValidation.java | 279 ++---------------- .../jdk/jdk/nio/zipfs/EndOfCenValidation.java | 150 ++++++++++ test/lib/jdk/test/lib/util/ZipUtils.java | 247 ++++++++++++++++ 5 files changed, 464 insertions(+), 268 deletions(-) create mode 100644 test/jdk/jdk/nio/zipfs/EndOfCenValidation.java create mode 100644 test/lib/jdk/test/lib/util/ZipUtils.java diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 43a3b37b7d0..140d76c8c91 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1719,8 +1719,10 @@ public class ZipFile implements ZipConstants, Closeable { this.cen = null; return; // only END header present } - if (end.cenlen > end.endpos) + // Validate END header + if (end.cenlen > end.endpos) { zerror("invalid END header (bad central directory size)"); + } long cenpos = end.endpos - end.cenlen; // position of CEN table // Get position of first local file (LOC) header, taking into // account that there may be a stub prefixed to the ZIP file. @@ -1728,18 +1730,22 @@ public class ZipFile implements ZipConstants, Closeable { if (locpos < 0) { zerror("invalid END header (bad central directory offset)"); } - // read in the CEN if (end.cenlen > MAX_CEN_SIZE) { zerror("invalid END header (central directory size too large)"); } if (end.centot < 0 || end.centot > end.cenlen / CENHDR) { zerror("invalid END header (total entries count too large)"); } - cen = this.cen = new byte[(int)end.cenlen]; - if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen) { + // Validation ensures these are <= Integer.MAX_VALUE + int cenlen = Math.toIntExact(end.cenlen); + int centot = Math.toIntExact(end.centot); + + // read in the CEN + cen = this.cen = new byte[cenlen]; + if (readFullyAt(cen, 0, cen.length, cenpos) != cenlen) { zerror("read CEN tables failed"); } - this.total = Math.toIntExact(end.centot); + this.total = centot; } else { cen = this.cen; this.total = knownTotal; diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java index b3db11eb1fe..3223ff9dccd 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java @@ -105,6 +105,9 @@ class ZipFileSystem extends FileSystem { private static final String COMPRESSION_METHOD_DEFLATED = "DEFLATED"; // Value specified for compressionMethod property to not compress Zip entries private static final String COMPRESSION_METHOD_STORED = "STORED"; + // CEN size is limited to the maximum array size in the JDK + // See ArraysSupport.SOFT_MAX_ARRAY_LENGTH; + private static final int MAX_CEN_SIZE = Integer.MAX_VALUE - 8; private final ZipFileSystemProvider provider; private final Path zfpath; @@ -1353,7 +1356,7 @@ class ZipFileSystem extends FileSystem { // to use the end64 values end.cenlen = cenlen64; end.cenoff = cenoff64; - end.centot = (int)centot64; // assume total < 2g + end.centot = centot64; end.endpos = end64pos; return end; } @@ -1575,23 +1578,34 @@ class ZipFileSystem extends FileSystem { buildNodeTree(); return null; // only END header present } - if (end.cenlen > end.endpos) - throw new ZipException("invalid END header (bad central directory size)"); + // Validate END header + if (end.cenlen > end.endpos) { + zerror("invalid END header (bad central directory size)"); + } long cenpos = end.endpos - end.cenlen; // position of CEN table - // Get position of first local file (LOC) header, taking into - // account that there may be a stub prefixed to the zip file. + // account that there may be a stub prefixed to the ZIP file. locpos = cenpos - end.cenoff; - if (locpos < 0) - throw new ZipException("invalid END header (bad central directory offset)"); + if (locpos < 0) { + zerror("invalid END header (bad central directory offset)"); + } + if (end.cenlen > MAX_CEN_SIZE) { + zerror("invalid END header (central directory size too large)"); + } + if (end.centot < 0 || end.centot > end.cenlen / CENHDR) { + zerror("invalid END header (total entries count too large)"); + } + // Validation ensures these are <= Integer.MAX_VALUE + int cenlen = Math.toIntExact(end.cenlen); + int centot = Math.toIntExact(end.centot); // read in the CEN - byte[] cen = new byte[(int)(end.cenlen)]; - if (readNBytesAt(cen, 0, cen.length, cenpos) != end.cenlen) { - throw new ZipException("read CEN tables failed"); + byte[] cen = new byte[cenlen]; + if (readNBytesAt(cen, 0, cen.length, cenpos) != cenlen) { + zerror("read CEN tables failed"); } // Iterate through the entries in the central directory - inodes = LinkedHashMap.newLinkedHashMap(end.centot + 1); + inodes = LinkedHashMap.newLinkedHashMap(centot + 1); int pos = 0; int limit = cen.length; while (pos < limit) { @@ -2666,7 +2680,7 @@ class ZipFileSystem extends FileSystem { // int disknum; // int sdisknum; // int endsub; - int centot; // 4 bytes + long centot; // 4 bytes long cenlen; // 4 bytes long cenoff; // 4 bytes // int comlen; // comment length @@ -2689,7 +2703,7 @@ class ZipFileSystem extends FileSystem { xoff = ZIP64_MINVAL; hasZip64 = true; } - int count = centot; + int count = Math.toIntExact(centot); if (count >= ZIP64_MINVAL32) { count = ZIP64_MINVAL32; hasZip64 = true; diff --git a/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java b/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java index 7adcfb9c128..7ca71c9890a 100644 --- a/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java +++ b/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.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 @@ -25,33 +25,26 @@ * @bug 8272746 * @modules java.base/jdk.internal.util * @summary Verify that ZipFile rejects files with CEN sizes exceeding the implementation limit + * @library /test/lib + * @build jdk.test.lib.util.ZipUtils * @run junit/othervm EndOfCenValidation */ import jdk.internal.util.ArraysSupport; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.io.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HexFormat; -import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; -import static org.junit.jupiter.api.Assertions.*; +import static jdk.test.lib.util.ZipUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * This test augments {@link TestTooManyEntries}. It creates sparse ZIPs where @@ -65,36 +58,13 @@ import static org.junit.jupiter.api.Assertions.*; public class EndOfCenValidation { // Zip files produced by this test - public static final Path CEN_TOO_LARGE_ZIP = Path.of("cen-size-too-large.zip"); - public static final Path INVALID_CEN_SIZE = Path.of("invalid-zen-size.zip"); - public static final Path BAD_CEN_OFFSET_ZIP = Path.of("bad-cen-offset.zip"); - // Some ZipFile constants for manipulating the 'End of central directory record' (END header) - private static final int ENDHDR = ZipFile.ENDHDR; // End of central directory record size - private static final int ENDSIZ = ZipFile.ENDSIZ; // Offset of CEN size field within ENDHDR - private static final int ENDOFF = ZipFile.ENDOFF; // Offset of CEN offset field within ENDHDR + static final Path CEN_TOO_LARGE_ZIP = Path.of("cen-size-too-large.zip"); + static final Path INVALID_CEN_SIZE = Path.of("invalid-zen-size.zip"); + static final Path BAD_CEN_OFFSET_ZIP = Path.of("bad-cen-offset.zip"); + static final Path BAD_ENTRY_COUNT_ZIP = Path.of("bad-entry-count.zip"); + // Maximum allowed CEN size allowed by ZipFile - private static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; - - // Expected message when CEN size does not match file size - private static final String INVALID_CEN_BAD_SIZE = "invalid END header (bad central directory size)"; - // Expected message when CEN offset is too large - private static final String INVALID_CEN_BAD_OFFSET = "invalid END header (bad central directory offset)"; - // Expected message when CEN size is too large - private static final String INVALID_CEN_SIZE_TOO_LARGE = "invalid END header (central directory size too large)"; - // Expected message when total entry count is too large - private static final String INVALID_BAD_ENTRY_COUNT = "invalid END header (total entries count too large)"; - - // A valid ZIP file, used as a template - private byte[] zipBytes; - - /** - * Create a valid ZIP file, used as a template - * @throws IOException if an error occurs - */ - @BeforeEach - public void setup() throws IOException { - zipBytes = templateZip(); - } + static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; /** * Delete big files after test, in case the file system did not support sparse files. @@ -105,6 +75,7 @@ public class EndOfCenValidation { Files.deleteIfExists(CEN_TOO_LARGE_ZIP); Files.deleteIfExists(INVALID_CEN_SIZE); Files.deleteIfExists(BAD_CEN_OFFSET_ZIP); + Files.deleteIfExists(BAD_ENTRY_COUNT_ZIP); } /** @@ -115,14 +86,8 @@ public class EndOfCenValidation { @Test public void shouldRejectTooLargeCenSize() throws IOException { int size = MAX_CEN_SIZE + 1; - Path zip = zipWithModifiedEndRecord(size, true, 0, CEN_TOO_LARGE_ZIP); - - ZipException ex = assertThrows(ZipException.class, () -> { - new ZipFile(zip.toFile()); - }); - - assertEquals(INVALID_CEN_SIZE_TOO_LARGE, ex.getMessage()); + verifyRejection(zip, INVALID_CEN_SIZE_TOO_LARGE); } /** @@ -133,16 +98,9 @@ public class EndOfCenValidation { */ @Test public void shouldRejectInvalidCenSize() throws IOException { - int size = MAX_CEN_SIZE; - Path zip = zipWithModifiedEndRecord(size, false, 0, INVALID_CEN_SIZE); - - ZipException ex = assertThrows(ZipException.class, () -> { - new ZipFile(zip.toFile()); - }); - - assertEquals(INVALID_CEN_BAD_SIZE, ex.getMessage()); + verifyRejection(zip, INVALID_CEN_BAD_SIZE); } /** @@ -153,16 +111,9 @@ public class EndOfCenValidation { */ @Test public void shouldRejectInvalidCenOffset() throws IOException { - int size = MAX_CEN_SIZE; - Path zip = zipWithModifiedEndRecord(size, true, 100, BAD_CEN_OFFSET_ZIP); - - ZipException ex = assertThrows(ZipException.class, () -> { - new ZipFile(zip.toFile()); - }); - - assertEquals(INVALID_CEN_BAD_OFFSET, ex.getMessage()); + verifyRejection(zip, INVALID_CEN_BAD_OFFSET); } /** @@ -181,192 +132,20 @@ public class EndOfCenValidation { Long.MAX_VALUE // Unreasonably large }) public void shouldRejectBadTotalEntries(long totalEntries) throws IOException { - /** - * A small ZIP using the ZIP64 format. - * - * ZIP created using: "echo -n hello | zip zip64.zip -" - * Hex encoded using: "cat zip64.zip | xxd -ps" - * - * The file has the following structure: - * - * 0000 LOCAL HEADER #1 04034B50 - * 0004 Extract Zip Spec 2D '4.5' - * 0005 Extract OS 00 'MS-DOS' - * 0006 General Purpose Flag 0000 - * 0008 Compression Method 0000 'Stored' - * 000A Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' - * 000E CRC 363A3020 - * 0012 Compressed Length FFFFFFFF - * 0016 Uncompressed Length FFFFFFFF - * 001A Filename Length 0001 - * 001C Extra Length 0014 - * 001E Filename '-' - * 001F Extra ID #0001 0001 'ZIP64' - * 0021 Length 0010 - * 0023 Uncompressed Size 0000000000000006 - * 002B Compressed Size 0000000000000006 - * 0033 PAYLOAD hello. - * - * 0039 CENTRAL HEADER #1 02014B50 - * 003D Created Zip Spec 1E '3.0' - * 003E Created OS 03 'Unix' - * 003F Extract Zip Spec 2D '4.5' - * 0040 Extract OS 00 'MS-DOS' - * 0041 General Purpose Flag 0000 - * 0043 Compression Method 0000 'Stored' - * 0045 Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' - * 0049 CRC 363A3020 - * 004D Compressed Length 00000006 - * 0051 Uncompressed Length FFFFFFFF - * 0055 Filename Length 0001 - * 0057 Extra Length 000C - * 0059 Comment Length 0000 - * 005B Disk Start 0000 - * 005D Int File Attributes 0001 - * [Bit 0] 1 Text Data - * 005F Ext File Attributes 11B00000 - * 0063 Local Header Offset 00000000 - * 0067 Filename '-' - * 0068 Extra ID #0001 0001 'ZIP64' - * 006A Length 0008 - * 006C Uncompressed Size 0000000000000006 - * - * 0074 ZIP64 END CENTRAL DIR 06064B50 - * RECORD - * 0078 Size of record 000000000000002C - * 0080 Created Zip Spec 1E '3.0' - * 0081 Created OS 03 'Unix' - * 0082 Extract Zip Spec 2D '4.5' - * 0083 Extract OS 00 'MS-DOS' - * 0084 Number of this disk 00000000 - * 0088 Central Dir Disk no 00000000 - * 008C Entries in this disk 0000000000000001 - * 0094 Total Entries 0000000000000001 - * 009C Size of Central Dir 000000000000003B - * 00A4 Offset to Central dir 0000000000000039 - * - * 00AC ZIP64 END CENTRAL DIR 07064B50 - * LOCATOR - * 00B0 Central Dir Disk no 00000000 - * 00B4 Offset to Central dir 0000000000000074 - * 00BC Total no of Disks 00000001 - * - * 00C0 END CENTRAL HEADER 06054B50 - * 00C4 Number of this disk 0000 - * 00C6 Central Dir Disk no 0000 - * 00C8 Entries in this disk 0001 - * 00CA Total Entries 0001 - * 00CC Size of Central Dir 0000003B - * 00D0 Offset to Central Dir FFFFFFFF - * 00D4 Comment Length 0000 - */ + Path zip = zip64WithModifiedTotalEntries(BAD_ENTRY_COUNT_ZIP, totalEntries); + verifyRejection(zip, INVALID_BAD_ENTRY_COUNT); + } - byte[] zipBytes = HexFormat.of().parseHex(""" - 504b03042d000000000078ab475920303a36ffffffffffffffff01001400 - 2d010010000600000000000000060000000000000068656c6c6f0a504b01 - 021e032d000000000078ab475920303a3606000000ffffffff01000c0000 - 00000001000000b011000000002d010008000600000000000000504b0606 - 2c000000000000001e032d00000000000000000001000000000000000100 - 0000000000003b000000000000003900000000000000504b060700000000 - 740000000000000001000000504b050600000000010001003b000000ffff - ffff0000 - """.replaceAll("\n","")); - - // Buffer to manipulate the above ZIP - ByteBuffer buf = ByteBuffer.wrap(zipBytes).order(ByteOrder.LITTLE_ENDIAN); - // Offset of the 'total entries' in the 'ZIP64 END CENTRAL DIR' record - // Update ZIP64 entry count to a value which cannot possibly fit in the small CEN - buf.putLong(0x94, totalEntries); - // The corresponding END field needs the ZIP64 magic value - buf.putShort(0xCA, (short) 0xFFFF); - // Write the ZIP to disk - Path zipFile = Path.of("bad-entry-count.zip"); - Files.write(zipFile, zipBytes); - - // Verify that the END header is rejected + /** + * Verify that ZipFile rejects the ZIP file with a ZipException + * with the given message + * @param zip ZIP file to open + * @param msg exception message to expect + */ + private static void verifyRejection(Path zip, String msg) { ZipException ex = assertThrows(ZipException.class, () -> { - try (var zf = new ZipFile(zipFile.toFile())) { - } + new ZipFile(zip.toFile()); }); - - assertEquals(INVALID_BAD_ENTRY_COUNT, ex.getMessage()); - } - - /** - * Create an ZIP file with a single entry, then modify the CEN size - * in the 'End of central directory record' (END header) to the given size. - * - * The CEN is optionally "inflated" with trailing zero bytes such that - * its actual size matches the one stated in the END header. - * - * The CEN offset is optiontially adjusted by the given amount - * - * The resulting ZIP is technically not valid, but it does allow us - * to test that large or invalid CEN sizes are rejected - * @param cenSize the CEN size to put in the END record - * @param inflateCen if true, zero-pad the CEN to the desired size - * @param cenOffAdjust Adjust the CEN offset field of the END record with this amount - * @throws IOException if an error occurs - */ - private Path zipWithModifiedEndRecord(int cenSize, - boolean inflateCen, - int cenOffAdjust, - Path zip) throws IOException { - - // A byte buffer for reading the END - ByteBuffer buffer = ByteBuffer.wrap(zipBytes.clone()).order(ByteOrder.LITTLE_ENDIAN); - - // Offset of the END header - int endOffset = buffer.limit() - ENDHDR; - - // Modify the CEN size - int sizeOffset = endOffset + ENDSIZ; - int currentCenSize = buffer.getInt(sizeOffset); - buffer.putInt(sizeOffset, cenSize); - - // Optionally modify the CEN offset - if (cenOffAdjust != 0) { - int offOffset = endOffset + ENDOFF; - int currentCenOff = buffer.getInt(offOffset); - buffer.putInt(offOffset, currentCenOff + cenOffAdjust); - } - - // When creating a sparse file, the file must not already exit - Files.deleteIfExists(zip); - - // Open a FileChannel for writing a sparse file - EnumSet options = EnumSet.of(StandardOpenOption.CREATE_NEW, - StandardOpenOption.WRITE, - StandardOpenOption.SPARSE); - - try (FileChannel channel = FileChannel.open(zip, options)) { - - // Write everything up to END - channel.write(buffer.slice(0, buffer.limit() - ENDHDR)); - - if (inflateCen) { - // Inject "empty bytes" to make the actual CEN size match the END - int injectBytes = cenSize - currentCenSize; - channel.position(channel.position() + injectBytes); - } - // Write the modified END - channel.write(buffer.slice(buffer.limit() - ENDHDR, ENDHDR)); - } - return zip; - } - - /** - * Produce a byte array of a ZIP with a single entry - * - * @throws IOException if an error occurs - */ - private byte[] templateZip() throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - try (ZipOutputStream zo = new ZipOutputStream(bout)) { - ZipEntry entry = new ZipEntry("duke.txt"); - zo.putNextEntry(entry); - zo.write("duke".getBytes(StandardCharsets.UTF_8)); - } - return bout.toByteArray(); + assertEquals(msg, ex.getMessage()); } } diff --git a/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java new file mode 100644 index 00000000000..ed0bdbb52ea --- /dev/null +++ b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java @@ -0,0 +1,150 @@ +/* + * 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 + * 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 + * @modules java.base/jdk.internal.util + * @summary Verify that ZipFileSystem rejects files with CEN sizes exceeding the implementation limit + * @library /test/lib + * @build jdk.test.lib.util.ZipUtils + * @run junit/othervm EndOfCenValidation + */ + +import jdk.internal.util.ArraysSupport; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.zip.ZipException; + +import static jdk.test.lib.util.ZipUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * This test augments {@link TestTooManyEntries}. It creates sparse ZIPs where + * the CEN size is inflated to the desired value. This helps this test run + * fast with much less resources. + * + * While the CEN in these files are zero-filled and the produced ZIPs are technically + * invalid, the CEN is never actually read by ZipFileSystem since it does + * 'End of central directory record' (END header) validation before reading the CEN. + */ +public class EndOfCenValidation { + + // Zip files produced by this test + static final Path CEN_TOO_LARGE_ZIP = Path.of("cen-size-too-large.zip"); + static final Path INVALID_CEN_SIZE = Path.of("invalid-zen-size.zip"); + static final Path BAD_CEN_OFFSET_ZIP = Path.of("bad-cen-offset.zip"); + static final Path BAD_ENTRY_COUNT_ZIP = Path.of("bad-entry-count.zip"); + + // Maximum allowed CEN size allowed by ZipFileSystem + static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; + + /** + * Delete big files after test, in case the file system did not support sparse files. + * @throws IOException if an error occurs + */ + @AfterEach + public void cleanup() throws IOException { + Files.deleteIfExists(CEN_TOO_LARGE_ZIP); + Files.deleteIfExists(INVALID_CEN_SIZE); + Files.deleteIfExists(BAD_CEN_OFFSET_ZIP); + Files.deleteIfExists(BAD_ENTRY_COUNT_ZIP); + } + + /** + * Validates that an 'End of central directory record' (END header) with a CEN + * length exceeding {@link #MAX_CEN_SIZE} limit is rejected + * @throws IOException if an error occurs + */ + @Test + public void shouldRejectTooLargeCenSize() throws IOException { + int size = MAX_CEN_SIZE + 1; + Path zip = zipWithModifiedEndRecord(size, true, 0, CEN_TOO_LARGE_ZIP); + verifyRejection(zip, INVALID_CEN_SIZE_TOO_LARGE); + } + + /** + * Validate that an 'End of central directory record' (END header) + * where the value of the CEN size field exceeds the position of + * the END header is rejected. + * @throws IOException if an error occurs + */ + @Test + public void shouldRejectInvalidCenSize() throws IOException { + int size = MAX_CEN_SIZE; + Path zip = zipWithModifiedEndRecord(size, false, 0, INVALID_CEN_SIZE); + verifyRejection(zip, INVALID_CEN_BAD_SIZE); + } + + /** + * Validate that an 'End of central directory record' (the END header) + * where the value of the CEN offset field is larger than the position + * of the END header minus the CEN size is rejected + * @throws IOException if an error occurs + */ + @Test + public void shouldRejectInvalidCenOffset() throws IOException { + int size = MAX_CEN_SIZE; + Path zip = zipWithModifiedEndRecord(size, true, 100, BAD_CEN_OFFSET_ZIP); + verifyRejection(zip, INVALID_CEN_BAD_OFFSET); + } + + /** + * Validate that a 'Zip64 End of Central Directory' record (the END header) + * where the value of the 'total entries' field is larger than what fits + * in the CEN size is rejected. + * + * @throws IOException if an error occurs + */ + @ParameterizedTest + @ValueSource(longs = { + -1, // Negative + Long.MIN_VALUE, // Very negative + 0x3B / 3L - 1, // Cannot fit in test ZIP's CEN + MAX_CEN_SIZE / 3 + 1, // Too large to allocate int[] entries array + Long.MAX_VALUE // Unreasonably large + }) + public void shouldRejectBadTotalEntries(long totalEntries) throws IOException { + Path zip = zip64WithModifiedTotalEntries(BAD_ENTRY_COUNT_ZIP, totalEntries); + verifyRejection(zip, INVALID_BAD_ENTRY_COUNT); + } + + /** + * Verify that ZipFileSystem.newFileSystem rejects the ZIP file with a ZipException + * with the given message + * @param zip ZIP file to open + * @param msg exception message to expect + */ + private static void verifyRejection(Path zip, String msg) { + ZipException ex = assertThrows(ZipException.class, () -> { + FileSystems.newFileSystem(zip); + }); + assertEquals(msg, ex.getMessage()); + } +} diff --git a/test/lib/jdk/test/lib/util/ZipUtils.java b/test/lib/jdk/test/lib/util/ZipUtils.java new file mode 100644 index 00000000000..a1568e51cc1 --- /dev/null +++ b/test/lib/jdk/test/lib/util/ZipUtils.java @@ -0,0 +1,247 @@ +/* + * 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 + * 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 jdk.test.lib.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.EnumSet; +import java.util.HexFormat; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +/** + * This class consists exclusively of static utility methods that are useful + * for creating and manipulating ZIP files. + */ +public final class ZipUtils { + // Some ZipFile constants for manipulating the 'End of central directory record' (END header) + private static final int ENDHDR = ZipFile.ENDHDR; // End of central directory record size + private static final int ENDSIZ = ZipFile.ENDSIZ; // Offset of CEN size field within ENDHDR + private static final int ENDOFF = ZipFile.ENDOFF; // Offset of CEN offset field within ENDHDR + // Expected message when CEN size does not match file size + public static final String INVALID_CEN_BAD_SIZE = "invalid END header (bad central directory size)"; + // Expected message when CEN offset is too large + public static final String INVALID_CEN_BAD_OFFSET = "invalid END header (bad central directory offset)"; + // Expected message when CEN size is too large + public static final String INVALID_CEN_SIZE_TOO_LARGE = "invalid END header (central directory size too large)"; + // Expected message when total entry count is too large + public static final String INVALID_BAD_ENTRY_COUNT = "invalid END header (total entries count too large)"; + + private ZipUtils() { } + + /** + * Create an ZIP file with a single entry, then modify the CEN size + * in the 'End of central directory record' (END header) to the given size. + * + * The CEN is optionally "inflated" with trailing zero bytes such that + * its actual size matches the one stated in the END header. + * + * The CEN offset is optiontially adjusted by the given amount + * + * The resulting ZIP is technically not valid, but it does allow us + * to test that large or invalid CEN sizes are rejected + * @param cenSize the CEN size to put in the END record + * @param inflateCen if true, zero-pad the CEN to the desired size + * @param cenOffAdjust Adjust the CEN offset field of the END record with this amount + * @throws IOException if an error occurs + */ + public static Path zipWithModifiedEndRecord(int cenSize, + boolean inflateCen, + int cenOffAdjust, + Path zip) throws IOException { + // A byte buffer for reading the END + ByteBuffer buffer = ByteBuffer.wrap(templateZip()).order(ByteOrder.LITTLE_ENDIAN); + + // Offset of the END header + int endOffset = buffer.limit() - ENDHDR; + + // Modify the CEN size + int sizeOffset = endOffset + ENDSIZ; + int currentCenSize = buffer.getInt(sizeOffset); + buffer.putInt(sizeOffset, cenSize); + + // Optionally modify the CEN offset + if (cenOffAdjust != 0) { + int offOffset = endOffset + ENDOFF; + int currentCenOff = buffer.getInt(offOffset); + buffer.putInt(offOffset, currentCenOff + cenOffAdjust); + } + // When creating a sparse file, the file must not already exit + Files.deleteIfExists(zip); + + // Open a FileChannel for writing a sparse file + EnumSet options = EnumSet.of(StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE, + StandardOpenOption.SPARSE); + + try (FileChannel channel = FileChannel.open(zip, options)) { + // Write everything up to END + channel.write(buffer.slice(0, buffer.limit() - ENDHDR)); + if (inflateCen) { + // Inject "empty bytes" to make the actual CEN size match the END + int injectBytes = cenSize - currentCenSize; + channel.position(channel.position() + injectBytes); + } + // Write the modified END + channel.write(buffer.slice(buffer.limit() - ENDHDR, ENDHDR)); + } + return zip; + } + + /** + * Create a small Zip64 ZIP file, then modify the Zip64 END header + * with a possibly very large total entry count + * + * @param zip file to write to + * @param totalEntries the number of entries wanted in the Zip64 END header + * @return the modified ZIP file + * @throws IOException if an unexpeced IO error occurs + */ + public static Path zip64WithModifiedTotalEntries(Path zip, long totalEntries) throws IOException { + /** + * A small ZIP using the ZIP64 format. + * + * ZIP created using: "echo -n hello | zip zip64.zip -" + * Hex encoded using: "cat zip64.zip | xxd -ps" + * + * The file has the following structure: + * + * 0000 LOCAL HEADER #1 04034B50 + * 0004 Extract Zip Spec 2D '4.5' + * 0005 Extract OS 00 'MS-DOS' + * 0006 General Purpose Flag 0000 + * 0008 Compression Method 0000 'Stored' + * 000A Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' + * 000E CRC 363A3020 + * 0012 Compressed Length FFFFFFFF + * 0016 Uncompressed Length FFFFFFFF + * 001A Filename Length 0001 + * 001C Extra Length 0014 + * 001E Filename '-' + * 001F Extra ID #0001 0001 'ZIP64' + * 0021 Length 0010 + * 0023 Uncompressed Size 0000000000000006 + * 002B Compressed Size 0000000000000006 + * 0033 PAYLOAD hello. + * + * 0039 CENTRAL HEADER #1 02014B50 + * 003D Created Zip Spec 1E '3.0' + * 003E Created OS 03 'Unix' + * 003F Extract Zip Spec 2D '4.5' + * 0040 Extract OS 00 'MS-DOS' + * 0041 General Purpose Flag 0000 + * 0043 Compression Method 0000 'Stored' + * 0045 Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' + * 0049 CRC 363A3020 + * 004D Compressed Length 00000006 + * 0051 Uncompressed Length FFFFFFFF + * 0055 Filename Length 0001 + * 0057 Extra Length 000C + * 0059 Comment Length 0000 + * 005B Disk Start 0000 + * 005D Int File Attributes 0001 + * [Bit 0] 1 Text Data + * 005F Ext File Attributes 11B00000 + * 0063 Local Header Offset 00000000 + * 0067 Filename '-' + * 0068 Extra ID #0001 0001 'ZIP64' + * 006A Length 0008 + * 006C Uncompressed Size 0000000000000006 + * + * 0074 ZIP64 END CENTRAL DIR 06064B50 + * RECORD + * 0078 Size of record 000000000000002C + * 0080 Created Zip Spec 1E '3.0' + * 0081 Created OS 03 'Unix' + * 0082 Extract Zip Spec 2D '4.5' + * 0083 Extract OS 00 'MS-DOS' + * 0084 Number of this disk 00000000 + * 0088 Central Dir Disk no 00000000 + * 008C Entries in this disk 0000000000000001 + * 0094 Total Entries 0000000000000001 + * 009C Size of Central Dir 000000000000003B + * 00A4 Offset to Central dir 0000000000000039 + * + * 00AC ZIP64 END CENTRAL DIR 07064B50 + * LOCATOR + * 00B0 Central Dir Disk no 00000000 + * 00B4 Offset to Central dir 0000000000000074 + * 00BC Total no of Disks 00000001 + * + * 00C0 END CENTRAL HEADER 06054B50 + * 00C4 Number of this disk 0000 + * 00C6 Central Dir Disk no 0000 + * 00C8 Entries in this disk 0001 + * 00CA Total Entries 0001 + * 00CC Size of Central Dir 0000003B + * 00D0 Offset to Central Dir FFFFFFFF + * 00D4 Comment Length 0000 + */ + + byte[] zipBytes = HexFormat.of().parseHex(""" + 504b03042d000000000078ab475920303a36ffffffffffffffff01001400 + 2d010010000600000000000000060000000000000068656c6c6f0a504b01 + 021e032d000000000078ab475920303a3606000000ffffffff01000c0000 + 00000001000000b011000000002d010008000600000000000000504b0606 + 2c000000000000001e032d00000000000000000001000000000000000100 + 0000000000003b000000000000003900000000000000504b060700000000 + 740000000000000001000000504b050600000000010001003b000000ffff + ffff0000 + """.replaceAll("\n","")); + + // Buffer to manipulate the above ZIP + ByteBuffer buf = ByteBuffer.wrap(zipBytes).order(ByteOrder.LITTLE_ENDIAN); + // Offset of the 'total entries' in the 'ZIP64 END CENTRAL DIR' record + // Update ZIP64 entry count to a value which cannot possibly fit in the small CEN + buf.putLong(0x94, totalEntries); + // The corresponding END field needs the ZIP64 magic value + buf.putShort(0xCA, (short) 0xFFFF); + // Write the ZIP to disk + Files.write(zip, zipBytes); + return zip; + } + + /** + * Produce a byte array of a ZIP with a single entry + * + * @throws IOException if an error occurs + */ + private static byte[] templateZip() throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + try (ZipOutputStream zo = new ZipOutputStream(bout)) { + ZipEntry entry = new ZipEntry("duke.txt"); + zo.putNextEntry(entry); + zo.write("duke".getBytes(StandardCharsets.UTF_8)); + } + return bout.toByteArray(); + } +} From f3d7ca33d797c3475a4c31d612e7ed8f71d1f5b0 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sat, 28 Feb 2026 22:58:10 +0000 Subject: [PATCH 098/636] 8378607: GlyphLayout cache can prevent Fonts from being GC'd Reviewed-by: jdv, tr --- .../share/classes/sun/font/GlyphLayout.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/java.desktop/share/classes/sun/font/GlyphLayout.java b/src/java.desktop/share/classes/sun/font/GlyphLayout.java index df42cce344e..5bff127f143 100644 --- a/src/java.desktop/share/classes/sun/font/GlyphLayout.java +++ b/src/java.desktop/share/classes/sun/font/GlyphLayout.java @@ -76,7 +76,7 @@ import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.util.ArrayList; -import java.util.concurrent.ConcurrentHashMap; +import java.util.WeakHashMap; import static java.lang.Character.*; @@ -125,18 +125,12 @@ public final class GlyphLayout { } private static final class SDCache { - public Font key_font; - public FontRenderContext key_frc; - public AffineTransform dtx; public AffineTransform gtx; public Point2D.Float delta; public FontStrikeDesc sd; private SDCache(Font font, FontRenderContext frc) { - key_font = font; - key_frc = frc; - // !!! add getVectorTransform and hasVectorTransform to frc? then // we could just skip this work... @@ -177,7 +171,7 @@ public final class GlyphLayout { private static final Point2D.Float ZERO_DELTA = new Point2D.Float(); private static - SoftReference> cacheRef; + SoftReference> cacheRef; private static final class SDKey { private final Font font; @@ -232,7 +226,7 @@ public final class GlyphLayout { } SDKey key = new SDKey(font, frc); // garbage, yuck... - ConcurrentHashMap cache = null; + WeakHashMap cache = null; SDCache res = null; if (cacheRef != null) { cache = cacheRef.get(); @@ -243,13 +237,17 @@ public final class GlyphLayout { if (res == null) { res = new SDCache(font, frc); if (cache == null) { - cache = new ConcurrentHashMap(10); + cache = new WeakHashMap(10); cacheRef = new - SoftReference>(cache); - } else if (cache.size() >= 512) { - cache.clear(); + SoftReference>(cache); + } else if (cache.size() >= 128) { + synchronized (SDCache.class) { + cache.clear(); + } + } + synchronized (SDCache.class) { + cache.put(key, res); } - cache.put(key, res); } return res; } From dbbb3b1ac5fd218ed2b3c7247f6602a7a58ca4de Mon Sep 17 00:00:00 2001 From: Raffaello Giulietti Date: Sun, 1 Mar 2026 12:05:58 +0000 Subject: [PATCH 099/636] 8378833: Improve offset arithmetic in ArraysSupport::mismatch Reviewed-by: liach, alanb, rriggs --- .../jdk/internal/util/ArraysSupport.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java index de7a5e44b91..c220455e80b 100644 --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -599,8 +599,8 @@ public class ArraysSupport { if (length > 3) { if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_CHAR_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_CHAR_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -648,8 +648,8 @@ public class ArraysSupport { if (length > 3) { if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_SHORT_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_SHORT_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -697,8 +697,8 @@ public class ArraysSupport { if (length > 1) { if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_INT_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_INT_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_INT_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -729,8 +729,8 @@ public class ArraysSupport { int i = 0; if (length > 1) { if (Float.floatToRawIntBits(a[aFromIndex]) == Float.floatToRawIntBits(b[bFromIndex])) { - long aOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_FLOAT_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_FLOAT_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -787,8 +787,8 @@ public class ArraysSupport { } if (a[aFromIndex] != b[bFromIndex]) return 0; - long aOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_LONG_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_LONG_INDEX_SCALE); int i = vectorizedMismatch( a, aOffset, b, bOffset, @@ -813,8 +813,8 @@ public class ArraysSupport { } int i = 0; if (Double.doubleToRawLongBits(a[aFromIndex]) == Double.doubleToRawLongBits(b[bFromIndex])) { - long aOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (aFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); - long bOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + (bFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); + long aOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + ((long) aFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); + long bOffset = Unsafe.ARRAY_DOUBLE_BASE_OFFSET + ((long) bFromIndex << LOG2_ARRAY_DOUBLE_INDEX_SCALE); i = vectorizedMismatch( a, aOffset, b, bOffset, From 0540e980ef556140c07c4416ce397ff002422842 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Sun, 1 Mar 2026 12:39:44 +0000 Subject: [PATCH 100/636] 8378642: Add src/utils/LogCompilation/logc.jar to .gitignore Reviewed-by: erikj --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0743489f8ec..b6b4a1a559a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ NashornProfile.txt **/JTreport/** **/JTwork/** /src/utils/LogCompilation/target/ +/src/utils/LogCompilation/logc.jar /.project/ /.settings/ /compile_commands.json From 9cf9fbec1f5475c39d8522c4bb88dae0881a0b7d Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 1 Mar 2026 17:35:56 +0000 Subject: [PATCH 101/636] 8378389: Remove AppContext from the Swing RepaintManager Reviewed-by: azvegint, serb --- .../share/classes/javax/swing/JComponent.java | 5 +- .../classes/javax/swing/RepaintManager.java | 59 ++++++++----------- .../swing/SwingPaintEventDispatcher.java | 19 ++---- .../share/classes/sun/awt/SunToolkit.java | 3 +- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/JComponent.java b/src/java.desktop/share/classes/javax/swing/JComponent.java index f515115bade..6351522ab49 100644 --- a/src/java.desktop/share/classes/javax/swing/JComponent.java +++ b/src/java.desktop/share/classes/javax/swing/JComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -4885,8 +4885,7 @@ public abstract class JComponent extends Container implements Serializable, * @see RepaintManager#addDirtyRegion */ public void repaint(long tm, int x, int y, int width, int height) { - RepaintManager.currentManager(SunToolkit.targetToAppContext(this)) - .addDirtyRegion(this, x, y, width, height); + RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height); } diff --git a/src/java.desktop/share/classes/javax/swing/RepaintManager.java b/src/java.desktop/share/classes/javax/swing/RepaintManager.java index dcc755cb4bc..db8b13afa22 100644 --- a/src/java.desktop/share/classes/javax/swing/RepaintManager.java +++ b/src/java.desktop/share/classes/javax/swing/RepaintManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -119,8 +119,6 @@ public class RepaintManager */ private PaintManager paintManager; - private static final Object repaintManagerKey = RepaintManager.class; - // Whether or not a VolatileImage should be used for double-buffered painting static boolean volatileImageBufferEnabled = true; /** @@ -236,8 +234,10 @@ public class RepaintManager } } + private static RepaintManager repaintManager; + /** - * Return the RepaintManager for the calling thread given a Component. + * Return the RepaintManager given a Component. * * @param c a Component -- unused in the default implementation, but could * be used by an overridden version to return a different RepaintManager @@ -249,21 +249,15 @@ public class RepaintManager // component is ever used to determine the current // RepaintManager, DisplayChangedRunnable will need to be modified // accordingly. - return currentManager(AppContext.getAppContext()); - } - /** - * Returns the RepaintManager for the specified AppContext. If - * a RepaintManager has not been created for the specified - * AppContext this will return null. - */ - static RepaintManager currentManager(AppContext appContext) { - RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey); - if (rm == null) { - rm = new RepaintManager(BUFFER_STRATEGY_TYPE); - appContext.put(repaintManagerKey, rm); + synchronized (RepaintManager.class) { + RepaintManager rm = repaintManager; + if (rm == null) { + rm = new RepaintManager(BUFFER_STRATEGY_TYPE); + repaintManager = rm; + } + return rm; } - return rm; } /** @@ -282,16 +276,12 @@ public class RepaintManager /** - * Set the RepaintManager that should be used for the calling - * thread. aRepaintManager will become the current RepaintManager - * for the calling thread's thread group. + * Set the RepaintManager. * @param aRepaintManager the RepaintManager object to use */ public static void setCurrentManager(RepaintManager aRepaintManager) { - if (aRepaintManager != null) { - SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager); - } else { - SwingUtilities.appContextRemove(repaintManagerKey); + synchronized (RepaintManager.class) { + repaintManager = aRepaintManager; } } @@ -373,7 +363,7 @@ public class RepaintManager // Queue a Runnable to invoke paintDirtyRegions and // validateInvalidComponents. - scheduleProcessingRunnable(SunToolkit.targetToAppContext(invalidComponent)); + scheduleProcessingRunnable(); } @@ -460,7 +450,7 @@ public class RepaintManager // Queue a Runnable to invoke paintDirtyRegions and // validateInvalidComponents. - scheduleProcessingRunnable(SunToolkit.targetToAppContext(c)); + scheduleProcessingRunnable(); } /** @@ -530,8 +520,7 @@ public class RepaintManager // This is called from the toolkit thread when a native expose is // received. // - void nativeAddDirtyRegion(AppContext appContext, Container c, - int x, int y, int w, int h) { + void nativeAddDirtyRegion(Container c, int x, int y, int w, int h) { if (w > 0 && h > 0) { synchronized(this) { Rectangle dirty = hwDirtyComponents.get(c); @@ -543,7 +532,7 @@ public class RepaintManager x, y, w, h, dirty)); } } - scheduleProcessingRunnable(appContext); + scheduleProcessingRunnable(); } } @@ -551,8 +540,7 @@ public class RepaintManager // This is called from the toolkit thread when awt needs to run a // Runnable before we paint. // - void nativeQueueSurfaceDataRunnable(AppContext appContext, - final Component c, final Runnable r) + void nativeQueueSurfaceDataRunnable(final Component c, final Runnable r) { synchronized(this) { if (runnableList == null) { @@ -560,7 +548,7 @@ public class RepaintManager } runnableList.add(r); } - scheduleProcessingRunnable(appContext); + scheduleProcessingRunnable(); } /** @@ -1411,11 +1399,11 @@ public class RepaintManager return paintManager; } - private void scheduleProcessingRunnable(AppContext context) { + private void scheduleProcessingRunnable() { if (processingRunnable.markPending()) { Toolkit tk = Toolkit.getDefaultToolkit(); if (tk instanceof SunToolkit) { - SunToolkit.getSystemEventQueueImplPP(context). + SunToolkit.getSystemEventQueueImplPP(). postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), processingRunnable)); } else { @@ -1733,8 +1721,7 @@ public class RepaintManager /** * Listener installed to detect display changes. When display changes, * schedules a callback to notify all RepaintManagers of the display - * changes. Only one DisplayChangedHandler is ever installed. The - * singleton instance will schedule notification for all AppContexts. + * changes. Only one DisplayChangedHandler is ever installed. */ private static final class DisplayChangedHandler implements DisplayChangedListener { diff --git a/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.java b/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.java index 4a7b0821a3d..8c904f7d00c 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.java +++ b/src/java.desktop/share/classes/javax/swing/SwingPaintEventDispatcher.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 @@ -28,7 +28,6 @@ import java.awt.Component; import java.awt.Container; import java.awt.Rectangle; import java.awt.event.PaintEvent; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.event.IgnorePaintEvent; @@ -52,12 +51,10 @@ class SwingPaintEventDispatcher extends sun.awt.PaintEventDispatcher { public PaintEvent createPaintEvent(Component component, int x, int y, int w, int h) { if (component instanceof RootPaneContainer) { - AppContext appContext = SunToolkit.targetToAppContext(component); - RepaintManager rm = RepaintManager.currentManager(appContext); + RepaintManager rm = RepaintManager.currentManager(component); if (!SHOW_FROM_DOUBLE_BUFFER || !rm.show((Container)component, x, y, w, h)) { - rm.nativeAddDirtyRegion(appContext, (Container)component, - x, y, w, h); + rm.nativeAddDirtyRegion((Container)component, x, y, w, h); } // For backward compatibility generate an empty paint // event. Not doing this broke parts of Netbeans. @@ -65,10 +62,8 @@ class SwingPaintEventDispatcher extends sun.awt.PaintEventDispatcher { new Rectangle(x, y, w, h)); } else if (component instanceof SwingHeavyWeight) { - AppContext appContext = SunToolkit.targetToAppContext(component); - RepaintManager rm = RepaintManager.currentManager(appContext); - rm.nativeAddDirtyRegion(appContext, (Container)component, - x, y, w, h); + RepaintManager rm = RepaintManager.currentManager(component); + rm.nativeAddDirtyRegion((Container)component, x, y, w, h); return new IgnorePaintEvent(component, PaintEvent.PAINT, new Rectangle(x, y, w, h)); } @@ -81,9 +76,7 @@ class SwingPaintEventDispatcher extends sun.awt.PaintEventDispatcher { public boolean queueSurfaceDataReplacing(Component c, Runnable r) { if (c instanceof RootPaneContainer) { - AppContext appContext = SunToolkit.targetToAppContext(c); - RepaintManager.currentManager(appContext). - nativeQueueSurfaceDataRunnable(appContext, c, r); + RepaintManager.currentManager(c).nativeQueueSurfaceDataRunnable(c, r); return true; } return super.queueSurfaceDataReplacing(c, r); diff --git a/src/java.desktop/share/classes/sun/awt/SunToolkit.java b/src/java.desktop/share/classes/sun/awt/SunToolkit.java index 10fb62e2fdc..e97b654486f 100644 --- a/src/java.desktop/share/classes/sun/awt/SunToolkit.java +++ b/src/java.desktop/share/classes/sun/awt/SunToolkit.java @@ -1034,8 +1034,7 @@ public abstract class SunToolkit extends Toolkit return getSystemEventQueueImplPP(); } - // Package private implementation - static EventQueue getSystemEventQueueImplPP() { + public static EventQueue getSystemEventQueueImplPP() { return getSystemEventQueueImplPP(AppContext.getAppContext()); } From d6246b35daa52af20671237f22e3dbbcf269301b Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 1 Mar 2026 17:49:33 +0000 Subject: [PATCH 102/636] 8378386: Remove AppContext from AWT ModalEventFilter.java Reviewed-by: serb, dnguyen --- .../classes/java/awt/ModalEventFilter.java | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/ModalEventFilter.java b/src/java.desktop/share/classes/java/awt/ModalEventFilter.java index 7941be89743..93956c34fc5 100644 --- a/src/java.desktop/share/classes/java/awt/ModalEventFilter.java +++ b/src/java.desktop/share/classes/java/awt/ModalEventFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 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 @@ -26,8 +26,6 @@ package java.awt; import java.awt.event.*; -import sun.awt.AppContext; - abstract class ModalEventFilter implements EventFilter { protected Dialog modalDialog; @@ -129,20 +127,14 @@ abstract class ModalEventFilter implements EventFilter { private static class ToolkitModalEventFilter extends ModalEventFilter { - private AppContext appContext; - ToolkitModalEventFilter(Dialog modalDialog) { super(modalDialog); - appContext = modalDialog.appContext; } protected FilterAction acceptWindow(Window w) { if (w.isModalExcluded(Dialog.ModalExclusionType.TOOLKIT_EXCLUDE)) { return FilterAction.ACCEPT; } - if (w.appContext != appContext) { - return FilterAction.REJECT; - } while (w != null) { if (w == modalDialog) { return FilterAction.ACCEPT_IMMEDIATELY; @@ -155,27 +147,21 @@ abstract class ModalEventFilter implements EventFilter { private static class ApplicationModalEventFilter extends ModalEventFilter { - private AppContext appContext; - ApplicationModalEventFilter(Dialog modalDialog) { super(modalDialog); - appContext = modalDialog.appContext; } protected FilterAction acceptWindow(Window w) { if (w.isModalExcluded(Dialog.ModalExclusionType.APPLICATION_EXCLUDE)) { return FilterAction.ACCEPT; } - if (w.appContext == appContext) { - while (w != null) { - if (w == modalDialog) { - return FilterAction.ACCEPT_IMMEDIATELY; - } - w = w.getOwner(); + while (w != null) { + if (w == modalDialog) { + return FilterAction.ACCEPT_IMMEDIATELY; } - return FilterAction.REJECT; + w = w.getOwner(); } - return FilterAction.ACCEPT; + return FilterAction.REJECT; } } From 2069edca68e9404f5dd3acef24055d19b0816200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20H=C3=BCbner?= Date: Mon, 2 Mar 2026 02:23:23 +0000 Subject: [PATCH 103/636] 8377454: TestZGCWithCDS.java should also test different combos of UseCompactObjectHeaders Reviewed-by: dholmes, stuefe --- .../runtime/cds/appcds/TestZGCWithCDS.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java index 7c0b5896a98..eb42bb9f93d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.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 @@ -22,7 +22,7 @@ */ /* - * @test + * @test id=COH * @bug 8232069 * @requires vm.cds * @requires vm.bits == 64 @@ -31,7 +31,32 @@ * @requires vm.gc == null * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @compile test-classes/Hello.java - * @run driver TestZGCWithCDS + * @comment Only run if COH is unset or enabled. + * @requires vm.opt.UseCompactObjectHeaders != "false" + * @comment Driver sets compressed oops/class pointers, jtreg overrides will cause problems. + Only run the test if the flags are not set via the command line. + * @requires vm.opt.UseCompressedOops == null + * @requires vm.opt.UseCompressedClassPointers == null + * @run driver TestZGCWithCDS true + */ + +/* + * @test id=NO-COH + * @bug 8232069 + * @requires vm.cds + * @requires vm.bits == 64 + * @requires vm.gc.Z + * @requires vm.gc.Serial + * @requires vm.gc == null + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile test-classes/Hello.java + * @comment Only run if COH is unset or disabled. + * @requires vm.opt.UseCompactObjectHeaders != "true" + * @comment Driver sets compressed oops/class pointers, jtreg overrides will cause problems. + Only run the test if the flags are not set via the command line. + * @requires vm.opt.UseCompressedOops == null + * @requires vm.opt.UseCompressedClassPointers == null + * @run driver TestZGCWithCDS false */ import jdk.test.lib.Platform; @@ -42,7 +67,8 @@ public class TestZGCWithCDS { public final static String UNABLE_TO_USE_ARCHIVE = "Unable to use shared archive."; public final static String ERR_MSG = "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled."; public static void main(String... args) throws Exception { - String compactHeaders = "-XX:+UseCompactObjectHeaders"; + boolean compactHeadersOn = Boolean.valueOf(args[0]); + String compactHeaders = "-XX:" + (compactHeadersOn ? "+" : "-") + "UseCompactObjectHeaders"; String helloJar = JarBuilder.build("hello", "Hello"); System.out.println("0. Dump with ZGC"); OutputAnalyzer out = TestCommon From ae4df28b38b318accc6265901ebe08dd3089a262 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 2 Mar 2026 08:30:29 +0000 Subject: [PATCH 104/636] 8378677: Inline clear into ContiguousSpace::initialize Reviewed-by: kbarrett --- src/hotspot/share/gc/shared/space.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 84ba21527fd..093fa4432e2 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -51,7 +51,7 @@ void ContiguousSpace::initialize(MemRegion mr, set_bottom(bottom); set_end(end); if (clear_space) { - clear(SpaceDecorator::DontMangle); + set_top(bottom); } if (ZapUnusedHeapArea) { mangle_unused_area(); From 8d370ce59836c2b84d15e72655aa9202feda0581 Mon Sep 17 00:00:00 2001 From: Daniel Gredler Date: Mon, 2 Mar 2026 10:04:27 +0000 Subject: [PATCH 105/636] 8377937: [macos] GlyphMetrics advance does not consider font rotation Reviewed-by: prr, serb, psadhukhan --- .../macosx/classes/sun/font/CStrike.java | 16 +++- .../GlyphMetricsRotatedFontTest.java | 80 +++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/awt/font/GlyphVector/GlyphMetricsRotatedFontTest.java diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index bffb76fb1ad..f7fa00070ff 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -174,7 +174,19 @@ public final class CStrike extends PhysicalStrike { @Override Point2D.Float getGlyphMetrics(final int glyphCode) { - return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f); + Point2D.Float metrics = new Point2D.Float(); + long glyphPtr = getGlyphImagePtr(glyphCode); + if (glyphPtr != 0L) { + metrics.x = StrikeCache.getGlyphXAdvance(glyphPtr); + metrics.y = StrikeCache.getGlyphYAdvance(glyphPtr); + /* advance is currently in device space, need to convert back + * into user space. + * This must not include the translation component. */ + if (invDevTx != null) { + invDevTx.deltaTransform(metrics, metrics); + } + } + return metrics; } @Override diff --git a/test/jdk/java/awt/font/GlyphVector/GlyphMetricsRotatedFontTest.java b/test/jdk/java/awt/font/GlyphVector/GlyphMetricsRotatedFontTest.java new file mode 100644 index 00000000000..eb00fd4f34d --- /dev/null +++ b/test/jdk/java/awt/font/GlyphVector/GlyphMetricsRotatedFontTest.java @@ -0,0 +1,80 @@ +/* + * 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 8148334 8377937 + * @summary Checks behavior of GlyphVector.getGlyphMetrics(int) with rotated fonts. + */ + +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; + +public class GlyphMetricsRotatedFontTest { + + public static void main(String[] args) { + + String text = "The quick brown \r\n fox JUMPS over \t the lazy dog."; + FontRenderContext frc = new FontRenderContext(null, true, true); + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + String[] names = ge.getAvailableFontFamilyNames(); + + for (String name : names) { + + Font normal = new Font(name, Font.PLAIN, 60); + if (normal.canDisplayUpTo("AZaz09") != -1) { + continue; + } + + double theta = 0.5; // about 30 degrees + AffineTransform tx = AffineTransform.getRotateInstance(theta); + Font rotated = normal.deriveFont(tx); + + GlyphVector gv1 = normal.createGlyphVector(frc, text); + GlyphVector gv2 = rotated.createGlyphVector(frc, text); + + for (int i = 0; i < gv1.getNumGlyphs(); i++) { + GlyphMetrics gm1 = gv1.getGlyphMetrics(i); + GlyphMetrics gm2 = gv2.getGlyphMetrics(i); + assertEqual(0, gm1.getAdvanceY(), 0, name + " normal y", i); + double expectedX = Math.cos(theta) * gm1.getAdvanceX(); + double expectedY = Math.sin(theta) * gm1.getAdvanceX(); + assertEqual(expectedX, gm2.getAdvanceX(), 1, name + " rotated x", i); + assertEqual(expectedY, gm2.getAdvanceY(), 1, name + " rotated y", i); + } + } + } + + private static void assertEqual(double d1, double d2, double variance, + String scenario, int index) { + if (Math.abs(d1 - d2) > variance) { + String msg = String.format("%s for index %d: %f != %f", scenario, index, d1, d2); + throw new RuntimeException(msg); + } + } +} From b12daa41e23eaac2777a8f89ef279963d0e6f7a0 Mon Sep 17 00:00:00 2001 From: Oli Gillespie Date: Mon, 2 Mar 2026 10:16:57 +0000 Subject: [PATCH 106/636] 8378110: Add -XX: prefix to settings-file flags in RuntimeMXBean.getInputArguments() Reviewed-by: kevinw, dholmes --- src/hotspot/share/prims/jvm.cpp | 4 +- .../RuntimeMXBean/InputArgument.java | 53 +++++++++++++------ 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 2b3a2966c27..423e1a5a1f4 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3641,7 +3641,9 @@ JVM_ENTRY(jobjectArray, JVM_GetVmArguments(JNIEnv *env)) int index = 0; for (int j = 0; j < num_flags; j++, index++) { - Handle h = java_lang_String::create_from_platform_dependent_str(vm_flags[j], CHECK_NULL); + stringStream prefixed; + prefixed.print("-XX:%s", vm_flags[j]); + Handle h = java_lang_String::create_from_platform_dependent_str(prefixed.base(), CHECK_NULL); result_h->obj_at_put(index, h()); } for (int i = 0; i < num_args; i++, index++) { diff --git a/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java b/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java index 66c3d3f41f5..0e7609b86ed 100644 --- a/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java +++ b/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -62,41 +62,60 @@ * @run main/othervm -Dprops=something InputArgument -Dprops=something */ +/* + * @test + * @bug 8378110 + * @summary RuntimeMXBean.getInputArguments() handling of flags from settings file. + * + * @run driver InputArgument generateFlagsFile + * @run main/othervm -XX:+UseFastJNIAccessors -XX:Flags=InputArgument.flags InputArgument + * -XX:+UseFastJNIAccessors -XX:-UseG1GC -XX:+UseParallelGC -XX:MaxHeapSize=100M + */ + import java.lang.management.*; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; import java.util.ListIterator; public class InputArgument { private static RuntimeMXBean rm = ManagementFactory.getRuntimeMXBean(); - private static String vmOption = null; public static void main(String args[]) throws Exception { - // set the expected input argument - if (args.length > 0) { - vmOption = args[0]; - } - - List options = rm.getInputArguments(); - if (vmOption == null) { + if (args.length == 1 && "generateFlagsFile".equals(args[0])) { + generateFlagsFile(); return; } - boolean testFailed = true; + String[] vmOptions = args; + List options = rm.getInputArguments(); System.out.println("Number of arguments = " + options.size()); int i = 0; for (String arg : options) { System.out.println("Input arg " + i + " = " + arg); i++; - if (arg.equals(vmOption)) { - testFailed = false; - break; - } } - if (testFailed) { - throw new RuntimeException("TEST FAILED: " + - "VM option " + vmOption + " not found"); + + for (String expected : vmOptions) { + boolean found = false; + for (String arg : options) { + if (arg.equals(expected)) { + found = true; + break; + } + } + if (!found) { + throw new RuntimeException("TEST FAILED: " + + "VM option " + expected + " not found"); + } } System.out.println("Test passed."); } + + private static void generateFlagsFile() throws Exception { + // 3 types of flag; both boolean cases and 1 numeric + Files.writeString(Paths.get("", "InputArgument.flags"), + String.format("-UseG1GC%n+UseParallelGC%nMaxHeapSize=100M%n")); + } } From b7d0cb5fb36965874f0950ab882dc517b002509f Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Mon, 2 Mar 2026 12:49:01 +0000 Subject: [PATCH 107/636] 8378888: jdk/incubator/vector/Float16OperationsBenchmark.java uses wrong package name Reviewed-by: jiefu, jbhateja, syan, liach --- .../bench/jdk/incubator/vector/Float16OperationsBenchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java index ebbfbb01cc6..cbfe9958924 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.openjdk.bench.java.lang; +package org.openjdk.bench.jdk.incubator.vector; import java.util.stream.IntStream; import java.util.concurrent.TimeUnit; From 29a40c3c6821da3d40c3aa45a0650e8d0ad5255d Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Mon, 2 Mar 2026 13:39:44 +0000 Subject: [PATCH 108/636] 8378793: Add ResolvedFieldEntry is_valid assert Reviewed-by: dholmes, dsimms, matsaave --- .../share/interpreter/interpreterRuntime.cpp | 12 +++---- src/hotspot/share/oops/resolvedFieldEntry.cpp | 34 ++++++++++++++++++- src/hotspot/share/oops/resolvedFieldEntry.hpp | 24 ++++++------- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index ca7174389cf..e3cf5d589c2 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -711,9 +711,7 @@ void InterpreterRuntime::resolve_get_put(Bytecodes::Code bytecode, int field_ind } ResolvedFieldEntry* entry = pool->resolved_field_entry_at(field_index); - entry->set_flags(info.access_flags().is_final(), info.access_flags().is_volatile()); - entry->fill_in(info.field_holder(), info.offset(), - checked_cast(info.index()), checked_cast(state), + entry->fill_in(info, checked_cast(state), static_cast(get_code), static_cast(put_code)); } @@ -1189,10 +1187,9 @@ JRT_LEAF(void, InterpreterRuntime::at_unwind(JavaThread* current)) JRT_END JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread* current, oopDesc* obj, - ResolvedFieldEntry *entry)) + ResolvedFieldEntry* entry)) // check the access_flags for the field in the klass - InstanceKlass* ik = entry->field_holder(); int index = entry->field_index(); if (!ik->field_status(index).is_access_watched()) return; @@ -1212,11 +1209,10 @@ JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread* current, oopDe JRT_END JRT_ENTRY(void, InterpreterRuntime::post_field_modification(JavaThread* current, oopDesc* obj, - ResolvedFieldEntry *entry, jvalue *value)) - - InstanceKlass* ik = entry->field_holder(); + ResolvedFieldEntry* entry, jvalue* value)) // check the access_flags for the field in the klass + InstanceKlass* ik = entry->field_holder(); int index = entry->field_index(); // bail out if field modifications are not watched if (!ik->field_status(index).is_modification_watched()) return; diff --git a/src/hotspot/share/oops/resolvedFieldEntry.cpp b/src/hotspot/share/oops/resolvedFieldEntry.cpp index 83f1a6919a6..49e9115ca9a 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.cpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.cpp @@ -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 @@ -24,7 +24,12 @@ #include "cds/archiveBuilder.hpp" #include "cppstdlib/type_traits.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/instanceOop.hpp" #include "oops/resolvedFieldEntry.hpp" +#include "runtime/fieldDescriptor.inline.hpp" +#include "utilities/checkedCast.hpp" +#include "utilities/globalDefinitions.hpp" static_assert(std::is_trivially_copyable_v); @@ -34,6 +39,19 @@ class ResolvedFieldEntryWithExtra : public ResolvedFieldEntry { }; static_assert(sizeof(ResolvedFieldEntryWithExtra) > sizeof(ResolvedFieldEntry)); +void ResolvedFieldEntry::fill_in(const fieldDescriptor& info, u1 tos_state, u1 get_code, u1 put_code) { + set_flags(info.access_flags().is_final(), info.access_flags().is_volatile()); + _field_holder = info.field_holder(); + _field_offset = info.offset(); + _field_index = checked_cast(info.index()); + _tos_state = tos_state; + + // These must be set after the other fields + set_bytecode(&_get_code, get_code); + set_bytecode(&_put_code, put_code); + assert_is_valid(); +} + void ResolvedFieldEntry::print_on(outputStream* st) const { st->print_cr("Field Entry:"); @@ -52,6 +70,20 @@ void ResolvedFieldEntry::print_on(outputStream* st) const { st->print_cr(" - Put Bytecode: %s", Bytecodes::name((Bytecodes::Code)put_code())); } +#ifdef ASSERT +void ResolvedFieldEntry::assert_is_valid() const { + assert(field_holder()->is_instance_klass(), "should be instanceKlass"); + assert(field_offset() >= instanceOopDesc::base_offset_in_bytes(), + "field offset out of range %d >= %d", field_offset(), instanceOopDesc::base_offset_in_bytes()); + assert(as_BasicType((TosState)tos_state()) != T_ILLEGAL, "tos_state is ILLEGAL"); + assert(_flags < (1 << (max_flag_shift + 1)), "flags are too large %d", _flags); + assert((get_code() == 0 || get_code() == Bytecodes::_getstatic || get_code() == Bytecodes::_getfield), + "invalid get bytecode %d", get_code()); + assert((put_code() == 0 || put_code() == Bytecodes::_putstatic || put_code() == Bytecodes::_putfield), + "invalid put bytecode %d", put_code()); +} +#endif + #if INCLUDE_CDS void ResolvedFieldEntry::remove_unshareable_info() { *this = ResolvedFieldEntry(_cpool_index); diff --git a/src/hotspot/share/oops/resolvedFieldEntry.hpp b/src/hotspot/share/oops/resolvedFieldEntry.hpp index 77ad4815730..bdd9999dd63 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.hpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.hpp @@ -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 @@ -26,7 +26,6 @@ #define SHARE_OOPS_RESOLVEDFIELDENTRY_HPP #include "interpreter/bytecodes.hpp" -#include "oops/instanceKlass.hpp" #include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" #include "utilities/sizes.hpp" @@ -46,7 +45,8 @@ // The explicit paddings are necessary for generating deterministic CDS archives. They prevent // the C++ compiler from potentially inserting random values in unused gaps. -//class InstanceKlass; +class InstanceKlass; + class ResolvedFieldEntry { friend class VMStructs; @@ -84,6 +84,7 @@ public: enum { is_volatile_shift = 0, is_final_shift = 1, // unused + max_flag_shift = is_final_shift }; // Getters @@ -113,6 +114,7 @@ public: // Printing void print_on(outputStream* st) const; + private: void set_flags(bool is_final_flag, bool is_volatile_flag) { int new_flags = (is_final_flag << is_final_shift) | static_cast(is_volatile_flag); _flags = checked_cast(new_flags); @@ -129,17 +131,12 @@ public: AtomicAccess::release_store(code, new_code); } - // Populate the strucutre with resolution information - void fill_in(InstanceKlass* klass, int offset, u2 index, u1 tos_state, u1 b1, u1 b2) { - _field_holder = klass; - _field_offset = offset; - _field_index = index; - _tos_state = tos_state; + // Debug help + void assert_is_valid() const NOT_DEBUG_RETURN; - // These must be set after the other fields - set_bytecode(&_get_code, b1); - set_bytecode(&_put_code, b2); - } + public: + // Populate the strucutre with resolution information + void fill_in(const fieldDescriptor& info, u1 tos_state, u1 get_code, u1 put_code); // CDS #if INCLUDE_CDS @@ -155,7 +152,6 @@ public: static ByteSize put_code_offset() { return byte_offset_of(ResolvedFieldEntry, _put_code); } static ByteSize type_offset() { return byte_offset_of(ResolvedFieldEntry, _tos_state); } static ByteSize flags_offset() { return byte_offset_of(ResolvedFieldEntry, _flags); } - }; #endif //SHARE_OOPS_RESOLVEDFIELDENTRY_HPP From 373ad02d3a1fefcd3e1b3b90f594ab7a2cacfd9f Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Mon, 2 Mar 2026 14:12:33 +0000 Subject: [PATCH 109/636] 8378786: PeerConnectionId::cloneBuffer should use absolute bulk get Reviewed-by: vyazici, djelinski --- .../internal/net/http/quic/PeerConnectionId.java | 9 +++++++-- .../net/http/quic/QuicConnectionImpl.java | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnectionId.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnectionId.java index 0c6f946d1a2..b822480c2a5 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnectionId.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnectionId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, 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 @@ -72,8 +72,13 @@ public final class PeerConnectionId extends QuicConnectionId { } private static ByteBuffer cloneBuffer(ByteBuffer src) { + // we make a copy of the bytes and create a new + // ByteBuffer here because we do not want to retain + // the memory that was allocated for the original + // ByteBuffer, which could ba a slice of a larger + // buffer, such as the whole datagram payload. final byte[] idBytes = new byte[src.remaining()]; - src.get(idBytes); + src.get(src.position(), idBytes); return ByteBuffer.wrap(idBytes); } 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 edb94d5929a..41b814a551c 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 @@ -703,7 +703,9 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece return; } if (debug.on()) { - debug.log("scheduleForDecryption: %d bytes", received); + debug.log("scheduleForDecryption: %s bytes [idbytes: %s(%s,%s)]", + received, datagram.destConnId().getClass().getSimpleName(), + datagram.destConnId().position(), datagram.destConnId().limit()); } endpoint.buffer(received); incoming.add(datagram); @@ -1847,6 +1849,10 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece header.destinationId().toHexString(), Utils.asHexString(destConnId)); } + assert packetIndex > 1 : + "first long packet CID does not match itself %s(%s,%s)" + .formatted(destConnId.getClass().getSimpleName(), + destConnId.position(), destConnId.limit()); return; } var peekedVersion = header.version(); @@ -1918,6 +1924,10 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece " wrong connection id (%s vs %s)", packetIndex, Utils.asHexString(cid), Utils.asHexString(destConnId)); } + assert packetIndex > 1 : "first short packet CID does not match itself %s(%s,%s)" + .formatted(destConnId.getClass().getSimpleName(), + destConnId.position(), destConnId.limit()); + return; } @@ -1934,6 +1944,9 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece if (debug.on()) { debug.log("Failed to process incoming packet", t); } + if (t instanceof AssertionError) { + this.terminator.terminate(TerminationCause.forException(t)); + } } } From 2c3e4f08fa3f15fa37b59dff89b6039ac1051a6d Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Mon, 2 Mar 2026 14:13:19 +0000 Subject: [PATCH 110/636] 8378595: Refactor miscellaneous tests under test/jdk/java/net/httpclient from TestNG to JUnit Reviewed-by: syan, vyazici --- .../BufferingSubscriberCancelTest.java | 2 +- .../BodyHandlerOfFileDownloadTest.java | 74 ++++++++++--------- .../PathSubscriber/BodyHandlerOfFileTest.java | 70 +++++++++--------- .../BodySubscriberOfFileTest.java | 73 +++++++++--------- .../httpclient/offline/OfflineTesting.java | 34 +++++---- .../FileProcessorPermissionTest.java | 8 +- .../filePerms/SecurityBeforeFile.java | 18 +++-- 7 files changed, 148 insertions(+), 131 deletions(-) diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java index 7813b8c3d7b..4d73ec31280 100644 --- a/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java +++ b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java @@ -214,6 +214,6 @@ public class BufferingSubscriberCancelTest { return; Thread.sleep(100); } - assertEquals(expected, actual); // will fail with the usual testng message + assertEquals(expected, actual); // will fail with the usual junit message } } diff --git a/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileDownloadTest.java b/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileDownloadTest.java index eb397d37b7a..92d359e32c6 100644 --- a/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileDownloadTest.java +++ b/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileDownloadTest.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 @@ -37,15 +37,11 @@ * jdk.test.lib.net.SimpleSSLContext * jdk.test.lib.Platform * jdk.test.lib.util.FileUtils - * @run testng/othervm BodyHandlerOfFileDownloadTest + * @run junit/othervm BodyHandlerOfFileDownloadTest */ import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.util.FileUtils; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -73,28 +69,35 @@ import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BodyHandlerOfFileDownloadTest implements HttpServerAdapters { static final String MSG = "msg"; static final String contentDispositionValue = "attachment; filename=example.html"; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; - FileSystem zipFs; - Path defaultFsPath; - Path zipFsPath; + private static FileSystem zipFs; + private static Path defaultFsPath; + private static Path zipFsPath; // Default file system @@ -106,12 +109,10 @@ public class BodyHandlerOfFileDownloadTest implements HttpServerAdapters { return dir; } - @DataProvider(name = "defaultFsData") - public Object[][] defaultFsData() { + public static Object[][] defaultFsData() { return new Object[][]{ { http3URI, defaultFsPath, MSG, true }, { http3URI, defaultFsPath, MSG, false }, - { httpURI, defaultFsPath, MSG, true }, { httpsURI, defaultFsPath, MSG, true }, { http2URI, defaultFsPath, MSG, true }, @@ -123,7 +124,8 @@ public class BodyHandlerOfFileDownloadTest implements HttpServerAdapters { }; } - @Test(dataProvider = "defaultFsData") + @ParameterizedTest + @MethodSource("defaultFsData") public void testDefaultFs(String uriString, Path path, String expectedMsg, @@ -171,10 +173,10 @@ public class BodyHandlerOfFileDownloadTest implements HttpServerAdapters { out.printf("Resp code: %s\n", resp.statusCode()); out.println("Resp body Path: " + resp.body()); out.printf("Resp body written to file: %s\n", msg); - assertEquals(resp.statusCode(), 200); - assertEquals(msg, expectedMsg); + assertEquals(200, resp.statusCode()); + assertEquals(expectedMsg, msg); assertTrue(resp.headers().firstValue("Content-Disposition").isPresent()); - assertEquals(resp.headers().firstValue("Content-Disposition").get(), contentDispositionValue); + assertEquals(contentDispositionValue, resp.headers().firstValue("Content-Disposition").get()); if (!sameClient) { client.close(); } @@ -199,15 +201,17 @@ public class BodyHandlerOfFileDownloadTest implements HttpServerAdapters { return dir; } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testZipFs() { - out.printf("\n\n--- testZipFs(): starting\n"); - BodyHandlers.ofFileDownload(zipFsPath, CREATE, TRUNCATE_EXISTING, WRITE); + Assertions.assertThrows(IllegalArgumentException.class, () -> { + out.printf("\n\n--- testZipFs(): starting\n"); + BodyHandlers.ofFileDownload(zipFsPath, CREATE, TRUNCATE_EXISTING, WRITE); + }); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { defaultFsPath = defaultFsDir(); zipFs = newZipFs(); zipFsPath = zipFsDir(zipFs); @@ -239,8 +243,8 @@ public class BodyHandlerOfFileDownloadTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { if (Files.exists(zipFsPath)) FileUtils.deleteFileTreeWithRetry(zipFsPath); if (Files.exists(defaultFsPath)) diff --git a/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileTest.java b/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileTest.java index 13871933eac..53b8607f294 100644 --- a/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileTest.java +++ b/test/jdk/java/net/httpclient/PathSubscriber/BodyHandlerOfFileTest.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 @@ -36,15 +36,11 @@ * jdk.httpclient.test.lib.http2.Queue * jdk.test.lib.net.SimpleSSLContext * jdk.test.lib.Platform jdk.test.lib.util.FileUtils - * @run testng/othervm BodyHandlerOfFileTest + * @run junit/othervm BodyHandlerOfFileTest */ import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.util.FileUtils; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -56,7 +52,10 @@ import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; -import java.nio.file.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Map; import jdk.httpclient.test.lib.common.HttpServerAdapters; import static java.lang.System.out; @@ -66,26 +65,31 @@ import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BodyHandlerOfFileTest implements HttpServerAdapters { static final String MSG = "msg"; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; - FileSystem zipFs; - Path defaultFsPath; - Path zipFsPath; + private static FileSystem zipFs; + private static Path defaultFsPath; + private static Path zipFsPath; // Default file system set-up @@ -97,8 +101,7 @@ public class BodyHandlerOfFileTest implements HttpServerAdapters { return file; } - @DataProvider(name = "defaultFsData") - public Object[][] defaultFsData() { + public static Object[][] defaultFsData() { return new Object[][]{ { http3URI, defaultFsPath, MSG, true }, { http3URI, defaultFsPath, MSG, false }, @@ -114,7 +117,8 @@ public class BodyHandlerOfFileTest implements HttpServerAdapters { }; } - @Test(dataProvider = "defaultFsData") + @ParameterizedTest + @MethodSource("defaultFsData") public void testDefaultFs(String uriString, Path path, String expectedMsg, @@ -139,8 +143,7 @@ public class BodyHandlerOfFileTest implements HttpServerAdapters { return file; } - @DataProvider(name = "zipFsData") - public Object[][] zipFsData() { + public static Object[][] zipFsData() { return new Object[][]{ { http3URI, zipFsPath, MSG, true }, { http3URI, zipFsPath, MSG, false }, @@ -156,7 +159,8 @@ public class BodyHandlerOfFileTest implements HttpServerAdapters { }; } - @Test(dataProvider = "zipFsData") + @ParameterizedTest + @MethodSource("zipFsData") public void testZipFs(String uriString, Path path, String expectedMsg, @@ -203,8 +207,8 @@ public class BodyHandlerOfFileTest implements HttpServerAdapters { String msg = Files.readString(path, StandardCharsets.UTF_8); out.printf("Resp code: %s\n", resp.statusCode()); out.printf("Msg written to %s: %s\n", resp.body(), msg); - assertEquals(resp.statusCode(), 200); - assertEquals(msg, expectedMsg); + assertEquals(200, resp.statusCode()); + assertEquals(expectedMsg, msg); if (!sameClient) { client.close(); } @@ -214,8 +218,8 @@ public class BodyHandlerOfFileTest implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { defaultFsPath = defaultFsFile(); zipFs = newZipFs(); zipFsPath = zipFsFile(zipFs); @@ -247,8 +251,8 @@ public class BodyHandlerOfFileTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { if (Files.exists(zipFsPath)) FileUtils.deleteFileTreeWithRetry(zipFsPath); if (Files.exists(defaultFsPath)) diff --git a/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.java b/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.java index d7c9a3af4f1..da0c7220b6b 100644 --- a/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.java +++ b/test/jdk/java/net/httpclient/PathSubscriber/BodySubscriberOfFileTest.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 @@ -35,15 +35,11 @@ * jdk.httpclient.test.lib.http2.OutgoingPushPromise * jdk.httpclient.test.lib.http2.Queue jdk.test.lib.net.SimpleSSLContext * jdk.test.lib.Platform jdk.test.lib.util.FileUtils - * @run testng/othervm BodySubscriberOfFileTest + * @run junit/othervm BodySubscriberOfFileTest */ import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.util.FileUtils; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -58,7 +54,10 @@ import java.net.http.HttpResponse.BodySubscribers; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.nio.file.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Map; import java.util.concurrent.Flow; import java.util.stream.IntStream; @@ -70,26 +69,32 @@ import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BodySubscriberOfFileTest implements HttpServerAdapters { static final String MSG = "msg"; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] - HttpTestServer httpsTestServer; // HTTPS/1.1 - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - HttpTestServer http3TestServer; // HTTP/3 ( h3 ) - String httpURI; - String httpsURI; - String http2URI; - String https2URI; - String http3URI; + private static HttpTestServer httpTestServer; // HTTP/1.1 [ 5 servers ] + private static HttpTestServer httpsTestServer; // HTTPS/1.1 + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 ) + private static String httpURI; + private static String httpsURI; + private static String http2URI; + private static String https2URI; + private static String http3URI; - FileSystem zipFs; - Path defaultFsPath; - Path zipFsPath; + private static FileSystem zipFs; + private static Path defaultFsPath; + private static Path zipFsPath; // Default file system set-up @@ -101,8 +106,7 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { return file; } - @DataProvider(name = "defaultFsData") - public Object[][] defaultFsData() { + public static Object[][] defaultFsData() { return new Object[][]{ { http3URI, defaultFsPath, MSG, true }, { http3URI, defaultFsPath, MSG, false }, @@ -118,7 +122,8 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { }; } - @Test(dataProvider = "defaultFsData") + @ParameterizedTest + @MethodSource("defaultFsData") public void testDefaultFs(String uriString, Path path, String expectedMsg, @@ -143,8 +148,7 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { return file; } - @DataProvider(name = "zipFsData") - public Object[][] zipFsData() { + public static Object[][] zipFsData() { return new Object[][]{ { http3URI, zipFsPath, MSG, true }, { http3URI, zipFsPath, MSG, false }, @@ -160,7 +164,8 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { }; } - @Test(dataProvider = "zipFsData") + @ParameterizedTest + @MethodSource("zipFsData") public void testZipFs(String uriString, Path path, String expectedMsg, @@ -209,8 +214,8 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { String msg = Files.readString(path, StandardCharsets.UTF_8); out.printf("Resp code: %s\n", resp.statusCode()); out.printf("Msg written to %s: %s\n", resp.body(), msg); - assertEquals(resp.statusCode(), 200); - assertEquals(msg, expectedMsg); + assertEquals(200, resp.statusCode()); + assertEquals(expectedMsg, msg); if (!sameClient) { client.close(); } @@ -240,12 +245,12 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { }); subscriber.onNext(buffers); subscriber.onComplete(); - buffers.forEach(b -> assertEquals(b.remaining(), 0) ); + buffers.forEach(b -> assertEquals(0, b.remaining()) ); assertEquals(expectedSize, Files.size(defaultFsPath)); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { defaultFsPath = defaultFsFile(); zipFs = newZipFs(); zipFsPath = zipFsFile(zipFs); @@ -277,8 +282,8 @@ public class BodySubscriberOfFileTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { if (Files.exists(zipFsPath)) FileUtils.deleteFileTreeWithRetry(zipFsPath); if (Files.exists(defaultFsPath)) diff --git a/test/jdk/java/net/httpclient/offline/OfflineTesting.java b/test/jdk/java/net/httpclient/offline/OfflineTesting.java index 2f4833bf179..f36a457022e 100644 --- a/test/jdk/java/net/httpclient/offline/OfflineTesting.java +++ b/test/jdk/java/net/httpclient/offline/OfflineTesting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -25,7 +25,7 @@ * @test * @summary Demonstrates how to achieve testing without network connections * @build DelegatingHttpClient FixedHttpResponse FixedResponseHttpClient - * @run testng/othervm OfflineTesting + * @run junit/othervm OfflineTesting */ import java.io.IOException; @@ -40,13 +40,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.BiPredicate; -import org.testng.annotations.Test; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNull; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class OfflineTesting { @@ -72,9 +74,9 @@ public class OfflineTesting { client.sendAsync(request, BodyHandlers.ofString()) .thenAccept(response -> { System.out.println("response: " + response); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(response.headers().firstValue("Server").isPresent()); - assertEquals(response.body(), "A response message"); + assertEquals("A response message", response.body()); }) .join(); } @@ -91,9 +93,9 @@ public class OfflineTesting { client.sendAsync(request, BodyHandlers.ofByteArray()) .thenAccept(response -> { System.out.println("response: " + response); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); assertTrue(response.headers().firstValue("Content-Type").isPresent()); - assertEquals(response.body(), "A response message".getBytes(UTF_8)); + Assertions.assertArrayEquals("A response message".getBytes(UTF_8), response.body()); }) .join(); } @@ -125,9 +127,9 @@ public class OfflineTesting { try (var client = fixedClient) { client.sendAsync(request, BodyHandlers.ofString()) .thenAccept(response -> { - assertEquals(response.statusCode(), 404); + assertEquals(404, response.statusCode()); response.headers().firstValue("Content-Type") - .ifPresentOrElse(type -> assertEquals(type, "text/html"), + .ifPresentOrElse(type -> assertEquals("text/html", type), () -> fail("Content-Type not present")); assertTrue(response.body().contains("404 Not Found")); }) @@ -151,8 +153,8 @@ public class OfflineTesting { client.sendAsync(request, BodyHandlers.ofString()) .thenAccept(response -> { System.out.println("response: " + response); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), "Hello World"); + assertEquals(200, response.statusCode()); + assertEquals("Hello World", response.body()); }) .join(); } @@ -172,8 +174,8 @@ public class OfflineTesting { HttpResponse response = client.send(request, BodyHandlers.ofString()); System.out.println("response: " + response); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), "Hello chegar!!"); + assertEquals(200, response.statusCode()); + assertEquals("Hello chegar!!", response.body()); } } diff --git a/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java b/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java index c8920771727..361b053fe43 100644 --- a/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java +++ b/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -24,7 +24,7 @@ /* * @test * @summary Basic checks for File Processors - * @run testng/othervm FileProcessorPermissionTest + * @run junit/othervm FileProcessorPermissionTest */ import java.nio.file.Path; @@ -32,9 +32,9 @@ import java.nio.file.Paths; import java.util.List; import java.net.http.HttpRequest; import java.net.http.HttpResponse.BodyHandlers; -import org.testng.annotations.Test; import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.Test; public class FileProcessorPermissionTest { diff --git a/test/jdk/java/net/httpclient/security/filePerms/SecurityBeforeFile.java b/test/jdk/java/net/httpclient/security/filePerms/SecurityBeforeFile.java index d54e27b89ce..c8d7bb64b36 100644 --- a/test/jdk/java/net/httpclient/security/filePerms/SecurityBeforeFile.java +++ b/test/jdk/java/net/httpclient/security/filePerms/SecurityBeforeFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -25,7 +25,7 @@ * @test * @summary Verifies security checks are performed before existence checks * in pre-defined body processors APIs - * @run testng/othervm SecurityBeforeFile + * @run junit/othervm SecurityBeforeFile */ import java.io.FileNotFoundException; @@ -35,11 +35,13 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse.BodyHandlers; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.fail; public class SecurityBeforeFile { @@ -57,8 +59,7 @@ public class SecurityBeforeFile { } } - @DataProvider(name = "handlerOpenOptions") - public Object[][] handlerOpenOptions() { + public static Object[][] handlerOpenOptions() { return new Object[][] { { new OpenOption[] { } }, { new OpenOption[] { CREATE } }, @@ -66,7 +67,8 @@ public class SecurityBeforeFile { }; } - @Test(dataProvider = "handlerOpenOptions") + @ParameterizedTest + @MethodSource("handlerOpenOptions") public void BodyHandlersOfFileDownload(OpenOption[] openOptions) { Path p = Paths.get("doesNotExistDir"); if (Files.exists(p)) From 2678fe41ca29ca413ab1fbfc69b689c9be1b6c14 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Mon, 2 Mar 2026 14:16:23 +0000 Subject: [PATCH 111/636] 8378870: Remove sun.awt.AWTAccessor from imports in ImageIcon Reviewed-by: serb, kizune, azvegint --- src/java.desktop/share/classes/javax/swing/ImageIcon.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/ImageIcon.java b/src/java.desktop/share/classes/javax/swing/ImageIcon.java index ee6c08ebb15..707a1766eee 100644 --- a/src/java.desktop/share/classes/javax/swing/ImageIcon.java +++ b/src/java.desktop/share/classes/javax/swing/ImageIcon.java @@ -53,8 +53,6 @@ import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; -import sun.awt.AWTAccessor; - /** * An implementation of the Icon interface that paints Icons * from Images. Images that are created from a URL, filename or byte array From 2adffe0c3cf911df55a79d183d8d63f03b3acc97 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Mon, 2 Mar 2026 14:19:34 +0000 Subject: [PATCH 112/636] 8378872: Mark waitList in FetcherInfo final Reviewed-by: prr, azvegint --- .../share/classes/sun/awt/image/ImageFetcher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java b/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java index 917378389a4..986b79edde9 100644 --- a/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java +++ b/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -314,10 +314,10 @@ class FetcherInfo { static final int MAX_NUM_FETCHERS = 4; static final FetcherInfo FETCHER_INFO = new FetcherInfo(); - Thread[] fetchers; + final Thread[] fetchers; int numFetchers; int numWaiting; - Vector waitList; + final Vector waitList; private FetcherInfo() { fetchers = new Thread[MAX_NUM_FETCHERS]; From cc4ca9fde84c95e369169fe1cd3f62c5d3379d18 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Mon, 2 Mar 2026 14:25:41 +0000 Subject: [PATCH 113/636] 8378128: Make PLABStats data members private Reviewed-by: tschatzl, ayang, jwaters --- src/hotspot/share/gc/shared/plab.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/shared/plab.hpp b/src/hotspot/share/gc/shared/plab.hpp index 5200f022633..d893a720e2a 100644 --- a/src/hotspot/share/gc/shared/plab.hpp +++ b/src/hotspot/share/gc/shared/plab.hpp @@ -147,14 +147,14 @@ public: // PLAB book-keeping. class PLABStats : public CHeapObj { -protected: - const char* _description; // Identifying string. - Atomic _allocated; // Total allocated Atomic _wasted; // of which wasted (internal fragmentation) Atomic _undo_wasted; // of which wasted on undo (is not used for calculation of PLAB size) Atomic _unused; // Unused in last buffer +protected: + const char* _description; // Identifying string. + virtual void reset() { _allocated.store_relaxed(0); _wasted.store_relaxed(0); @@ -164,11 +164,11 @@ protected: public: PLABStats(const char* description) : - _description(description), _allocated(0), _wasted(0), _undo_wasted(0), - _unused(0) + _unused(0), + _description(description) { } virtual ~PLABStats() { } From da99f1a330bfa363507908fe83ac2f8c7cd4b18a Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Mon, 2 Mar 2026 14:29:11 +0000 Subject: [PATCH 114/636] 8378897: assertion failure due to missing depends_only_on_test_impl definition in SqrtHFNode Reviewed-by: qamai --- src/hotspot/share/opto/subnode.hpp | 3 + .../c2/TestDependsOnTestSqrtHFAssertion.java | 69 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/c2/TestDependsOnTestSqrtHFAssertion.java diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index a90661c49ee..54cb1d20cd0 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -564,6 +564,9 @@ public: const Type* bottom_type() const { return Type::HALF_FLOAT; } virtual uint ideal_reg() const { return Op_RegF; } virtual const Type* Value(PhaseGVN* phase) const; + +private: + virtual bool depends_only_on_test_impl() const { return false; } }; diff --git a/test/hotspot/jtreg/compiler/c2/TestDependsOnTestSqrtHFAssertion.java b/test/hotspot/jtreg/compiler/c2/TestDependsOnTestSqrtHFAssertion.java new file mode 100644 index 00000000000..5ad9a927097 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestDependsOnTestSqrtHFAssertion.java @@ -0,0 +1,69 @@ +/* + * 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 8378897 + * @summary assertion failure due to missing depends_only_on_test_impl definition in SqrtHFNode + * @library /test/lib / + * @modules jdk.incubator.vector + * @requires vm.debug & vm.compiler2.enabled + * @run main/othervm --add-modules=jdk.incubator.vector compiler.c2.TestDependsOnTestSqrtHFAssertion + */ + +package compiler.c2; + +import jdk.incubator.vector.*; + +public class TestDependsOnTestSqrtHFAssertion { + public static int x1 = 10; + + public static int x2 = 20; + + public static int y = 30; + + public static int micro(int x1, int x2, int y, int ctr) { + int res = 0; + for (int i = 0; i < ctr; i++) { + if (y != 0) { + if (x1 > 0) { + if (x2 > 0) { + if (y != 0) { + res += (int)Float16.float16ToRawShortBits(Float16.sqrt(Float16.shortBitsToFloat16((short)(x1/y)))); + res += (int)Float16.float16ToRawShortBits(Float16.sqrt(Float16.shortBitsToFloat16((short)(x2/y)))); + } + } + } + } + } + return res; + } + + public static void main(String [] args) { + int res = 0; + for (int i = 0 ; i < 100000; i++) { + res += micro(x1, x2, y, i); + } + IO.println("PASS" + res); + } +} From 7b5b70c9cb67b7e04d92fbf31dc1d1b97ee99613 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Mon, 2 Mar 2026 15:36:54 +0000 Subject: [PATCH 115/636] 8378565: Refactor test/jdk/java/net/httpclient/http3/*.java TestNG tests to JUnit Reviewed-by: vyazici --- .../http3/BadCipherSuiteErrorTest.java | 6 +- .../httpclient/http3/H3BadHeadersTest.java | 59 ++++++------- .../net/httpclient/http3/H3BasicTest.java | 7 +- .../httpclient/http3/H3ConcurrentPush.java | 61 +++++++------- .../http3/H3ConnectionPoolTest.java | 17 ++-- .../http3/H3FixedThreadPoolTest.java | 6 +- .../http3/H3HeaderSizeLimitTest.java | 33 ++++---- .../httpclient/http3/H3HeadersEncoding.java | 27 +++--- .../http3/H3ImplicitPushCancel.java | 51 ++++++------ .../http3/H3InsertionsLimitTest.java | 31 ++++--- .../http3/H3LogHandshakeErrors.java | 32 ++++--- .../http3/H3MemoryHandlingTest.java | 44 +++++----- .../H3MultipleConnectionsToSameHost.java | 23 ++--- .../net/httpclient/http3/H3PushCancel.java | 75 ++++++++--------- .../net/httpclient/http3/H3RedirectTest.java | 9 +- .../net/httpclient/http3/H3ServerPush.java | 83 ++++++++++--------- .../httpclient/http3/H3ServerPushCancel.java | 63 +++++++------- .../http3/H3ServerPushWithDiffTypes.java | 21 ++--- .../net/httpclient/http3/H3SimpleGet.java | 47 ++++++----- .../net/httpclient/http3/H3SimplePost.java | 17 ++-- .../net/httpclient/http3/H3SimpleTest.java | 37 +++++---- .../httpclient/http3/H3StopSendingTest.java | 35 ++++---- .../http3/H3StreamLimitReachedTest.java | 21 ++--- .../net/httpclient/http3/HTTP3NoBodyTest.java | 18 ++-- .../http3/Http3ExpectContinueTest.java | 42 +++++----- .../http3/PeerUniStreamDispatcherTest.java | 46 +++++----- .../net/httpclient/http3/StopSendingTest.java | 27 +++--- .../net/httpclient/http3/StreamLimitTest.java | 57 ++++++------- 28 files changed, 505 insertions(+), 490 deletions(-) diff --git a/test/jdk/java/net/httpclient/http3/BadCipherSuiteErrorTest.java b/test/jdk/java/net/httpclient/http3/BadCipherSuiteErrorTest.java index 727f2c78352..fdfc498b21e 100644 --- a/test/jdk/java/net/httpclient/http3/BadCipherSuiteErrorTest.java +++ b/test/jdk/java/net/httpclient/http3/BadCipherSuiteErrorTest.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 @@ -26,7 +26,7 @@ * @bug 8157105 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm/timeout=60 -Djavax.net.debug=ssl -Djdk.httpclient.HttpClient.log=all BadCipherSuiteErrorTest + * @run junit/othervm/timeout=60 -Djavax.net.debug=ssl -Djdk.httpclient.HttpClient.log=all BadCipherSuiteErrorTest * @summary check exception thrown when bad TLS parameters selected */ @@ -49,7 +49,7 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; /** * When selecting an unacceptable cipher suite the TLS handshake will fail. diff --git a/test/jdk/java/net/httpclient/http3/H3BadHeadersTest.java b/test/jdk/java/net/httpclient/http3/H3BadHeadersTest.java index fb0e1ef3085..fff11f4ceb4 100644 --- a/test/jdk/java/net/httpclient/http3/H3BadHeadersTest.java +++ b/test/jdk/java/net/httpclient/http3/H3BadHeadersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,17 +27,13 @@ * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext * @compile ../ReferenceTracker.java - * @run testng/othervm -Djdk.internal.httpclient.debug=true H3BadHeadersTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true H3BadHeadersTest * @summary this test verifies the behaviour of the HttpClient when presented * with bad headers */ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; import java.io.InputStream; @@ -57,10 +53,17 @@ import java.util.concurrent.ExecutionException; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.util.List.of; import static java.util.Map.entry; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class H3BadHeadersTest implements HttpServerAdapters { @@ -76,14 +79,13 @@ public class H3BadHeadersTest implements HttpServerAdapters { static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer http3TestServer; // HTTP/3 ( h3 only ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 + h3 ) - String http3URI; - String https2URI; + private static HttpTestServer http3TestServer; // HTTP/3 ( h3 only ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 + h3 ) + private static String http3URI; + private static String https2URI; - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][] { { http3URI, false}, { https2URI, false}, @@ -93,7 +95,8 @@ public class H3BadHeadersTest implements HttpServerAdapters { } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void test(String uri, boolean sameClient) throws Exception @@ -126,8 +129,8 @@ public class H3BadHeadersTest implements HttpServerAdapters { .HEAD().setOption(H3_DISCOVERY, config).build(); System.out.println("\nSending HEAD request: " + head); var headResponse = client.send(head, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), Version.HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(Version.HTTP_2, headResponse.version()); } URI uriWithQuery = URI.create(uri + "?BAD_HEADERS=" + i); @@ -163,7 +166,8 @@ public class H3BadHeadersTest implements HttpServerAdapters { System.err.printf("%ntest %s, %s, DONE%n%n", uri, sameClient); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void testAsync(String uri, boolean sameClient) throws Exception { @@ -199,8 +203,8 @@ public class H3BadHeadersTest implements HttpServerAdapters { System.out.println("\nSending HEAD request: " + head); var headResponse = client.send(head, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), Version.HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(Version.HTTP_2, headResponse.version()); } URI uriWithQuery = URI.create(uri + "?BAD_HEADERS=" + i); @@ -246,8 +250,7 @@ public class H3BadHeadersTest implements HttpServerAdapters { // sync with implementation. static void assertDetailMessage(Throwable throwable, int iterationIndex) { try { - assertTrue(throwable instanceof IOException, - "Expected IOException, got, " + throwable); + assertInstanceOf(IOException.class, throwable, "Expected IOException, got, " + throwable); assertNotNull(throwable.getMessage(), "No message for " + throwable); assertTrue(throwable.getMessage().contains("malformed response"), "Expected \"malformed response\" in: " + throwable.getMessage()); @@ -269,8 +272,8 @@ public class H3BadHeadersTest implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { System.out.println("creating servers"); http3TestServer = HttpTestServer.create(Http3DiscoveryMode.HTTP_3_URI_ONLY, sslContext); http3TestServer.addHandler(new BadHeadersHandler(), "/http3/echo"); @@ -285,8 +288,8 @@ public class H3BadHeadersTest implements HttpServerAdapters { System.out.println("server started"); } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { System.err.println("\n\n**** stopping servers\n"); System.out.println("stopping servers"); http3TestServer.stop(); diff --git a/test/jdk/java/net/httpclient/http3/H3BasicTest.java b/test/jdk/java/net/httpclient/http3/H3BasicTest.java index a03df11c1a3..bddb9879ae9 100644 --- a/test/jdk/java/net/httpclient/http3/H3BasicTest.java +++ b/test/jdk/java/net/httpclient/http3/H3BasicTest.java @@ -31,7 +31,7 @@ * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors + * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors * -Djdk.internal.httpclient.debug=true * H3BasicTest */ @@ -60,7 +60,6 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; @@ -70,6 +69,8 @@ import static jdk.test.lib.Asserts.assertFileContentsEqual; import static jdk.test.lib.Utils.createTempFile; import static jdk.test.lib.Utils.createTempFileOfSize; +import org.junit.jupiter.api.Test; + public class H3BasicTest implements HttpServerAdapters { private static final Random RANDOM = RandomFactory.getRandom(); @@ -151,7 +152,7 @@ public class H3BasicTest implements HttpServerAdapters { } @Test - public static void test() throws Exception { + public void test() throws Exception { try { initialize(); System.out.println("servers initialized"); diff --git a/test/jdk/java/net/httpclient/http3/H3ConcurrentPush.java b/test/jdk/java/net/httpclient/http3/H3ConcurrentPush.java index 0cdb2f900fd..4392f829258 100644 --- a/test/jdk/java/net/httpclient/http3/H3ConcurrentPush.java +++ b/test/jdk/java/net/httpclient/http3/H3ConcurrentPush.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 @@ -25,7 +25,7 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,responses,trace * -Djdk.httpclient.http3.maxConcurrentPushStreams=45 @@ -73,17 +73,18 @@ import java.util.function.Supplier; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.common.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class H3ConcurrentPush implements HttpServerAdapters { @@ -92,7 +93,7 @@ public class H3ConcurrentPush implements HttpServerAdapters { static final PrintStream err = System.err; static final PrintStream out = System.out; - static Map PUSH_PROMISES = Map.of( + static final Map PUSH_PROMISES = Map.of( "/x/y/z/1", "the first push promise body", "/x/y/z/2", "the second push promise body", "/x/y/z/3", "the third push promise body", @@ -105,13 +106,13 @@ public class H3ConcurrentPush implements HttpServerAdapters { ); static final String MAIN_RESPONSE_BODY = "the main response body"; - HttpTestServer server; - URI uri; - URI headURI; - ServerPushHandler pushHandler; + private static HttpTestServer server; + private static URI uri; + private static URI headURI; + private static ServerPushHandler pushHandler; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { server = HttpTestServer.create(ANY, SimpleSSLContext.findSSLContext()); pushHandler = new ServerPushHandler(MAIN_RESPONSE_BODY, PUSH_PROMISES); server.addHandler(pushHandler, "/push/"); @@ -122,14 +123,14 @@ public class H3ConcurrentPush implements HttpServerAdapters { headURI = new URI("https://" + server.serverAuthority() + "/head/x"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { server.stop(); } static HttpResponse assert200ResponseCode(HttpResponse response) { - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); return response; } @@ -137,8 +138,8 @@ public class H3ConcurrentPush implements HttpServerAdapters { HttpRequest headRequest = HttpRequest.newBuilder(headURI) .HEAD().version(Version.HTTP_2).build(); var headResponse = client.send(headRequest, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), Version.HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(Version.HTTP_2, headResponse.version()); } static final class TestPushPromiseHandler implements PushPromiseHandler { @@ -233,16 +234,16 @@ public class H3ConcurrentPush implements HttpServerAdapters { promises.forEach((request, value) -> { HttpResponse response = value.join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (PUSH_PROMISES.containsKey(request.uri().getPath())) { - assertEquals(response.body(), PUSH_PROMISES.get(request.uri().getPath())); + assertEquals(PUSH_PROMISES.get(request.uri().getPath()), response.body()); } else { - assertEquals(response.body(), MAIN_RESPONSE_BODY); + assertEquals(MAIN_RESPONSE_BODY, response.body()); } }); int expectedPushes = Math.min(PUSH_PROMISES.size(), maxPushes) + 5; - assertEquals(promises.size(), expectedPushes); + assertEquals(expectedPushes, promises.size()); promises.clear(); @@ -251,12 +252,12 @@ public class H3ConcurrentPush implements HttpServerAdapters { client.sendAsync(HttpRequest.newBuilder(uri).build(), BodyHandlers.ofString()) .thenApply(H3ConcurrentPush::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); } catch (CompletionException c) { throw new AssertionError(c.getCause()); } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // Send with no promise handler, but use pushId bigger than allowed. // This should cause the connection to get closed @@ -268,7 +269,7 @@ public class H3ConcurrentPush implements HttpServerAdapters { client.sendAsync(bigger, BodyHandlers.ofString()) .thenApply(H3ConcurrentPush::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); throw new AssertionError("Expected IOException not thrown"); } catch (CompletionException c) { @@ -287,7 +288,7 @@ public class H3ConcurrentPush implements HttpServerAdapters { throw new AssertionError("Unexpected exception: " + c.getCause(), c.getCause()); } } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // the next time around we should have a new connection, // so we can restart from scratch @@ -298,7 +299,7 @@ public class H3ConcurrentPush implements HttpServerAdapters { var error = errors.stream().findFirst().orElse(null); if (error != null) throw error; var notified = custom.notified; - assertEquals(notified.size(), 9*4*2, "Unexpected notification: " + notified); + assertEquals(9*4*2, notified.size(), "Unexpected notification: " + notified); } } diff --git a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java index c90059ccbfd..614d564005b 100644 --- a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java @@ -28,7 +28,7 @@ * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:hs + * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:hs * -Djdk.internal.httpclient.debug=false * H3ConnectionPoolTest */ @@ -53,7 +53,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; @@ -65,6 +64,8 @@ import static jdk.test.lib.Asserts.assertEquals; import static jdk.test.lib.Asserts.assertNotEquals; import static jdk.test.lib.Asserts.assertTrue; +import org.junit.jupiter.api.Test; + public class H3ConnectionPoolTest implements HttpServerAdapters { private static final String CLASS_NAME = H3ConnectionPoolTest.class.getSimpleName(); @@ -172,7 +173,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { } @Test - public static void testH3Only() throws Exception { + public void testH3Only() throws Exception { System.out.println("\nTesting HTTP/3 only"); initialize(true); try (HttpClient client = getClient()) { @@ -212,12 +213,12 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { } @Test - public static void testH2H3WithTwoAltSVC() throws Exception { + public void testH2H3WithTwoAltSVC() throws Exception { testH2H3(false); } @Test - public static void testH2H3WithAltSVCOnSamePort() throws Exception { + public void testH2H3WithAltSVCOnSamePort() throws Exception { testH2H3(true); } @@ -309,7 +310,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { // fourth request with HTTP_3_URI_ONLY should reuse the first connection, // and not reuse the second. HttpRequest request4 = req1Builder.copy().build(); - HttpResponse response4 = client.send(request1, BodyHandlers.ofString()); + HttpResponse response4 = client.send(request4, BodyHandlers.ofString()); assertEquals(HTTP_3, response4.version()); assertEquals(response4.connectionLabel().get(), response1.connectionLabel().get()); assertNotEquals(response4.connectionLabel().get(), response3.connectionLabel().get()); @@ -345,12 +346,12 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { } @Test - public static void testParallelH2H3WithTwoAltSVC() throws Exception { + public void testParallelH2H3WithTwoAltSVC() throws Exception { testH2H3Concurrent(false); } @Test - public static void testParallelH2H3WithAltSVCOnSamePort() throws Exception { + public void testParallelH2H3WithAltSVCOnSamePort() throws Exception { testH2H3Concurrent(true); } diff --git a/test/jdk/java/net/httpclient/http3/H3FixedThreadPoolTest.java b/test/jdk/java/net/httpclient/http3/H3FixedThreadPoolTest.java index 6c181186fda..fd0e8214361 100644 --- a/test/jdk/java/net/httpclient/http3/H3FixedThreadPoolTest.java +++ b/test/jdk/java/net/httpclient/http3/H3FixedThreadPoolTest.java @@ -36,7 +36,7 @@ * JTreg on Tier 7 so that, if the client becomes wedged again, the * JTreg timeout handlers can collect more diagnostic information. * - * @run testng/othervm -Djdk.internal.httpclient.debug=err + * @run junit/othervm -Djdk.internal.httpclient.debug=err * -Djdk.httpclient.HttpClient.log=ssl,headers,requests,responses,errors * -Djdk.httpclient.quic.idleTimeout=666666 * -Djdk.test.server.quic.idleTimeout=666666 @@ -69,7 +69,7 @@ import static java.net.http.HttpOption.H3_DISCOVERY; import static jdk.test.lib.Asserts.assertFileContentsEqual; import static jdk.test.lib.Utils.createTempFileOfSize; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class H3FixedThreadPoolTest implements HttpServerAdapters { @@ -118,7 +118,7 @@ public class H3FixedThreadPoolTest implements HttpServerAdapters { } @Test - public static void test() throws Exception { + public void test() throws Exception { try { initialize(); simpleTest(false); diff --git a/test/jdk/java/net/httpclient/http3/H3HeaderSizeLimitTest.java b/test/jdk/java/net/httpclient/http3/H3HeaderSizeLimitTest.java index b48ef9a33f3..d5864989436 100644 --- a/test/jdk/java/net/httpclient/http3/H3HeaderSizeLimitTest.java +++ b/test/jdk/java/net/httpclient/http3/H3HeaderSizeLimitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -29,7 +29,6 @@ import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; -import java.time.Duration; import java.util.concurrent.ExecutionException; import javax.net.ssl.SSLContext; @@ -39,16 +38,16 @@ import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.httpclient.test.lib.quic.QuicServer; import jdk.internal.net.http.Http3ConnectionAccess; import jdk.internal.net.http.http3.ConnectionSettings; -import jdk.test.lib.Utils; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + /* * @test * @summary Verifies that the HTTP client respects the SETTINGS_MAX_FIELD_SECTION_SIZE setting on HTTP3 connection @@ -57,7 +56,7 @@ import static java.net.http.HttpOption.H3_DISCOVERY; * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * @build java.net.http/jdk.internal.net.http.Http3ConnectionAccess - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors H3HeaderSizeLimitTest */ @@ -65,11 +64,11 @@ public class H3HeaderSizeLimitTest implements HttpServerAdapters { private static final long HEADER_SIZE_LIMIT_BYTES = 1024; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer h3Server; - private String requestURIBase; + private static HttpTestServer h3Server; + private static String requestURIBase; - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { final QuicServer quicServer = Http3TestServer.quicServerBuilder() .sslContext(sslContext) .build(); @@ -82,8 +81,8 @@ public class H3HeaderSizeLimitTest implements HttpServerAdapters { .port(h3Server.getAddress().getPort()).build().toString(); } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { if (h3Server != null) { System.out.println("Stopping server " + h3Server.getAddress()); h3Server.stop(); @@ -111,7 +110,7 @@ public class H3HeaderSizeLimitTest implements HttpServerAdapters { final HttpResponse response = client.send( reqBuilder.build(), BodyHandlers.discarding()); - Assert.assertEquals(response.statusCode(), 200, "Unexpected status code"); + Assertions.assertEquals(200, response.statusCode(), "Unexpected status code"); if (i == 3) { var cf = Http3ConnectionAccess.peerSettings(client, response); if (!cf.isDone()) { @@ -131,14 +130,14 @@ public class H3HeaderSizeLimitTest implements HttpServerAdapters { } final HttpRequest request = reqBuilder.build(); System.out.println("Issuing request to " + reqURI); - final IOException thrown = Assert.expectThrows(ProtocolException.class, + final IOException thrown = Assertions.assertThrows(ProtocolException.class, () -> client.send(request, BodyHandlers.discarding())); if (!thrown.getMessage().equals("Request headers size exceeds limit set by peer")) { throw thrown; } // test same with async System.out.println("Issuing async request to " + reqURI); - final ExecutionException asyncThrown = Assert.expectThrows(ExecutionException.class, + final ExecutionException asyncThrown = Assertions.assertThrows(ExecutionException.class, () -> client.sendAsync(request, BodyHandlers.discarding()).get()); if (!(asyncThrown.getCause() instanceof ProtocolException)) { System.err.println("Received unexpected cause"); diff --git a/test/jdk/java/net/httpclient/http3/H3HeadersEncoding.java b/test/jdk/java/net/httpclient/http3/H3HeadersEncoding.java index 44ccfac4a6a..6b7b24f049d 100644 --- a/test/jdk/java/net/httpclient/http3/H3HeadersEncoding.java +++ b/test/jdk/java/net/httpclient/http3/H3HeadersEncoding.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 @@ -27,7 +27,7 @@ * @build jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext * @compile ../ReferenceTracker.java - * @run testng/othervm -Djdk.httpclient.qpack.encoderTableCapacityLimit=4096 + * @run junit/othervm -Djdk.httpclient.qpack.encoderTableCapacityLimit=4096 * -Djdk.httpclient.qpack.decoderMaxTableCapacity=4096 * -Dhttp3.test.server.encoderAllowedHeaders=* * -Dhttp3.test.server.decoderMaxTableCapacity=4096 @@ -40,9 +40,6 @@ import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -72,17 +69,21 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static jdk.httpclient.test.lib.common.HttpServerAdapters.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + public class H3HeadersEncoding { private static final int REQUESTS_COUNT = 500; private static final int HEADERS_PER_REQUEST = 20; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer http3TestServer; - HeadersHandler serverHeadersHandler; - String http3URI; + private static HttpTestServer http3TestServer; + private static HeadersHandler serverHeadersHandler; + private static String http3URI; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { System.out.println("Creating servers"); http3TestServer = HttpTestServer.create(Http3DiscoveryMode.HTTP_3_URI_ONLY, sslContext); serverHeadersHandler = new HeadersHandler(); @@ -92,8 +93,8 @@ public class H3HeadersEncoding { http3TestServer.start(); } - @AfterTest - public void tearDown() { + @AfterAll + public static void tearDown() { http3TestServer.stop(); } @@ -272,7 +273,7 @@ public class H3HeadersEncoding { } - private class HeadersHandler implements HttpTestHandler { + private static class HeadersHandler implements HttpTestHandler { @Override public void handle(HttpTestExchange t) throws IOException { diff --git a/test/jdk/java/net/httpclient/http3/H3ImplicitPushCancel.java b/test/jdk/java/net/httpclient/http3/H3ImplicitPushCancel.java index 130ccd40cd6..570f84ad620 100644 --- a/test/jdk/java/net/httpclient/http3/H3ImplicitPushCancel.java +++ b/test/jdk/java/net/httpclient/http3/H3ImplicitPushCancel.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 @@ -25,7 +25,7 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,responses,trace * H3ImplicitPushCancel @@ -56,17 +56,18 @@ import java.util.concurrent.ConcurrentMap; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.common.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class H3ImplicitPushCancel implements HttpServerAdapters { - static Map PUSH_PROMISES = Map.of( + static final Map PUSH_PROMISES = Map.of( "/x/y/z/1", "the first push promise body", "/x/y/z/2", "the second push promise body", "/x/y/z/3", "the third push promise body", @@ -79,12 +80,12 @@ public class H3ImplicitPushCancel implements HttpServerAdapters { ); static final String MAIN_RESPONSE_BODY = "the main response body"; - HttpTestServer server; - URI uri; - URI headURI; + private static HttpTestServer server; + private static URI uri; + private static URI headURI; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { server = HttpTestServer.create(ANY, SimpleSSLContext.findSSLContext()); HttpTestHandler pushHandler = new ServerPushHandler(MAIN_RESPONSE_BODY, PUSH_PROMISES); @@ -96,14 +97,14 @@ public class H3ImplicitPushCancel implements HttpServerAdapters { headURI = new URI("https://" + server.serverAuthority() + "/head/x"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { server.stop(); } static HttpResponse assert200ResponseCode(HttpResponse response) { - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); return response; } @@ -111,8 +112,8 @@ public class H3ImplicitPushCancel implements HttpServerAdapters { HttpRequest headRequest = HttpRequest.newBuilder(headURI) .HEAD().version(Version.HTTP_2).build(); var headResponse = client.send(headRequest, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), Version.HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(Version.HTTP_2, headResponse.version()); } /* @@ -136,7 +137,7 @@ public class H3ImplicitPushCancel implements HttpServerAdapters { .build(), BodyHandlers.ofString()) .thenApply(H3ImplicitPushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); System.out.println("Got result before error was raised"); throw new AssertionError("should have failed"); @@ -171,14 +172,14 @@ public class H3ImplicitPushCancel implements HttpServerAdapters { promises.putIfAbsent(main.request(), CompletableFuture.completedFuture(main)); promises.forEach((request, value) -> { HttpResponse response = value.join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (PUSH_PROMISES.containsKey(request.uri().getPath())) { - assertEquals(response.body(), PUSH_PROMISES.get(request.uri().getPath())); + assertEquals(PUSH_PROMISES.get(request.uri().getPath()), response.body()); } else { - assertEquals(response.body(), MAIN_RESPONSE_BODY); + assertEquals(MAIN_RESPONSE_BODY, response.body()); } }); - assertEquals(promises.size(), PUSH_PROMISES.size() + 1); + assertEquals(PUSH_PROMISES.size() + 1, promises.size()); promises.clear(); @@ -187,13 +188,13 @@ public class H3ImplicitPushCancel implements HttpServerAdapters { client.sendAsync(HttpRequest.newBuilder(uri).build(), BodyHandlers.ofString()) .thenApply(H3ImplicitPushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); } catch (CompletionException c) { throw new AssertionError(c.getCause()); } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); } } diff --git a/test/jdk/java/net/httpclient/http3/H3InsertionsLimitTest.java b/test/jdk/java/net/httpclient/http3/H3InsertionsLimitTest.java index ff38cd4fa98..6eabc23677a 100644 --- a/test/jdk/java/net/httpclient/http3/H3InsertionsLimitTest.java +++ b/test/jdk/java/net/httpclient/http3/H3InsertionsLimitTest.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 @@ -27,13 +27,8 @@ import jdk.httpclient.test.lib.quic.QuicServer; import jdk.internal.net.http.http3.ConnectionSettings; import jdk.internal.net.http.qpack.Encoder; import jdk.internal.net.http.qpack.TableEntry; -import jdk.test.lib.Utils; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -44,12 +39,16 @@ import java.net.http.HttpClient; import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; import java.net.http.HttpResponse.BodyHandlers; -import java.time.Duration; import java.util.concurrent.CountDownLatch; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + /* * @test * @summary Verifies that the HTTP client respects the maxLiteralWithIndexing @@ -59,7 +58,7 @@ import static java.net.http.HttpOption.H3_DISCOVERY; * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * @build java.net.http/jdk.internal.net.http.Http3ConnectionAccess - * @run testng/othervm -Djdk.httpclient.qpack.encoderTableCapacityLimit=4096 + * @run junit/othervm -Djdk.httpclient.qpack.encoderTableCapacityLimit=4096 * -Djdk.internal.httpclient.qpack.allowBlockingEncoding=true * -Djdk.httpclient.qpack.decoderMaxTableCapacity=4096 * -Djdk.httpclient.qpack.decoderBlockedStreams=1024 @@ -75,8 +74,8 @@ public class H3InsertionsLimitTest implements HttpServerAdapters { private static final long HEADER_SIZE_LIMIT_BYTES = 8192; private static final long MAX_SERVER_DT_CAPACITY = 4096; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer h3Server; - private String requestURIBase; + private static HttpTestServer h3Server; + private static String requestURIBase; public static final long MAX_LITERALS_WITH_INDEXING = 32L; private static final CountDownLatch WAIT_FOR_FAILURE = new CountDownLatch(1); @@ -120,8 +119,8 @@ public class H3InsertionsLimitTest implements HttpServerAdapters { exchange.sendResponseHeaders(200, 0); } - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { final QuicServer quicServer = Http3TestServer.quicServerBuilder() .bindAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)) .sslContext(sslContext) @@ -138,8 +137,8 @@ public class H3InsertionsLimitTest implements HttpServerAdapters { .port(h3Server.getAddress().getPort()).build().toString(); } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { if (h3Server != null) { System.out.println("Stopping server " + h3Server.getAddress()); h3Server.stop(); @@ -161,10 +160,10 @@ public class H3InsertionsLimitTest implements HttpServerAdapters { System.out.println("Issuing request to " + reqURI); try { client.send(request, BodyHandlers.discarding()); - Assert.fail("IOException expected"); + Assertions.fail("IOException expected"); } catch (IOException ioe) { System.out.println("Got IOException: " + ioe); - Assert.assertTrue(ioe.getMessage() + Assertions.assertTrue(ioe.getMessage() .contains("Too many literal with indexing")); } finally { WAIT_FOR_FAILURE.countDown(); diff --git a/test/jdk/java/net/httpclient/http3/H3LogHandshakeErrors.java b/test/jdk/java/net/httpclient/http3/H3LogHandshakeErrors.java index f7a258c069c..85a4b6113f0 100644 --- a/test/jdk/java/net/httpclient/http3/H3LogHandshakeErrors.java +++ b/test/jdk/java/net/httpclient/http3/H3LogHandshakeErrors.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 @@ -24,10 +24,8 @@ import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; -import java.net.Socket; import java.net.URI; import java.net.http.HttpClient; -import java.net.http.HttpOption; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; @@ -44,16 +42,16 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.quic.QuicConnectionImpl; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /* * @test @@ -63,7 +61,7 @@ import static org.testng.Assert.*; * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=errors * H3LogHandshakeErrors */ @@ -71,14 +69,14 @@ import static org.testng.Assert.*; public class H3LogHandshakeErrors implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer h3Server; - private ServerSocket tcpServerSocket = null; - private Thread tcpServerThread = null; - private String requestURI; + private static HttpTestServer h3Server; + private static ServerSocket tcpServerSocket = null; + private static Thread tcpServerThread = null; + private static String requestURI; private static Logger clientLogger; - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { // create an H3 only server h3Server = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); h3Server.addHandler((exchange) -> exchange.sendResponseHeaders(200, 0), "/hello"); @@ -113,8 +111,8 @@ public class H3LogHandshakeErrors implements HttpServerAdapters { } } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { if (h3Server != null) { System.out.println("Stopping server " + h3Server.getAddress()); h3Server.stop(); diff --git a/test/jdk/java/net/httpclient/http3/H3MemoryHandlingTest.java b/test/jdk/java/net/httpclient/http3/H3MemoryHandlingTest.java index c9474be51c2..2fcb7388f7b 100644 --- a/test/jdk/java/net/httpclient/http3/H3MemoryHandlingTest.java +++ b/test/jdk/java/net/httpclient/http3/H3MemoryHandlingTest.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 @@ -30,9 +30,6 @@ import jdk.internal.net.http.quic.streams.QuicBidiStream; import jdk.internal.net.quic.QuicVersion; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -52,7 +49,11 @@ import java.util.concurrent.TimeUnit; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /* * @test @@ -62,7 +63,7 @@ import static org.testng.Assert.*; * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * @build java.net.http/jdk.internal.net.http.Http3ConnectionAccess - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * -Djdk.httpclient.quic.maxStreamInitialData=16384 @@ -71,11 +72,11 @@ import static org.testng.Assert.*; public class H3MemoryHandlingTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private QuicStandaloneServer server; - private String requestURIBase; + private static QuicStandaloneServer server; + private static String requestURIBase; - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { server = QuicStandaloneServer.newBuilder() .availableVersions(new QuicVersion[]{QuicVersion.QUIC_V1}) .sslContext(sslContext) @@ -87,8 +88,8 @@ public class H3MemoryHandlingTest implements HttpServerAdapters { .port(server.getAddress().getPort()).build().toString(); } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { if (server != null) { System.out.println("Stopping server " + server.getAddress()); server.close(); @@ -125,19 +126,16 @@ public class H3MemoryHandlingTest implements HttpServerAdapters { serverAllWritesDone.complete(false); } }); - HttpClient client = getHttpClient(); - try { + try (HttpClient client = getHttpClient()) { HttpRequest request = getRequest(); final HttpResponse response1 = client.send( request, BodyHandlers.ofInputStream()); - assertEquals(response1.statusCode(), 200); + assertEquals(200, response1.statusCode()); assertFalse(errorCF.isDone(), "Expected the connection to be open"); assertFalse(serverAllWritesDone.isDone()); response1.body().close(); final boolean done = serverAllWritesDone.get(10, TimeUnit.SECONDS); assertFalse(done, "Too much data was buffered by the client"); - } finally { - client.close(); } } @@ -176,12 +174,12 @@ public class H3MemoryHandlingTest implements HttpServerAdapters { handlerCF.completeExceptionally(e); } }); - HttpClient client = getHttpClient(); - try { + + try (HttpClient client = getHttpClient()) { HttpRequest request = getRequest(); final HttpResponse response1 = client.send( - request, BodyHandlers.ofInputStream()); - assertEquals(response1.statusCode(), 200); + request, BodyHandlers.ofInputStream()); + assertEquals(200, response1.statusCode()); assertFalse(errorCF.isDone(), "Expected the connection to be open"); assertFalse(handlerCF.isDone()); assertTrue(writerBlocked.await(10, TimeUnit.SECONDS), @@ -191,10 +189,8 @@ public class H3MemoryHandlingTest implements HttpServerAdapters { try (InputStream body = response1.body()) { receivedResponse = body.readAllBytes(); } - assertEquals(receivedResponse.length, 32768, + assertEquals(32768, receivedResponse.length, "Unexpected response length"); - } finally { - client.close(); } assertTrue(handlerCF.get(10, TimeUnit.SECONDS), "Unexpected result"); diff --git a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java index 14149da7815..67ac821b6f7 100644 --- a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java +++ b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -29,7 +29,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError * -Djdk.httpclient.quic.minPtoBackoffTime=60 * -Djdk.httpclient.quic.maxPtoBackoffTime=90 * -Djdk.httpclient.quic.maxPtoBackoff=10 @@ -52,7 +52,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError * -Djdk.httpclient.quic.minPtoBackoffTime=45 * -Djdk.httpclient.quic.maxPtoBackoffTime=60 * -Djdk.httpclient.quic.maxPtoBackoff=9 @@ -75,7 +75,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError * -Djdk.httpclient.quic.idleTimeout=120 * -Djdk.httpclient.keepalive.timeout.h3=120 * -Djdk.test.server.quic.idleTimeout=90 @@ -100,7 +100,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError * -Djdk.httpclient.quic.idleTimeout=120 * -Djdk.httpclient.keepalive.timeout.h3=120 * -Djdk.test.server.quic.idleTimeout=90 @@ -161,14 +161,15 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.common.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static jdk.internal.net.http.Http3ClientProperties.MAX_STREAM_LIMIT_WAIT_TIMEOUT; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + public class H3MultipleConnectionsToSameHost implements HttpServerAdapters { static HttpTestServer httpsServer; static HttpClient client = null; @@ -223,11 +224,11 @@ public class H3MultipleConnectionsToSameHost implements HttpServerAdapters { } public static void main(String[] args) throws Exception { - test(); + new H3MultipleConnectionsToSameHost().test(); } @Test - public static void test() throws Exception { + public void test() throws Exception { try { long prestart = System.nanoTime(); initialize(); @@ -244,7 +245,7 @@ public class H3MultipleConnectionsToSameHost implements HttpServerAdapters { .GET().build(); long start = System.nanoTime(); var resp = client.send(request, BodyHandlers.ofByteArrayConsumer(b-> {})); - Assert.assertEquals(resp.statusCode(), 200); + Assertions.assertEquals(200, resp.statusCode()); long elapsed = System.nanoTime() - start; System.out.println("First request took: " + elapsed + " nanos (" + TimeUnit.NANOSECONDS.toMillis(elapsed) + " ms)"); final int max = property("simpleget.requests", 50); @@ -298,7 +299,7 @@ public class H3MultipleConnectionsToSameHost implements HttpServerAdapters { } } - list.forEach((cf) -> Assert.assertEquals(cf.join().statusCode(), 200)); + list.forEach((cf) -> Assertions.assertEquals(200, cf.join().statusCode())); client.close(); } catch (Throwable tt) { System.err.println("tt caught"); diff --git a/test/jdk/java/net/httpclient/http3/H3PushCancel.java b/test/jdk/java/net/httpclient/http3/H3PushCancel.java index 324942b67d0..b9e15b7d8e5 100644 --- a/test/jdk/java/net/httpclient/http3/H3PushCancel.java +++ b/test/jdk/java/net/httpclient/http3/H3PushCancel.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 @@ -25,7 +25,7 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,responses,trace * -Djdk.httpclient.http3.maxConcurrentPushStreams=5 @@ -69,18 +69,19 @@ import java.util.function.Function; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.common.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class H3PushCancel implements HttpServerAdapters { - static Map PUSH_PROMISES = Map.of( + static final Map PUSH_PROMISES = Map.of( "/x/y/z/1", "the first push promise body", "/x/y/z/2", "the second push promise body", "/x/y/z/3", "the third push promise body", @@ -93,13 +94,13 @@ public class H3PushCancel implements HttpServerAdapters { ); static final String MAIN_RESPONSE_BODY = "the main response body"; - HttpTestServer server; - URI uri; - URI headURI; - ServerPushHandler pushHandler; + private static HttpTestServer server; + private static URI uri; + private static URI headURI; + private static ServerPushHandler pushHandler; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { server = HttpTestServer.create(ANY, SimpleSSLContext.findSSLContext()); pushHandler = new ServerPushHandler(MAIN_RESPONSE_BODY, PUSH_PROMISES); server.addHandler(pushHandler, "/push/"); @@ -110,14 +111,14 @@ public class H3PushCancel implements HttpServerAdapters { headURI = new URI("https://" + server.serverAuthority() + "/head/x"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { server.stop(); } static HttpResponse assert200ResponseCode(HttpResponse response) { - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); return response; } @@ -125,8 +126,8 @@ public class H3PushCancel implements HttpServerAdapters { HttpRequest headRequest = HttpRequest.newBuilder(headURI) .HEAD().version(Version.HTTP_2).build(); var headResponse = client.send(headRequest, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), Version.HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(Version.HTTP_2, headResponse.version()); } @Test @@ -173,14 +174,14 @@ public class H3PushCancel implements HttpServerAdapters { promises.putIfAbsent(main.request(), CompletableFuture.completedFuture(main)); promises.forEach((request, value) -> { HttpResponse response = value.join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (PUSH_PROMISES.containsKey(request.uri().getPath())) { - assertEquals(response.body(), PUSH_PROMISES.get(request.uri().getPath())); + assertEquals(PUSH_PROMISES.get(request.uri().getPath()), response.body()); } else { - assertEquals(response.body(), MAIN_RESPONSE_BODY); + assertEquals(MAIN_RESPONSE_BODY, response.body()); } }); - assertEquals(promises.size(), Math.min(PUSH_PROMISES.size(), maxPushes) + 1); + assertEquals(Math.min(PUSH_PROMISES.size(), maxPushes) + 1, promises.size()); promises.clear(); } @@ -190,12 +191,12 @@ public class H3PushCancel implements HttpServerAdapters { client.sendAsync(HttpRequest.newBuilder(uri).build(), BodyHandlers.ofString()) .thenApply(H3PushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); } catch (CompletionException c) { throw new AssertionError(c.getCause()); } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // Send with no promise handler, but use pushId bigger than allowed. // This should cause the connection to get closed @@ -207,7 +208,7 @@ public class H3PushCancel implements HttpServerAdapters { client.sendAsync(bigger, BodyHandlers.ofString()) .thenApply(H3PushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); throw new AssertionError("Expected IOException not thrown"); } catch (CompletionException c) { @@ -226,7 +227,7 @@ public class H3PushCancel implements HttpServerAdapters { throw new AssertionError("Unexpected exception: " + c.getCause(), c.getCause()); } } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // the next time around we should have a new connection // so we can restart from scratch @@ -315,16 +316,16 @@ public class H3PushCancel implements HttpServerAdapters { promises.putIfAbsent(main.request(), CompletableFuture.completedFuture(main)); promises.forEach((request, value) -> { HttpResponse response = value.join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (PUSH_PROMISES.containsKey(request.uri().getPath())) { - assertEquals(response.body(), PUSH_PROMISES.get(request.uri().getPath())); + assertEquals(PUSH_PROMISES.get(request.uri().getPath()), response.body()); } else { - assertEquals(response.body(), MAIN_RESPONSE_BODY); + assertEquals(MAIN_RESPONSE_BODY, response.body()); } }); int expectedPushes = Math.min(PUSH_PROMISES.size(), maxPushes) + 1; if (i == 0) expectedPushes--; // pushId == 1 was cancelled - assertEquals(promises.size(), expectedPushes); + assertEquals(expectedPushes, promises.size()); promises.clear(); } @@ -334,12 +335,12 @@ public class H3PushCancel implements HttpServerAdapters { client.sendAsync(HttpRequest.newBuilder(uri).build(), BodyHandlers.ofString()) .thenApply(H3PushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); } catch (CompletionException c) { throw new AssertionError(c.getCause()); } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // Send with no promise handler, but use pushId bigger than allowed. // This should cause the connection to get closed @@ -351,7 +352,7 @@ public class H3PushCancel implements HttpServerAdapters { client.sendAsync(bigger, BodyHandlers.ofString()) .thenApply(H3PushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body ->assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body ->assertEquals(MAIN_RESPONSE_BODY, body)) .join(); throw new AssertionError("Expected IOException not thrown"); } catch (CompletionException c) { @@ -370,7 +371,7 @@ public class H3PushCancel implements HttpServerAdapters { throw new AssertionError("Unexpected exception: " + c.getCause(), c.getCause()); } } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // the next time around we should have a new connection // so we can restart from scratch @@ -379,7 +380,7 @@ public class H3PushCancel implements HttpServerAdapters { errors.forEach(t -> t.printStackTrace(System.out)); var error = errors.stream().findFirst().orElse(null); if (error != null) throw error; - assertEquals(notified.size(), 0, "Unexpected notification: " + notified); + assertEquals(0, notified.size(), "Unexpected notification: " + notified); } } diff --git a/test/jdk/java/net/httpclient/http3/H3RedirectTest.java b/test/jdk/java/net/httpclient/http3/H3RedirectTest.java index 0a5546bc1c1..67a7d99fa71 100644 --- a/test/jdk/java/net/httpclient/http3/H3RedirectTest.java +++ b/test/jdk/java/net/httpclient/http3/H3RedirectTest.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,7 +28,7 @@ * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.common.HttpServerAdapters * @compile ../ReferenceTracker.java - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=frames,ssl,requests,responses,errors * -Djdk.internal.httpclient.debug=true * H3RedirectTest @@ -50,11 +50,12 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.Test; + public class H3RedirectTest implements HttpServerAdapters { static int httpPort; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); @@ -135,7 +136,7 @@ public class H3RedirectTest implements HttpServerAdapters { } @Test - public static void test() throws Exception { + public void test() throws Exception { try { initialize(); simpleTest(); diff --git a/test/jdk/java/net/httpclient/http3/H3ServerPush.java b/test/jdk/java/net/httpclient/http3/H3ServerPush.java index 83f68a15579..50aa817b155 100644 --- a/test/jdk/java/net/httpclient/http3/H3ServerPush.java +++ b/test/jdk/java/net/httpclient/http3/H3ServerPush.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 @@ -29,7 +29,7 @@ * jdk.httpclient.test.lib.http2.PushHandler * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm/timeout=960 + * @run junit/othervm/timeout=960 * -Djdk.httpclient.HttpClient.log=errors,requests,headers * -Djdk.internal.httpclient.debug=false * H3ServerPush @@ -66,13 +66,14 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.PushHandler; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.nio.charset.StandardCharsets.UTF_8; import static jdk.test.lib.Utils.createTempFileOfSize; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class H3ServerPush implements HttpServerAdapters { @@ -81,14 +82,14 @@ public class H3ServerPush implements HttpServerAdapters { static final int LOOPS = 13; static final int FILE_SIZE = 512 * 1024 + 343; - static Path tempFile; + private static Path tempFile; - HttpTestServer server; - URI uri; - URI headURI; + private static HttpTestServer server; + private static URI uri; + private static URI headURI; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { tempFile = createTempFileOfSize(CLASS_NAME, ".dat", FILE_SIZE); var sslContext = SimpleSSLContext.findSSLContext(); var h2Server = new Http2TestServer(true, sslContext); @@ -109,12 +110,12 @@ public class H3ServerPush implements HttpServerAdapters { HttpRequest headRequest = HttpRequest.newBuilder(headURI) .HEAD().version(Version.HTTP_2).build(); var headResponse = client.send(headRequest, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), Version.HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(Version.HTTP_2, headResponse.version()); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { server.stop(); } @@ -144,7 +145,7 @@ public class H3ServerPush implements HttpServerAdapters { resultMap.put(request, cf); System.out.println("waiting for response"); var resp = cf.join(); - assertEquals(resp.version(), Version.HTTP_3); + assertEquals(Version.HTTP_3, resp.version()); var seen = new HashSet<>(); resultMap.forEach((k, v) -> { if (seen.add(k)) { @@ -158,16 +159,16 @@ public class H3ServerPush implements HttpServerAdapters { for (HttpRequest r : resultMap.keySet()) { System.out.println("Checking " + r); HttpResponse response = resultMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); - assertEquals(response.body(), tempFileAsString); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); + assertEquals(tempFileAsString, response.body()); } resultMap.forEach((k, v) -> { if (seen.add(k)) { System.out.println("Got " + v.join()); } }); - assertEquals(resultMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultMap.size()); } } @@ -194,11 +195,11 @@ public class H3ServerPush implements HttpServerAdapters { System.err.println("results.size: " + resultMap.size()); for (HttpRequest r : resultMap.keySet()) { HttpResponse response = resultMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); - assertEquals(response.body(), tempFileAsString); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); + assertEquals(tempFileAsString, response.body()); } - assertEquals(resultMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultMap.size()); } } @@ -242,12 +243,12 @@ public class H3ServerPush implements HttpServerAdapters { resultsMap.put(request, cf); for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); String fileAsString = Files.readString(response.body()); - assertEquals(fileAsString, tempFileAsString); + assertEquals(tempFileAsString, fileAsString); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } } @@ -274,12 +275,12 @@ public class H3ServerPush implements HttpServerAdapters { resultsMap.put(request, cf); for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); String fileAsString = Files.readString(response.body()); - assertEquals(fileAsString, tempFileAsString); + assertEquals(tempFileAsString, fileAsString); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } } @@ -340,13 +341,13 @@ public class H3ServerPush implements HttpServerAdapters { resultsMap.put(request, cf); for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes(); String result = new String(ba, UTF_8); - assertEquals(result, tempFileAsString); + assertEquals(tempFileAsString, result); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } } @@ -384,13 +385,13 @@ public class H3ServerPush implements HttpServerAdapters { resultsMap.put(request, cf); for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), Version.HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(Version.HTTP_3, response.version()); byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes(); String result = new String(ba, UTF_8); - assertEquals(result, tempFileAsString); + assertEquals(tempFileAsString, result); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } } } diff --git a/test/jdk/java/net/httpclient/http3/H3ServerPushCancel.java b/test/jdk/java/net/httpclient/http3/H3ServerPushCancel.java index 312b9ac507d..b34e2a1567e 100644 --- a/test/jdk/java/net/httpclient/http3/H3ServerPushCancel.java +++ b/test/jdk/java/net/httpclient/http3/H3ServerPushCancel.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 @@ -25,7 +25,7 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,responses,trace * -Djdk.httpclient.http3.maxConcurrentPushStreams=45 @@ -73,9 +73,6 @@ import java.util.function.Supplier; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.common.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; @@ -83,10 +80,14 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class H3ServerPushCancel implements HttpServerAdapters { @@ -95,7 +96,7 @@ public class H3ServerPushCancel implements HttpServerAdapters { static final PrintStream err = System.err; static final PrintStream out = System.out; - static Map PUSH_PROMISES = Map.of( + static final Map PUSH_PROMISES = Map.of( "/x/y/z/1", "the first push promise body", "/x/y/z/2", "the second push promise body", "/x/y/z/3", "the third push promise body", @@ -109,13 +110,13 @@ public class H3ServerPushCancel implements HttpServerAdapters { static final String MAIN_RESPONSE_BODY = "the main response body"; static final int REQUESTS = 5; - HttpTestServer server; - URI uri; - URI headURI; - ServerPushHandler pushHandler; + private static HttpTestServer server; + private static URI uri; + private static URI headURI; + private static ServerPushHandler pushHandler; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { server = HttpTestServer.create(ANY, SimpleSSLContext.findSSLContext()); pushHandler = new ServerPushHandler(MAIN_RESPONSE_BODY, PUSH_PROMISES); server.addHandler(pushHandler, "/push/"); @@ -126,14 +127,14 @@ public class H3ServerPushCancel implements HttpServerAdapters { headURI = new URI("https://" + server.serverAuthority() + "/head/x"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { server.stop(); } static HttpResponse assert200ResponseCode(HttpResponse response) { - assertEquals(response.statusCode(), 200); - assertEquals(response.version(), HTTP_3); + assertEquals(200, response.statusCode()); + assertEquals(HTTP_3, response.version()); return response; } @@ -141,8 +142,8 @@ public class H3ServerPushCancel implements HttpServerAdapters { HttpRequest headRequest = HttpRequest.newBuilder(headURI) .HEAD().version(HTTP_2).build(); var headResponse = client.send(headRequest, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(HTTP_2, headResponse.version()); } static final class TestPushPromiseHandler implements PushPromiseHandler { @@ -283,14 +284,14 @@ public class H3ServerPushCancel implements HttpServerAdapters { throw new AssertionError("Unexpected message: " + msg, ex); } } else { - assertEquals(join(value).body(), PUSH_PROMISES.get(request.uri().getPath())); + assertEquals(PUSH_PROMISES.get(request.uri().getPath()), join(value).body()); } expectedPushIds.add(pushId); - } else assertEquals(pushId.getClass(), Http3PushId.class); + } else assertEquals(Http3PushId.class, pushId.getClass()); } else { HttpResponse response = join(value); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), MAIN_RESPONSE_BODY); + assertEquals(200, response.statusCode()); + assertEquals(MAIN_RESPONSE_BODY, response.body()); } }); @@ -311,12 +312,12 @@ public class H3ServerPushCancel implements HttpServerAdapters { client.sendAsync(HttpRequest.newBuilder(uri).build(), BodyHandlers.ofString()) .thenApply(H3ServerPushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); } catch (CompletionException c) { throw new AssertionError(c.getCause()); } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // Send with no promise handler, but use pushId bigger than allowed. // This should cause the connection to get closed @@ -328,7 +329,7 @@ public class H3ServerPushCancel implements HttpServerAdapters { client.sendAsync(bigger, BodyHandlers.ofString()) .thenApply(H3ServerPushCancel::assert200ResponseCode) .thenApply(HttpResponse::body) - .thenAccept(body -> assertEquals(body, MAIN_RESPONSE_BODY)) + .thenAccept(body -> assertEquals(MAIN_RESPONSE_BODY, body)) .join(); throw new AssertionError("Expected IOException not thrown"); } catch (CompletionException c) { @@ -347,7 +348,7 @@ public class H3ServerPushCancel implements HttpServerAdapters { throw new AssertionError("Unexpected exception: " + c.getCause(), c.getCause()); } } - assertEquals(promises.size(), 0); + assertEquals(0, promises.size()); // the next time around we should have a new connection, // so we can restart from scratch @@ -408,7 +409,7 @@ public class H3ServerPushCancel implements HttpServerAdapters { // excluding those that got cancelled, // we should have received REQUEST-1 notifications // per push promise and per connection - assertEquals(count, (PUSH_PROMISES.size()-3)*2*(REQUESTS-1), + assertEquals((PUSH_PROMISES.size()-3)*2*(REQUESTS-1), count, "Unexpected notification: " + notified); } } diff --git a/test/jdk/java/net/httpclient/http3/H3ServerPushWithDiffTypes.java b/test/jdk/java/net/httpclient/http3/H3ServerPushWithDiffTypes.java index d00012826ef..a863db43c29 100644 --- a/test/jdk/java/net/httpclient/http3/H3ServerPushWithDiffTypes.java +++ b/test/jdk/java/net/httpclient/http3/H3ServerPushWithDiffTypes.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 @@ -25,7 +25,7 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,responses * H3ServerPushWithDiffTypes @@ -65,11 +65,12 @@ import java.util.function.BiPredicate; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.Test; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; public class H3ServerPushWithDiffTypes implements HttpServerAdapters { @@ -89,8 +90,8 @@ public class H3ServerPushWithDiffTypes implements HttpServerAdapters { HttpRequest headRequest = HttpRequest.newBuilder(headURI) .HEAD().version(Version.HTTP_2).build(); var headResponse = client.send(headRequest, BodyHandlers.ofString()); - assertEquals(headResponse.statusCode(), 200); - assertEquals(headResponse.version(), Version.HTTP_2); + assertEquals(200, headResponse.statusCode()); + assertEquals(Version.HTTP_2, headResponse.version()); } @Test @@ -127,13 +128,13 @@ public class H3ServerPushWithDiffTypes implements HttpServerAdapters { results.put(request, cf); cf.join(); - assertEquals(results.size(), PUSH_PROMISES.size() + 1); + assertEquals(PUSH_PROMISES.size() + 1, results.size()); for (HttpRequest r : results.keySet()) { URI u = r.uri(); var resp = results.get(r).get(); - assertEquals(resp.statusCode(), 200); - assertEquals(resp.version(), Version.HTTP_3); + assertEquals(200, resp.statusCode()); + assertEquals(Version.HTTP_3, resp.version()); BodyAndType body = resp.body(); String result; // convert all body types to String for easier comparison @@ -153,7 +154,7 @@ public class H3ServerPushWithDiffTypes implements HttpServerAdapters { String expected = PUSH_PROMISES.get(r.uri().getPath()); if (expected == null) expected = "the main response body"; - assertEquals(result, expected); + assertEquals(expected, result); } } } diff --git a/test/jdk/java/net/httpclient/http3/H3SimpleGet.java b/test/jdk/java/net/httpclient/http3/H3SimpleGet.java index 3745c32afbf..ae113322cd3 100644 --- a/test/jdk/java/net/httpclient/http3/H3SimpleGet.java +++ b/test/jdk/java/net/httpclient/http3/H3SimpleGet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -30,14 +30,14 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.httpclient.retryOnStreamlimit=20 * -Djdk.httpclient.redirects.retrylimit=21 * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Dsimpleget.requests=150 * -Dsimpleget.chunks=16384 * -Djdk.httpclient.retryOnStreamlimit=5 @@ -53,14 +53,14 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.httpclient.retryOnStreamlimit=20 * -Djdk.httpclient.redirects.retrylimit=21 * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Dsimpleget.requests=150 * -Dsimpleget.chunks=16384 * -Djdk.httpclient.retryOnStreamlimit=5 @@ -77,16 +77,16 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations * -Djdk.httpclient.retryOnStreamlimit=20 * -Djdk.httpclient.redirects.retrylimit=21 * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations * -Dsimpleget.requests=150 * -Dsimpleget.chunks=16384 @@ -103,16 +103,16 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.internal.httpclient.quic.useNioSelector=true * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.internal.httpclient.quic.useNioSelector=true * -Djdk.httpclient.retryOnStreamlimit=20 * -Djdk.httpclient.redirects.retrylimit=21 * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.internal.httpclient.quic.useNioSelector=true * -Dsimpleget.requests=150 * -Dsimpleget.chunks=16384 @@ -129,16 +129,16 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.internal.httpclient.quic.useNioSelector=true * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.internal.httpclient.quic.useNioSelector=true * -Djdk.httpclient.retryOnStreamlimit=20 * -Djdk.httpclient.redirects.retrylimit=21 * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 * H3SimpleGet - * @run testng/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError + * @run junit/othervm/timeout=480 -XX:+HeapDumpOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError * -Djdk.internal.httpclient.quic.useNioSelector=true * -Dsimpleget.requests=150 * -Dsimpleget.chunks=16384 @@ -154,7 +154,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=480 -Djdk.internal.httpclient.quic.congestionController=reno + * @run junit/othervm/timeout=480 -Djdk.internal.httpclient.quic.congestionController=reno * H3SimpleGet * @summary send multiple GET requests using Reno congestion controller */ @@ -198,13 +198,14 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + public class H3SimpleGet implements HttpServerAdapters { static HttpTestServer httpsServer; static HttpClient client = null; @@ -261,13 +262,13 @@ public class H3SimpleGet implements HttpServerAdapters { } public static void main(String[] args) throws Exception { - test(); + new H3SimpleGet().test(); } static volatile boolean waitBeforeTest = false; @Test - public static void test() throws Exception { + public void test() throws Exception { try { if (waitBeforeTest) { Thread.sleep(20000); @@ -283,7 +284,7 @@ public class H3SimpleGet implements HttpServerAdapters { .GET().build(); long start = System.nanoTime(); var resp = client.send(request, BodyHandlers.ofByteArrayConsumer(b-> {})); - Assert.assertEquals(resp.statusCode(), 200); + Assertions.assertEquals(200, resp.statusCode()); long elapsed = System.nanoTime() - start; System.out.println("Stat: First request took: " + elapsed + " nanos (" + TimeUnit.NANOSECONDS.toMillis(elapsed) + " ms)"); @@ -314,7 +315,7 @@ public class H3SimpleGet implements HttpServerAdapters { + connections.size() + " connections"); } } - list.forEach((cf) -> Assert.assertEquals(cf.join().statusCode(), 200)); + list.forEach((cf) -> Assertions.assertEquals(200, cf.join().statusCode())); } catch (Throwable tt) { System.err.println("tt caught"); tt.printStackTrace(); diff --git a/test/jdk/java/net/httpclient/http3/H3SimplePost.java b/test/jdk/java/net/httpclient/http3/H3SimplePost.java index 4cf46988873..0294f2f69da 100644 --- a/test/jdk/java/net/httpclient/http3/H3SimplePost.java +++ b/test/jdk/java/net/httpclient/http3/H3SimplePost.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -27,7 +27,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=480 H3SimplePost + * @run junit/othervm/timeout=480 H3SimplePost */ // -Djdk.httpclient.HttpClient.log=requests,errors,quic // -Djdk.httpclient.quic.defaultMTU=64000 @@ -37,8 +37,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -61,6 +59,9 @@ import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + public class H3SimplePost implements HttpServerAdapters { static HttpTestServer httpsServer; static HttpClient client = null; @@ -115,11 +116,11 @@ public class H3SimplePost implements HttpServerAdapters { } public static void main(String[] args) throws Exception { - test(); + new H3SimplePost().test(); } @Test - public static void test() throws Exception { + public void test() throws Exception { try { long prestart = System.nanoTime(); initialize(); @@ -140,7 +141,7 @@ public class H3SimplePost implements HttpServerAdapters { .build(); long start = System.nanoTime(); var resp = client.send(getRequest, BodyHandlers.ofByteArrayConsumer(b-> {})); - Assert.assertEquals(resp.statusCode(), 200); + Assertions.assertEquals(200, resp.statusCode()); long elapsed = System.nanoTime() - start; System.out.println("First GET request took: " + elapsed + " nanos (" + TimeUnit.NANOSECONDS.toMillis(elapsed) + " ms)"); final int max = 50; @@ -155,7 +156,7 @@ public class H3SimplePost implements HttpServerAdapters { System.out.println("Next " + max + " POST requests took: " + elapsed2 + " nanos (" + TimeUnit.NANOSECONDS.toMillis(elapsed2) + "ms for " + max + " requests): " + elapsed2 / max + " nanos per request (" + TimeUnit.NANOSECONDS.toMillis(elapsed2) / max + " ms)"); - list.forEach((cf) -> Assert.assertEquals(cf.join().statusCode(), 200)); + list.forEach((cf) -> Assertions.assertEquals(200, cf.join().statusCode())); } catch (Throwable tt) { System.err.println("tt caught"); tt.printStackTrace(); diff --git a/test/jdk/java/net/httpclient/http3/H3SimpleTest.java b/test/jdk/java/net/httpclient/http3/H3SimpleTest.java index 1880d436501..4258f3bac73 100644 --- a/test/jdk/java/net/httpclient/http3/H3SimpleTest.java +++ b/test/jdk/java/net/httpclient/http3/H3SimpleTest.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 @@ -32,16 +32,17 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + /* * @test * @summary Basic test to verify that simple GET/POST/HEAD @@ -50,21 +51,21 @@ import static java.net.http.HttpOption.H3_DISCOVERY; * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * H3SimpleTest - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * -Djava.net.preferIPv6Addresses=true * H3SimpleTest - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * -Djava.net.preferIPv4Stack=true * H3SimpleTest - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors * -Djdk.internal.httpclient.quic.congestionController=reno @@ -74,11 +75,11 @@ import static java.net.http.HttpOption.H3_DISCOVERY; public class H3SimpleTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer h3Server; - private String requestURI; + private static HttpTestServer h3Server; + private static String requestURI; - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { // create an H3 only server h3Server = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); h3Server.addHandler((exchange) -> exchange.sendResponseHeaders(200, 0), "/hello"); @@ -87,8 +88,8 @@ public class H3SimpleTest implements HttpServerAdapters { requestURI = "https://" + h3Server.serverAuthority() + "/hello"; } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { if (h3Server != null) { System.out.println("Stopping server " + h3Server.getAddress()); h3Server.stop(); @@ -113,18 +114,18 @@ public class H3SimpleTest implements HttpServerAdapters { final HttpRequest req1 = reqBuilder.copy().GET().build(); System.out.println("Issuing request: " + req1); final HttpResponse resp1 = client.send(req1, BodyHandlers.discarding()); - Assert.assertEquals(resp1.statusCode(), 200, "unexpected response code for GET request"); + Assertions.assertEquals(200, resp1.statusCode(), "unexpected response code for GET request"); // POST final HttpRequest req2 = reqBuilder.copy().POST(BodyPublishers.ofString("foo")).build(); System.out.println("Issuing request: " + req2); final HttpResponse resp2 = client.send(req2, BodyHandlers.discarding()); - Assert.assertEquals(resp2.statusCode(), 200, "unexpected response code for POST request"); + Assertions.assertEquals(200, resp2.statusCode(), "unexpected response code for POST request"); // HEAD final HttpRequest req3 = reqBuilder.copy().HEAD().build(); System.out.println("Issuing request: " + req3); final HttpResponse resp3 = client.send(req3, BodyHandlers.discarding()); - Assert.assertEquals(resp3.statusCode(), 200, "unexpected response code for HEAD request"); + Assertions.assertEquals(200, resp3.statusCode(), "unexpected response code for HEAD request"); } } diff --git a/test/jdk/java/net/httpclient/http3/H3StopSendingTest.java b/test/jdk/java/net/httpclient/http3/H3StopSendingTest.java index 5b017c15d9b..86b12a1dae6 100644 --- a/test/jdk/java/net/httpclient/http3/H3StopSendingTest.java +++ b/test/jdk/java/net/httpclient/http3/H3StopSendingTest.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 @@ -26,7 +26,7 @@ * @summary Verifies that the client reacts correctly to the receipt of a STOP_SENDING frame. * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm/timeout=40 -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=trace,errors,headers + * @run junit/othervm/timeout=40 -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=trace,errors,headers * H3StopSendingTest */ @@ -35,9 +35,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; import jdk.test.lib.net.SimpleSSLContext; import jdk.internal.net.http.http3.Http3Error; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -53,14 +50,18 @@ import java.util.concurrent.ExecutionException; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class H3StopSendingTest { - HttpTestServer h3TestServer; - HttpRequest postRequestNoError, postRequestError; - HttpRequest postRequestNoErrorWithData, postRequestErrorWithData; - URI h3TestServerUriNoError, h3TestServerUriError; + private static HttpTestServer h3TestServer; + private static HttpRequest postRequestNoError, postRequestError; + private static HttpRequest postRequestNoErrorWithData, postRequestErrorWithData; + private static URI h3TestServerUriNoError, h3TestServerUriError; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); static final String TEST_ROOT_PATH = "/h3_stop_sending_test"; @@ -81,13 +82,13 @@ public class H3StopSendingTest { err.println(resp.headers()); err.println(resp.body()); err.println(resp.statusCode()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); resp = client.sendAsync(postRequestNoErrorWithData, HttpResponse.BodyHandlers.ofString()).get(); err.println(resp.headers()); err.println(resp.body()); err.println(resp.statusCode()); - assertEquals(resp.statusCode(), 200); - assertEquals(resp.body(), RESPONSE_MESSAGE.repeat(MESSAGE_REPEAT)); + assertEquals(200, resp.statusCode()); + assertEquals(RESPONSE_MESSAGE.repeat(MESSAGE_REPEAT), resp.body()); } } @@ -125,8 +126,8 @@ public class H3StopSendingTest { } } - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { h3TestServer = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); h3TestServer.addHandler(new ServerRequestStopSendingHandler(), TEST_ROOT_PATH); @@ -162,8 +163,8 @@ public class H3StopSendingTest { .build(); } - @AfterTest - public void afterTest() { + @AfterAll + public static void afterTest() { h3TestServer.stop(); } diff --git a/test/jdk/java/net/httpclient/http3/H3StreamLimitReachedTest.java b/test/jdk/java/net/httpclient/http3/H3StreamLimitReachedTest.java index a4524b9ae5e..1ac4a750d77 100644 --- a/test/jdk/java/net/httpclient/http3/H3StreamLimitReachedTest.java +++ b/test/jdk/java/net/httpclient/http3/H3StreamLimitReachedTest.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 @@ -46,7 +46,7 @@ * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:control + * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:control * -Djdk.internal.httpclient.debug=false * -Djdk.internal.httpclient.quic.maxBidiStreams=1 * H3StreamLimitReachedTest @@ -77,7 +77,7 @@ * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:control + * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:control * -Djdk.internal.httpclient.debug=false * -Djdk.internal.httpclient.quic.maxBidiStreams=1 * -Djdk.httpclient.http3.maxStreamLimitTimeout=0 @@ -114,7 +114,6 @@ import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; @@ -125,7 +124,9 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static jdk.test.lib.Asserts.assertEquals; import static jdk.test.lib.Asserts.assertNotEquals; import static jdk.test.lib.Asserts.assertTrue; -import static org.testng.Assert.assertFalse; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import org.junit.jupiter.api.Test; public class H3StreamLimitReachedTest implements HttpServerAdapters { @@ -329,7 +330,7 @@ public class H3StreamLimitReachedTest implements HttpServerAdapters { } @Test - public static void testH3Only() throws Exception { + public void testH3Only() throws Exception { System.out.println("\nTesting HTTP/3 only"); initialize(true); try (HttpClient client = getClient()) { @@ -402,12 +403,12 @@ public class H3StreamLimitReachedTest implements HttpServerAdapters { } @Test - public static void testH2H3WithTwoAltSVC() throws Exception { + public void testH2H3WithTwoAltSVC() throws Exception { testH2H3(false); } @Test - public static void testH2H3WithAltSVCOnSamePort() throws Exception { + public void testH2H3WithAltSVCOnSamePort() throws Exception { testH2H3(true); } @@ -627,12 +628,12 @@ public class H3StreamLimitReachedTest implements HttpServerAdapters { } @Test - public static void testParallelH2H3WithTwoAltSVC() throws Exception { + public void testParallelH2H3WithTwoAltSVC() throws Exception { testH2H3Concurrent(false); } @Test - public static void testParallelH2H3WithAltSVCOnSamePort() throws Exception { + public void testParallelH2H3WithAltSVCOnSamePort() throws Exception { testH2H3Concurrent(true); } diff --git a/test/jdk/java/net/httpclient/http3/HTTP3NoBodyTest.java b/test/jdk/java/net/httpclient/http3/HTTP3NoBodyTest.java index 601e40c7dc6..b43ce8dcf79 100644 --- a/test/jdk/java/net/httpclient/http3/HTTP3NoBodyTest.java +++ b/test/jdk/java/net/httpclient/http3/HTTP3NoBodyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -29,7 +29,7 @@ * jdk.httpclient.test.lib.http3.Http3TestServer * jdk.httpclient.test.lib.common.HttpServerAdapters * @compile ../ReferenceTracker.java - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors + * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors * -Djdk.internal.httpclient.debug=true * HTTP3NoBodyTest * @summary this is a copy of http2/NoBodyTest over HTTP/3 @@ -38,8 +38,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.*; -import javax.net.ssl.*; +import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpHeaders; import java.net.http.HttpRequest; @@ -48,9 +47,12 @@ import java.net.http.HttpOption.Http3DiscoveryMode; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.util.Random; -import java.util.concurrent.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; +import javax.net.ssl.SSLContext; + import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; @@ -58,7 +60,6 @@ import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.RandomFactory; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; @@ -66,7 +67,8 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; -@Test +import org.junit.jupiter.api.Test; + public class HTTP3NoBodyTest { private static final Random RANDOM = RandomFactory.getRandom(); @@ -119,7 +121,7 @@ public class HTTP3NoBodyTest { } @Test - public static void runtest() throws Exception { + public void runtest() throws Exception { try { initialize(); warmup(false); diff --git a/test/jdk/java/net/httpclient/http3/Http3ExpectContinueTest.java b/test/jdk/java/net/httpclient/http3/Http3ExpectContinueTest.java index 53ce6a68d38..1cf6900ed5d 100644 --- a/test/jdk/java/net/httpclient/http3/Http3ExpectContinueTest.java +++ b/test/jdk/java/net/httpclient/http3/Http3ExpectContinueTest.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 @@ -27,7 +27,7 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @compile ../ReferenceTracker.java * @build jdk.httpclient.test.lib.common.HttpServerAdapters - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,headers * Http3ExpectContinueTest */ @@ -36,11 +36,6 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http3.Http3TestServer; import jdk.httpclient.test.lib.quic.QuicServer; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.TestException; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -60,15 +55,20 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import static java.net.http.HttpClient.Version.HTTP_3; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class Http3ExpectContinueTest implements HttpServerAdapters { - ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; - Http3TestServer http3TestServer; + private static Http3TestServer http3TestServer; - URI h3postUri, h3forcePostUri, h3hangUri; + private static URI h3postUri, h3forcePostUri, h3hangUri; static PrintStream err = new PrintStream(System.err); static PrintStream out = new PrintStream(System.out); @@ -78,8 +78,7 @@ public class Http3ExpectContinueTest implements HttpServerAdapters { static final String BODY = "Post body"; private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - @DataProvider(name = "uris") - public Object[][] urisData() { + public static Object[][] urisData() { return new Object[][]{ // URI, Expected Status Code, Will finish with Exception { h3postUri, 200, false }, @@ -88,7 +87,8 @@ public class Http3ExpectContinueTest implements HttpServerAdapters { }; } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("urisData") public void test(URI uri, int expectedStatusCode, boolean exceptionally) throws CancellationException, InterruptedException, ExecutionException, IOException { @@ -149,8 +149,8 @@ public class Http3ExpectContinueTest implements HttpServerAdapters { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { final QuicServer quicServer = Http3TestServer.quicServerBuilder() .sslContext(sslContext) .build(); @@ -167,8 +167,8 @@ public class Http3ExpectContinueTest implements HttpServerAdapters { http3TestServer.start(); } - @AfterTest - public void teardown() throws IOException { + @AfterAll + public static void teardown() throws IOException { var error = TRACKER.check(500); if (error != null) throw error; http3TestServer.stop(); @@ -233,11 +233,11 @@ public class Http3ExpectContinueTest implements HttpServerAdapters { } if (exceptionally && testThrowable != null) { err.println("Finished exceptionally Test throwable: " + testThrowable); - assertEquals(IOException.class, testThrowable.getClass()); + assertEquals(testThrowable.getClass(), IOException.class); } else if (exceptionally) { - throw new TestException("Expected case to finish with an IOException but testException is null"); + fail("Expected case to finish with an IOException but testException is null"); } else if (resp != null) { - assertEquals(resp.statusCode(), expectedStatusCode); + assertEquals(expectedStatusCode, resp.statusCode()); err.println("Request completed successfully for path " + path); err.println("Response Headers: " + resp.headers()); err.println("Response Status Code: " + resp.statusCode()); diff --git a/test/jdk/java/net/httpclient/http3/PeerUniStreamDispatcherTest.java b/test/jdk/java/net/httpclient/http3/PeerUniStreamDispatcherTest.java index be1b8304bf1..fc6d43bb472 100644 --- a/test/jdk/java/net/httpclient/http3/PeerUniStreamDispatcherTest.java +++ b/test/jdk/java/net/httpclient/http3/PeerUniStreamDispatcherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -23,7 +23,7 @@ /* * @test - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=out * PeerUniStreamDispatcherTest * @summary Unit test for the PeerUniStreamDispatcher @@ -45,8 +45,8 @@ import jdk.internal.net.http.quic.streams.QuicReceiverStream; import jdk.internal.net.http.quic.streams.QuicStreamReader; import jdk.internal.net.http.quic.streams.QuicStreams; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class PeerUniStreamDispatcherTest { @@ -301,7 +301,7 @@ public class PeerUniStreamDispatcherTest { assertTrue(reader.connected()); int size = VariableLengthEncoder.getEncodedSize(code); ByteBuffer buffer = ByteBuffer.allocate(size); - assertEquals(buffer.remaining(), size); + assertEquals(size, buffer.remaining()); VariableLengthEncoder.encode(buffer, code); buffer.flip(); stream.buffers.add(buffer); @@ -313,7 +313,7 @@ public class PeerUniStreamDispatcherTest { // will loop correctly. size = VariableLengthEncoder.getEncodedSize(1L << 62 - 5); ByteBuffer buffer2 = ByteBuffer.allocate(size); - assertEquals(buffer2.remaining(), size); + assertEquals(size, buffer2.remaining()); VariableLengthEncoder.encode(buffer2, 1L << 62 - 5); buffer2.flip(); stream.buffers.add(ByteBuffer.wrap(new byte[] {buffer2.get()})); @@ -328,7 +328,7 @@ public class PeerUniStreamDispatcherTest { assertFalse(reader.connected()); assertFalse(dispatcher.dispatched.isEmpty()); assertTrue(stream.buffers.isEmpty()); - assertEquals(dispatcher.dispatched.size(), 1); + assertEquals(1, dispatcher.dispatched.size()); var dispatched = dispatcher.dispatched.get(0); checkDispatched(type, code, stream, dispatched); } @@ -343,30 +343,30 @@ public class PeerUniStreamDispatcherTest { case RESERVED -> DispatchedStream.ReservedStream.class; case UNKNOWN -> DispatchedStream.UnknownStream.class; }; - assertEquals(dispatched.getClass(), streamClass, + assertEquals(streamClass, dispatched.getClass(), "unexpected dispatched class " + dispatched + " for " + type); if (dispatched instanceof DispatchedStream.StandardStream st) { System.out.println("Got expected stream: " + st); - assertEquals(st.type(), type); - assertEquals(st.stream, stream); + assertEquals(type, st.type()); + assertEquals(stream, st.stream); } else if (dispatched instanceof DispatchedStream.ReservedStream res) { System.out.println("Got expected stream: " + res); - assertEquals(res.type(), type); - assertEquals(res.stream, stream); - assertEquals(res.code(), code); + assertEquals(type, res.type()); + assertEquals(stream, res.stream); + assertEquals(code, res.code()); assertTrue(Http3Streams.isReserved(res.code())); } else if (dispatched instanceof DispatchedStream.UnknownStream unk) { System.out.println("Got expected stream: " + unk); - assertEquals(unk.type(), type); - assertEquals(unk.stream, stream); - assertEquals(unk.code(), code); + assertEquals(type, unk.type()); + assertEquals(stream, unk.stream); + assertEquals(code, unk.code()); assertFalse(Http3Streams.isReserved(unk.code())); } else if (dispatched instanceof DispatchedStream.PushStream push) { System.out.println("Got expected stream: " + push); - assertEquals(push.type(), type); - assertEquals(push.stream, stream); - assertEquals(push.pushId, 1L << 62 - 5); - assertEquals(push.type(), DISPATCHED_STREAM.PUSH); + assertEquals(type, push.type()); + assertEquals(stream, push.stream); + assertEquals(1L << 62 - 5, push.pushId); + assertEquals(DISPATCHED_STREAM.PUSH, push.type()); } } @@ -406,9 +406,9 @@ public class PeerUniStreamDispatcherTest { SequentialScheduler scheduler = stream.scheduler; assertTrue(reader.connected()); int size = VariableLengthEncoder.getEncodedSize(code); - assertEquals(size, 8); + assertEquals(8, size); ByteBuffer buffer = ByteBuffer.allocate(size); - assertEquals(buffer.remaining(), size); + assertEquals(size, buffer.remaining()); VariableLengthEncoder.encode(buffer, code); buffer.flip(); dispatcher.start(); @@ -428,7 +428,7 @@ public class PeerUniStreamDispatcherTest { assertFalse(reader.connected()); assertFalse(dispatcher.dispatched.isEmpty()); assertTrue(stream.buffers.isEmpty()); - assertEquals(dispatcher.dispatched.size(), 1); + assertEquals(1, dispatcher.dispatched.size()); var dispatched = dispatcher.dispatched.get(0); checkDispatched(type, code, stream, dispatched); } diff --git a/test/jdk/java/net/httpclient/http3/StopSendingTest.java b/test/jdk/java/net/httpclient/http3/StopSendingTest.java index ff1b8db7bc8..8c9a6f84b65 100644 --- a/test/jdk/java/net/httpclient/http3/StopSendingTest.java +++ b/test/jdk/java/net/httpclient/http3/StopSendingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -44,14 +44,15 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.internal.net.http.ResponseSubscribers; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + /* * @test * @summary Exercises the HTTP3 client to send a STOP_SENDING frame @@ -59,17 +60,17 @@ import static java.net.http.HttpOption.H3_DISCOVERY; * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * @compile ../ReferenceTracker.java - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=requests,responses,errors StopSendingTest */ public class StopSendingTest implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer h3Server; - private String requestURIBase; + private static HttpTestServer h3Server; + private static String requestURIBase; - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { h3Server = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); h3Server.addHandler(new Handler(), "/hello"); h3Server.start(); @@ -79,8 +80,8 @@ public class StopSendingTest implements HttpServerAdapters { } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { if (h3Server != null) { System.out.println("Stopping server " + h3Server.getAddress()); h3Server.stop(); @@ -154,7 +155,7 @@ public class StopSendingTest implements HttpServerAdapters { // of the Future instance, sometimes the Future.cancel(true) results // in an ExecutionException which wraps the CancellationException. // TODO: fix the actual race condition and then expect only CancellationException here - final Exception actualException = Assert.expectThrows(Exception.class, futureResp::get); + final Exception actualException = Assertions.assertThrows(Exception.class, futureResp::get); if (actualException instanceof CancellationException) { // expected System.out.println("Received the expected CancellationException"); diff --git a/test/jdk/java/net/httpclient/http3/StreamLimitTest.java b/test/jdk/java/net/httpclient/http3/StreamLimitTest.java index e0610cf89b8..9e15db9ceb9 100644 --- a/test/jdk/java/net/httpclient/http3/StreamLimitTest.java +++ b/test/jdk/java/net/httpclient/http3/StreamLimitTest.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 @@ -45,14 +45,15 @@ import jdk.httpclient.test.lib.quic.QuicServerConnection; import jdk.internal.net.http.quic.QuicTransportParameters; import jdk.internal.net.http.quic.QuicTransportParameters.ParameterId; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_3; import static java.net.http.HttpOption.H3_DISCOVERY; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + /* * @test * @summary verifies that when the Quic stream limit is reached @@ -64,17 +65,17 @@ import static java.net.http.HttpOption.H3_DISCOVERY; * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.httpclient.test.lib.http3.Http3TestServer - * @run testng/othervm -Djdk.internal.httpclient.debug=true StreamLimitTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true StreamLimitTest */ public class StreamLimitTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - private HttpTestServer server; - private QuicServer quicServer; - private URI requestURI; - private volatile QuicServerConnection latestServerConn; + private static HttpTestServer server; + private static QuicServer quicServer; + private static URI requestURI; + private static volatile QuicServerConnection latestServerConn; - private final class Handler implements HttpTestHandler { + private static final class Handler implements HttpTestHandler { @Override public void handle(HttpTestExchange exchange) throws IOException { @@ -97,8 +98,8 @@ public class StreamLimitTest { } } - @BeforeClass - public void beforeClass() throws Exception { + @BeforeAll + public static void beforeClass() throws Exception { quicServer = Http3TestServer.quicServerBuilder().sslContext(sslContext).build(); final Http3TestServer h3Server = new Http3TestServer(quicServer) { @Override @@ -118,8 +119,8 @@ public class StreamLimitTest { requestURI = new URI("https://" + server.serverAuthority() + "/foo"); } - @AfterClass - public void afterClass() throws Exception { + @AfterAll + public static void afterClass() throws Exception { latestServerConn = null; if (server != null) { server.stop(); @@ -161,8 +162,8 @@ public class StreamLimitTest { System.out.println("Sending request " + i + " to " + requestURI); final HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString()); - Assert.assertEquals(resp.version(), HTTP_3, "Unexpected response version"); - Assert.assertEquals(resp.statusCode(), 200, "Unexpected response code"); + Assertions.assertEquals(HTTP_3, resp.version(), "Unexpected response version"); + Assertions.assertEquals(200, resp.statusCode(), "Unexpected response code"); final String respBody = resp.body(); System.out.println("Request " + i + " was handled by server connection: " + respBody); if (i == 1) { @@ -170,7 +171,7 @@ public class StreamLimitTest { // to this request requestHandledBy = respBody; } else { - Assert.assertEquals(respBody, requestHandledBy, "Request was handled by an" + + Assertions.assertEquals(requestHandledBy, respBody, "Request was handled by an" + " unexpected server connection"); } } @@ -193,20 +194,20 @@ public class StreamLimitTest { + requestURI); final HttpResponse resp = client.send(reqWithTimeout, HttpResponse.BodyHandlers.ofString()); - Assert.assertEquals(resp.version(), HTTP_3, "Unexpected response version"); - Assert.assertEquals(resp.statusCode(), 200, "Unexpected response code"); + Assertions.assertEquals(HTTP_3, resp.version(), "Unexpected response version"); + Assertions.assertEquals(200, resp.statusCode(), "Unexpected response code"); final String respBody = resp.body(); System.out.println("Request " + i + " was handled by server connection: " + respBody); if (i == 1) { // first request after the limit was hit. // verify that it was handled by a new connection and not the one that handled // the previous N requests - Assert.assertNotEquals(respBody, requestHandledBy, "Request was expected to be" + + Assertions.assertNotEquals(requestHandledBy, respBody, "Request was expected to be" + " handled by a new server connection, but wasn't"); // keep track this new server connection id which responded to this request requestHandledBy = respBody; } else { - Assert.assertEquals(respBody, requestHandledBy, "Request was handled by an" + + Assertions.assertEquals(requestHandledBy, respBody, "Request was handled by an" + " unexpected server connection"); } } @@ -231,13 +232,13 @@ public class StreamLimitTest { " to " + requestURI); final HttpResponse resp = client.send(reqWithTimeout, HttpResponse.BodyHandlers.ofString()); - Assert.assertEquals(resp.version(), HTTP_3, "Unexpected response version"); - Assert.assertEquals(resp.statusCode(), 200, "Unexpected response code"); + Assertions.assertEquals(HTTP_3, resp.version(), "Unexpected response version"); + Assertions.assertEquals(200, resp.statusCode(), "Unexpected response code"); final String respBody = resp.body(); System.out.println("Request " + i + " was handled by server connection: " + respBody); // all these requests should be handled by the same server connection which handled // the previous requests - Assert.assertEquals(respBody, requestHandledBy, "Request was handled by an" + + Assertions.assertEquals(requestHandledBy, respBody, "Request was handled by an" + " unexpected server connection"); } // at this point the newer limit for bidi stream creation has reached on the client. @@ -254,12 +255,12 @@ public class StreamLimitTest { System.out.println("Sending request, without timeout, to " + requestURI); final HttpResponse finalResp = client.send(finalReq, HttpResponse.BodyHandlers.ofString()); - Assert.assertEquals(finalResp.version(), HTTP_3, "Unexpected response version"); - Assert.assertEquals(finalResp.statusCode(), 200, "Unexpected response code"); + Assertions.assertEquals(HTTP_3, finalResp.version(), "Unexpected response version"); + Assertions.assertEquals(200, finalResp.statusCode(), "Unexpected response code"); final String finalRespBody = finalResp.body(); System.out.println("Request was handled by server connection: " + finalRespBody); // this request should have been handled by a new server connection - Assert.assertNotEquals(finalRespBody, requestHandledBy, "Request was handled by an" + + Assertions.assertNotEquals(requestHandledBy, finalRespBody, "Request was handled by an" + " unexpected server connection"); } } From 4fbc29199c207e426176749f51dfc6994dede044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Arias=20de=20Reyna=20Dom=C3=ADnguez?= Date: Mon, 2 Mar 2026 16:16:31 +0000 Subject: [PATCH 116/636] 8377777: Improve logging when rejecting assets from the AOT archive Reviewed-by: adinn, iklam, stuefe --- src/hotspot/share/oops/constantPool.cpp | 3 +- src/hotspot/share/oops/cpCache.cpp | 56 +++++++++++++------ src/hotspot/share/oops/cpCache.hpp | 2 +- .../AOTLinkedVarHandles.java | 1 + 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 640b2f2460f..456333efad0 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -310,8 +310,9 @@ void ConstantPool::iterate_archivable_resolved_references(Function function) { if (method_entries != nullptr) { for (int i = 0; i < method_entries->length(); i++) { ResolvedMethodEntry* rme = method_entries->adr_at(i); + const char* rejection_reason = nullptr; if (rme->is_resolved(Bytecodes::_invokehandle) && rme->has_appendix() && - cache()->can_archive_resolved_method(this, rme)) { + cache()->can_archive_resolved_method(this, rme, rejection_reason)) { int rr_index = rme->resolved_references_index(); assert(resolved_reference_at(rr_index) != nullptr, "must exist"); function(rr_index); diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 75cdcb5310a..34d7aa10299 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -450,12 +450,15 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { Symbol* klass_name = cp->klass_name_at(klass_cp_index); Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); - log.print("%s field CP entry [%3d]: %s => %s.%s:%s%s", - (archived ? "archived" : "reverted"), - cp_index, - cp->pool_holder()->name()->as_C_string(), - klass_name->as_C_string(), name->as_C_string(), signature->as_C_string(), - rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic) ? " *** static" : ""); + if (resolved) { + log.print("%s field CP entry [%3d]: %s => %s.%s:%s%s%s", + (archived ? "archived" : "reverted"), + cp_index, + cp->pool_holder()->name()->as_C_string(), + klass_name->as_C_string(), name->as_C_string(), signature->as_C_string(), + rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic) ? " *** static" : "", + (archived ? "" : " (resolution is not deterministic)")); + } } ArchiveBuilder::alloc_stats()->record_field_cp_entry(archived, resolved && !archived); } @@ -474,32 +477,40 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { rme->is_resolved(Bytecodes::_invokehandle) || (rme->is_resolved(Bytecodes::_invokestatic) && VM_Version::supports_fast_class_init_checks()); + const char* rejection_reason = nullptr; if (resolved && !CDSConfig::is_dumping_preimage_static_archive() - && can_archive_resolved_method(src_cp, rme)) { + && can_archive_resolved_method(src_cp, rme, rejection_reason)) { rme->mark_and_relocate(src_cp); archived = true; } else { rme->remove_unshareable_info(); } - LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled()) { + LogTarget(Trace, aot, resolve) lt; + if (lt.is_enabled()) { ResourceMark rm; int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index); Symbol* klass_name = cp->klass_name_at(klass_cp_index); Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); - log.print("%s%s method CP entry [%3d]: %s %s.%s:%s", + LogStream ls(lt); + if (resolved) { + ls.print("%s%s method CP entry [%3d]: %s %s.%s:%s", (archived ? "archived" : "reverted"), (rme->is_resolved(Bytecodes::_invokeinterface) ? " interface" : ""), cp_index, cp->pool_holder()->name()->as_C_string(), klass_name->as_C_string(), name->as_C_string(), signature->as_C_string()); + if (rejection_reason != nullptr) { + ls.print(" %s", rejection_reason); + } + } if (archived) { Klass* resolved_klass = cp->resolved_klass_at(klass_cp_index); - log.print(" => %s%s", + ls.print(" => %s%s", resolved_klass->name()->as_C_string(), (rme->is_resolved(Bytecodes::_invokestatic) ? " *** static" : "")); } + ls.cr(); } ArchiveBuilder::alloc_stats()->record_method_cp_entry(archived, resolved && !archived); } @@ -528,42 +539,50 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref); Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref); Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref)); - log.print("%s indy CP entry [%3d]: %s (%d)", - (archived ? "archived" : "reverted"), - cp_index, cp->pool_holder()->name()->as_C_string(), i); - log.print(" %s %s.%s:%s", (archived ? "=>" : " "), bsm_klass->as_C_string(), - bsm_name->as_C_string(), bsm_signature->as_C_string()); + if (resolved) { + log.print("%s indy CP entry [%3d]: %s (%d)", + (archived ? "archived" : "reverted"), + cp_index, cp->pool_holder()->name()->as_C_string(), i); + log.print(" %s %s.%s:%s%s", (archived ? "=>" : " "), bsm_klass->as_C_string(), + bsm_name->as_C_string(), bsm_signature->as_C_string(), + (archived ? "" : " (resolution is not deterministic)")); + } } ArchiveBuilder::alloc_stats()->record_indy_cp_entry(archived, resolved && !archived); } } -bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry) { - LogStreamHandle(Trace, aot, resolve) log; +bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry, const char*& rejection_reason) { InstanceKlass* pool_holder = constant_pool()->pool_holder(); if (pool_holder->defined_by_other_loaders()) { // Archiving resolved cp entries for classes from non-builtin loaders // is not yet supported. + rejection_reason = "(pool holder comes from a non-builtin loader)"; return false; } if (CDSConfig::is_dumping_dynamic_archive()) { // InstanceKlass::methods() has been resorted. We need to // update the vtable_index in method_entry (not implemented) + rejection_reason = "(InstanceKlass::methods() has been resorted)"; return false; } if (!method_entry->is_resolved(Bytecodes::_invokevirtual)) { if (method_entry->method() == nullptr) { + rejection_reason = "(method entry is not resolved)"; return false; } if (method_entry->method()->is_continuation_native_intrinsic()) { + rejection_reason = "(corresponding stub is generated on demand during method resolution)"; return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call). } if (method_entry->is_resolved(Bytecodes::_invokehandle) && !CDSConfig::is_dumping_method_handles()) { + rejection_reason = "(not dumping method handles)"; return false; } if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) { + rejection_reason = "(not dumping intrinsic method handles)"; return false; } } @@ -572,6 +591,7 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity"); if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { + rejection_reason = "(resolution is not deterministic)"; return false; } return true; diff --git a/src/hotspot/share/oops/cpCache.hpp b/src/hotspot/share/oops/cpCache.hpp index 14202f226d1..f698f50d3a8 100644 --- a/src/hotspot/share/oops/cpCache.hpp +++ b/src/hotspot/share/oops/cpCache.hpp @@ -226,7 +226,7 @@ class ConstantPoolCache: public MetaspaceObj { void remove_resolved_field_entries_if_non_deterministic(); void remove_resolved_indy_entries_if_non_deterministic(); void remove_resolved_method_entries_if_non_deterministic(); - bool can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry); + bool can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry, const char*& rejection_reason); #endif // RedefineClasses support diff --git a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java index 4586b94b519..1089e849a90 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java @@ -64,6 +64,7 @@ public class AOTLinkedVarHandles { OutputAnalyzer dumpOut = CDSTestUtils.createArchiveAndCheck(opts); dumpOut.shouldMatch(s + "java/lang/invoke/VarHandle.compareAndExchangeAcquire:\\(\\[DIDI\\)D =>"); dumpOut.shouldMatch(s + "java/lang/invoke/VarHandle.get:\\(\\[DI\\)D => "); + dumpOut.shouldNotContain("rejected .* CP entry.*"); CDSOptions runOpts = (new CDSOptions()) .setUseVersion(false) From c4e39cea51faa01ec7dcd447c2e89ef988e6a7fb Mon Sep 17 00:00:00 2001 From: Ashay Rane <253344819+raneashay@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:49:28 +0000 Subject: [PATCH 117/636] 8373635: C2: Wrong constant in GraphKit::basic_plus_adr() Reviewed-by: qamai, mhaessig --- src/hotspot/share/opto/graphKit.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 084b137f313..c969abb85bb 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1177,7 +1177,21 @@ bool GraphKit::compute_stack_effects(int& inputs, int& depth) { //------------------------------basic_plus_adr--------------------------------- Node* GraphKit::basic_plus_adr(Node* base, Node* ptr, Node* offset) { // short-circuit a common case - if (offset == intcon(0)) return ptr; + if (offset == MakeConX(0)) { + return ptr; + } +#ifdef ASSERT + // Both 32-bit and 64-bit zeros should have been handled by the previous `if` + // statement, so if we see either 32-bit or 64-bit zeros here, then we have a + // problem. + if (offset->is_Con()) { + const Type* t = offset->bottom_type(); + bool is_zero_int = t->isa_int() && t->is_int()->get_con() == 0; + bool is_zero_long = t->isa_long() && t->is_long()->get_con() == 0; + assert(!is_zero_int && !is_zero_long, + "Unexpected zero offset - should have matched MakeConX(0)"); + } +#endif return _gvn.transform( new AddPNode(base, ptr, offset) ); } From f2a52b7a069c9180638aec15cc0d748f337c3d2f Mon Sep 17 00:00:00 2001 From: William Kemper Date: Mon, 2 Mar 2026 17:02:22 +0000 Subject: [PATCH 118/636] 8378880: GenShen: Fix typo in recent conversion to atomic field Reviewed-by: kdnilsen, xpeng, ysr --- src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index d5e34d02b13..4fda65b4030 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -583,7 +583,7 @@ void ShenandoahOldGeneration::handle_failed_evacuation() { void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) { _promotion_failure_count.add_then_fetch(1UL); - _promotion_failure_words.and_then_fetch(size); + _promotion_failure_words.add_then_fetch(size); LogTarget(Debug, gc, plab) lt; LogStream ls(lt); From 0baeccddffb0a99ce82a1bffdf209c8d2dc05f1c Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Mon, 2 Mar 2026 17:22:48 +0000 Subject: [PATCH 119/636] 8378111: Migrate java/util/jar tests to JUnit Reviewed-by: lancea --- .../util/jar/Attributes/IterationOrder.java | 28 ++- test/jdk/java/util/jar/Attributes/Name.java | 19 +- .../Attributes/NullAndEmptyKeysAndValues.java | 22 +- .../util/jar/Attributes/PutAndPutAll.java | 36 ++- .../java/util/jar/Attributes/TestAttrsNL.java | 57 +++-- .../jar/JarEntry/GetMethodsReturnClones.java | 36 ++- .../java/util/jar/JarFile/Constructor.java | 46 ++-- .../IgnoreUnrelatedSignatureFiles.java | 137 ++++++----- .../util/jar/JarFile/JarBacktickManifest.java | 27 ++- .../java/util/jar/JarFile/JarNoManifest.java | 23 +- test/jdk/java/util/jar/JarFile/MevNPE.java | 13 +- .../java/util/jar/JarFile/ScanSignedJar.java | 29 +-- .../JarFile/SignedJarFileGetInputStream.java | 41 ++-- .../jar/JarFile/SignedJarPendingBlock.java | 65 +++-- .../java/util/jar/JarFile/SorryClosed.java | 58 +++-- test/jdk/java/util/jar/JarFile/TurkCert.java | 37 ++- .../util/jar/JarFile/VerifySignedJar.java | 55 ++--- .../jarVerification/MultiProviderTest.java | 31 +-- .../jar/JarFile/mrjar/MultiReleaseJarAPI.java | 87 +++---- .../mrjar/MultiReleaseJarHttpProperties.java | 37 +-- .../mrjar/MultiReleaseJarProperties.java | 45 ++-- .../mrjar/MultiReleaseJarSecurity.java | 38 +-- .../JarFile/mrjar/TestVersionedStream.java | 99 ++++---- .../util/jar/JarInputStream/EmptyJar.java | 22 +- .../JarInputStream/ExtraFileInMetaInf.java | 22 +- .../jar/JarInputStream/ScanSignedJar.java | 24 +- .../TestIndexedJarWithBadSignature.java | 34 +-- .../util/jar/Manifest/CreateManifest.java | 47 ++-- .../jar/Manifest/IncludeInExceptionsTest.java | 64 +++-- .../util/jar/Manifest/LineBreakLineWidth.java | 31 +-- .../util/jar/Manifest/ValueUtf8Coding.java | 16 +- .../jar/Manifest/WriteBinaryStructure.java | 25 +- test/jdk/java/util/jar/TestExtra.java | 226 ++++-------------- test/jdk/java/util/jar/TestJarExtra.java | 224 +++++++++++++++++ 34 files changed, 990 insertions(+), 811 deletions(-) create mode 100644 test/jdk/java/util/jar/TestJarExtra.java diff --git a/test/jdk/java/util/jar/Attributes/IterationOrder.java b/test/jdk/java/util/jar/Attributes/IterationOrder.java index 4028d71e7c4..edd1d9b8bc8 100644 --- a/test/jdk/java/util/jar/Attributes/IterationOrder.java +++ b/test/jdk/java/util/jar/Attributes/IterationOrder.java @@ -1,5 +1,6 @@ /* * Copyright 2014 Google, Inc. All Rights Reserved. + * 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 @@ -24,15 +25,26 @@ /* @test * @bug 8062194 * @summary Ensure Attribute iteration order is the insertion order. + * @run junit IterationOrder */ +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import java.util.Arrays; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; public class IterationOrder { - static void checkOrder(Attributes.Name k0, String v0, + + @ParameterizedTest + @MethodSource + void checkOrderTest(Attributes.Name k0, String v0, Attributes.Name k1, String v1, Attributes.Name k2, String v2) { Attributes x = new Attributes(); @@ -48,7 +60,7 @@ public class IterationOrder { && entries[1].getValue() == v1 && entries[2].getKey() == k2 && entries[2].getValue() == v2)) { - throw new AssertionError(Arrays.toString(entries)); + fail(Arrays.toString(entries)); } Object[] keys = x.keySet().toArray(); @@ -56,19 +68,21 @@ public class IterationOrder { && keys[0] == k0 && keys[1] == k1 && keys[2] == k2)) { - throw new AssertionError(Arrays.toString(keys)); + fail(Arrays.toString(keys)); } } - public static void main(String[] args) throws Exception { + static Stream checkOrderTest() { Attributes.Name k0 = Name.MANIFEST_VERSION; Attributes.Name k1 = Name.MAIN_CLASS; Attributes.Name k2 = Name.SEALED; String v0 = "42.0"; String v1 = "com.google.Hello"; String v2 = "yes"; - checkOrder(k0, v0, k1, v1, k2, v2); - checkOrder(k1, v1, k0, v0, k2, v2); - checkOrder(k2, v2, k1, v1, k0, v0); + return Stream.of( + Arguments.of(k0, v0, k1, v1, k2, v2), + Arguments.of(k1, v1, k0, v0, k2, v2), + Arguments.of(k2, v2, k1, v1, k0, v0) + ); } } diff --git a/test/jdk/java/util/jar/Attributes/Name.java b/test/jdk/java/util/jar/Attributes/Name.java index 78306028698..843038cb820 100644 --- a/test/jdk/java/util/jar/Attributes/Name.java +++ b/test/jdk/java/util/jar/Attributes/Name.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,17 +25,20 @@ @bug 4199981 @summary Make sure empty string is not a valid Attributes name. - */ + @run junit Name + */ +import org.junit.jupiter.api.Test; import java.util.jar.Attributes; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class Name { - public static void main(String[] args) throws Exception { - try { - Attributes.Name name = new Attributes.Name(""); - throw new Exception("empty string should be rejected"); - } catch (IllegalArgumentException e) { - } + + @Test + void emptyStringTest() { + assertThrows(IllegalArgumentException.class, () -> new Attributes.Name(""), + "empty string should be rejected"); } } diff --git a/test/jdk/java/util/jar/Attributes/NullAndEmptyKeysAndValues.java b/test/jdk/java/util/jar/Attributes/NullAndEmptyKeysAndValues.java index c62ddcced8a..e99825d235e 100644 --- a/test/jdk/java/util/jar/Attributes/NullAndEmptyKeysAndValues.java +++ b/test/jdk/java/util/jar/Attributes/NullAndEmptyKeysAndValues.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -21,8 +21,6 @@ * questions. */ -import static java.nio.charset.StandardCharsets.UTF_8; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -31,14 +29,16 @@ import java.util.jar.Manifest; import java.util.jar.Attributes.Name; import java.lang.reflect.Field; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.*; /** * @test * @bug 8066619 * @modules java.base/java.util.jar:+open - * @run testng/othervm --enable-final-field-mutation=ALL-UNNAMED NullAndEmptyKeysAndValues + * @run junit/othervm --enable-final-field-mutation=ALL-UNNAMED NullAndEmptyKeysAndValues * @summary Tests manifests with {@code null} and empty string {@code ""} * values as section name, header name, or value in both main and named * attributes sections. @@ -108,7 +108,7 @@ public class NullAndEmptyKeysAndValues { attr.set(mf, mainAtts); mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); mf = writeAndRead(mf); - assertEquals(mf.getMainAttributes().getValue(SOME_KEY), NULL_TEXT); + assertEquals(NULL_TEXT, mf.getMainAttributes().getValue(SOME_KEY)); } @Test @@ -122,7 +122,7 @@ public class NullAndEmptyKeysAndValues { attr.set(mf, mainAtts); mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); mf = writeAndRead(mf); - assertEquals(mf.getMainAttributes().getValue(SOME_KEY), EMPTY_STR); + assertEquals(EMPTY_STR, mf.getMainAttributes().getValue(SOME_KEY)); } @Test @@ -171,8 +171,7 @@ public class NullAndEmptyKeysAndValues { map.put(new Name(SOME_KEY), null); }}); mf = writeAndRead(mf); - assertEquals(mf.getEntries().get(SOME_KEY).getValue(SOME_KEY), - NULL_TEXT); + assertEquals(NULL_TEXT, mf.getEntries().get(SOME_KEY).getValue(SOME_KEY)); } @Test @@ -183,8 +182,7 @@ public class NullAndEmptyKeysAndValues { map.put(new Name(SOME_KEY), EMPTY_STR); }}); mf = writeAndRead(mf); - assertEquals(mf.getEntries().get(SOME_KEY).getValue(SOME_KEY), - EMPTY_STR); + assertEquals(EMPTY_STR, mf.getEntries().get(SOME_KEY).getValue(SOME_KEY)); } static Manifest writeAndRead(Manifest mf) throws IOException { diff --git a/test/jdk/java/util/jar/Attributes/PutAndPutAll.java b/test/jdk/java/util/jar/Attributes/PutAndPutAll.java index f459daf8c2e..bd61ba1e92d 100644 --- a/test/jdk/java/util/jar/Attributes/PutAndPutAll.java +++ b/test/jdk/java/util/jar/Attributes/PutAndPutAll.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -25,31 +25,27 @@ @bug 4165833 4167600 @summary Test if put and putAll will test for illegal arguments. - */ + @run junit PutAndPutAll + */ +import org.junit.jupiter.api.Test; + import java.util.jar.Attributes; import java.util.HashMap; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class PutAndPutAll { - public static void main(String[] args) throws Exception { + + @Test + void classCastTest() { Attributes at = new Attributes(); - try{ - at.put("this is not an Attributes.Name", "value"); - throw new Exception("put should check for non Attributes.Name names"); - } catch (ClassCastException e) { - } - - try{ - at.put(new Attributes.Name("name"), new Integer(0)); - throw new Exception("put should check for non String values"); - } catch (ClassCastException e) { - } - - try { - at.putAll(new HashMap()); - throw new Exception("putAll should check for non Attributes maps"); - } catch (ClassCastException e) { - } + assertThrows(ClassCastException.class, + () -> at.put("this is not an Attributes.Name", "value"), "put should check for non Attributes.Name names"); + assertThrows(ClassCastException.class, + () -> at.put(new Attributes.Name("name"), new Integer(0)), "put should check for non String values"); + assertThrows(ClassCastException.class, + () -> at.putAll(new HashMap()), "putAll should check for non Attributes maps"); } } diff --git a/test/jdk/java/util/jar/Attributes/TestAttrsNL.java b/test/jdk/java/util/jar/Attributes/TestAttrsNL.java index 34f7e4c4502..abfd3df6cd7 100644 --- a/test/jdk/java/util/jar/Attributes/TestAttrsNL.java +++ b/test/jdk/java/util/jar/Attributes/TestAttrsNL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,19 +24,28 @@ /* @test * @bug 8200530 * @summary Test Attributes newline + * @run junit TestAttrsNL */ +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; import java.util.jar.Manifest; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.io.ByteArrayInputStream; import java.util.Map; +import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TestAttrsNL { - public static void main(String[] args) throws Throwable { + static Stream newLineAttributesTest() throws IOException { String manifestStr = "Manifest-Version: 1.0\r\n" + @@ -68,16 +77,16 @@ public class TestAttrsNL { new Name("key44"), "value44" ); - test(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))), - mainAttrsExped, attrsExped); + var normal = Arguments.of(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))), + mainAttrsExped, attrsExped); - test(new Manifest(new ByteArrayInputStream( - manifestStr.replaceAll("\r\n", "\r").getBytes(UTF_8))), - mainAttrsExped, attrsExped); + var carriage = Arguments.of(new Manifest(new ByteArrayInputStream( + manifestStr.replaceAll("\r\n", "\r").getBytes(UTF_8))), + mainAttrsExped, attrsExped); - test(new Manifest(new ByteArrayInputStream( - manifestStr.replaceAll("\r\n", "\n").getBytes(UTF_8))), - mainAttrsExped, attrsExped); + var newLine = Arguments.of(new Manifest(new ByteArrayInputStream( + manifestStr.replaceAll("\r\n", "\n").getBytes(UTF_8))), + mainAttrsExped, attrsExped); // mixed manifestStr = @@ -93,31 +102,33 @@ public class TestAttrsNL { "key22: value22\n END\r\n" + "key33: value33\r \n" + "key44: value44\n"; - test(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))), + var mixed = Arguments.of(new Manifest(new ByteArrayInputStream(manifestStr.getBytes(UTF_8))), mainAttrsExped, attrsExped); - + return Stream.of(normal, carriage, newLine, mixed); } - private static void test(Manifest m, + @ParameterizedTest + @MethodSource + void newLineAttributesTest(Manifest m, Map mainAttrsExped, Map attrsExped) { Attributes mainAttrs = m.getMainAttributes(); mainAttrsExped.forEach( (k, v) -> { - if (!mainAttrs.containsKey(k) || !mainAttrs.get(k).equals(v)) { - System.out.printf(" containsKey(%s) : %b%n", k, mainAttrs.containsKey(k)); - System.out.printf(" get(%s) : %s%n", k, mainAttrs.get(k)); - throw new RuntimeException("expected attr: k=<" + k + ">, v=<" + v + ">"); - } + var expectedMsg = "expected attr: k=<" + k + ">, v=<" + v + ">"; + assertTrue(mainAttrs.containsKey(k), + " containsKey(%s) : %b%n%s".formatted(k, mainAttrs.containsKey(k), expectedMsg)); + assertEquals(v, mainAttrs.get(k), + " get(%s) : %s%n%s".formatted(k, mainAttrs.get(k), expectedMsg)); }); Attributes attrs = m.getAttributes("Hello"); attrs.forEach( (k, v) -> { - if (!attrs.containsKey(k) || !attrs.get(k).equals(v)) { - System.out.printf(" containsKey(%s) : %b%n", k, attrs.containsKey(k)); - System.out.printf(" get(%s) : %s%n", k, attrs.get(k)); - throw new RuntimeException("expected attr: k=<" + k + ">, v=<" + v + ">"); - } + var expectedMsg = "expected attr: k=<" + k + ">, v=<" + v + ">"; + assertTrue(attrs.containsKey(k), + " containsKey(%s) : %b%n%s".formatted(k, attrs.containsKey(k), expectedMsg)); + assertEquals(v, attrs.get(k), + " get(%s) : %s%n%s".formatted(k, attrs.get(k), expectedMsg)); }); } } diff --git a/test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java b/test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java index 3c4f06dd6f0..a9b35220de3 100644 --- a/test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java +++ b/test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, 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 @@ -26,20 +26,28 @@ * @bug 6337925 * @summary Ensure that callers cannot modify the internal JarEntry cert and * codesigner arrays. - * @author Sean Mullan + * @run junit GetMethodsReturnClones */ +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; import java.io.InputStream; import java.security.CodeSigner; import java.security.cert.Certificate; import java.util.*; import java.util.jar.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; + public class GetMethodsReturnClones { private static final String BASE = System.getProperty("test.src", ".") + System.getProperty("file.separator"); + private static List jarEntries; - public static void main(String[] args) throws Exception { + @BeforeAll() + static void setupEntries() throws IOException { List entries = new ArrayList<>(); try (JarFile jf = new JarFile(BASE + "test.jar", true)) { byte[] buffer = new byte[8192]; @@ -55,23 +63,29 @@ public class GetMethodsReturnClones { } } } + jarEntries = entries; + } - for (JarEntry je : entries) { + @Test + void certsTest() { + for (JarEntry je : jarEntries) { Certificate[] certs = je.getCertificates(); - CodeSigner[] signers = je.getCodeSigners(); if (certs != null) { certs[0] = null; certs = je.getCertificates(); - if (certs[0] == null) { - throw new Exception("Modified internal certs array"); - } + assertNotNull(certs[0], "Modified internal certs array"); } + } + } + + @Test + void signersTest() { + for (JarEntry je : jarEntries) { + CodeSigner[] signers = je.getCodeSigners(); if (signers != null) { signers[0] = null; signers = je.getCodeSigners(); - if (signers[0] == null) { - throw new Exception("Modified internal codesigners array"); - } + assertNotNull(signers[0], "Modified internal codesigners array"); } } } diff --git a/test/jdk/java/util/jar/JarFile/Constructor.java b/test/jdk/java/util/jar/JarFile/Constructor.java index 8c1a8623e61..071c68fdd9e 100644 --- a/test/jdk/java/util/jar/JarFile/Constructor.java +++ b/test/jdk/java/util/jar/JarFile/Constructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, 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 @@ -21,55 +21,45 @@ * questions. */ -/** +/* * @test * @bug 4842702 8211765 * @summary Check that constructors throw specified exceptions - * @author Martin Buchholz + * @run junit Constructor */ +import org.junit.jupiter.api.Test; + import java.util.jar.JarFile; import java.io.File; import java.io.IOException; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class Constructor { - private static void Unreached (Object o) - throws Exception - { - // Should never get here - throw new Exception ("Expected exception was not thrown"); - } - public static void main(String[] args) - throws Exception - { - try { Unreached (new JarFile ((File) null, true, JarFile.OPEN_READ)); } - catch (NullPointerException e) {} + @Test + void constructorTest() { - try { Unreached (new JarFile ((File) null, true)); } - catch (NullPointerException e) {} + assertThrows(NullPointerException.class, () -> new JarFile ((File) null, true, JarFile.OPEN_READ)); - try { Unreached (new JarFile ((File) null)); } - catch (NullPointerException e) {} + assertThrows(NullPointerException.class, () -> new JarFile ((File) null, true)); - try { Unreached (new JarFile ((String) null, true)); } - catch (NullPointerException e) {} + assertThrows(NullPointerException.class, () -> new JarFile ((File) null)); - try { Unreached (new JarFile ((String) null)); } - catch (NullPointerException e) {} + assertThrows(NullPointerException.class, () -> new JarFile ((String) null, true)); - try { Unreached (new JarFile ("NoSuchJar.jar")); } - catch (IOException e) {} + assertThrows(NullPointerException.class, () -> new JarFile ((String) null)); - try { Unreached (new JarFile (new File ("NoSuchJar.jar"))); } - catch (IOException e) {} + assertThrows(IOException.class, () -> new JarFile ("NoSuchJar.jar")); + + assertThrows(IOException.class, () -> new JarFile (new File ("NoSuchJar.jar"))); // Test that an IOExcception is thrown when an invalid charater // is part of the path on Windows and Unix final String invalidOSPath = System.getProperty("os.name") .startsWith("Windows") ? "C:\\*" : "foo\u0000bar"; - try { Unreached (new JarFile (invalidOSPath)); } - catch (IOException e) {} + assertThrows(IOException.class, () -> new JarFile (invalidOSPath)); } } diff --git a/test/jdk/java/util/jar/JarFile/IgnoreUnrelatedSignatureFiles.java b/test/jdk/java/util/jar/JarFile/IgnoreUnrelatedSignatureFiles.java index 0f55702c1f6..e5a32dfde73 100644 --- a/test/jdk/java/util/jar/JarFile/IgnoreUnrelatedSignatureFiles.java +++ b/test/jdk/java/util/jar/JarFile/IgnoreUnrelatedSignatureFiles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -21,7 +21,7 @@ * questions. */ -/** +/* * @test * @bug 8300140 * @summary Make sure signature related files in subdirectories of META-INF are not considered for verification @@ -29,12 +29,14 @@ * @modules java.base/sun.security.util * @modules java.base/sun.security.tools.keytool * @modules jdk.jartool/sun.security.tools.jarsigner - * @run main/othervm IgnoreUnrelatedSignatureFiles + * @run junit/othervm IgnoreUnrelatedSignatureFiles */ import jdk.internal.access.JavaUtilZipFileAccess; import jdk.internal.access.SharedSecrets; import jdk.security.jarsigner.JarSigner; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import sun.security.tools.jarsigner.Main; import sun.security.util.SignatureFileVerifier; @@ -62,6 +64,10 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + public class IgnoreUnrelatedSignatureFiles { private static final JavaUtilZipFileAccess JUZA = SharedSecrets.getJavaUtilZipFileAccess(); @@ -69,56 +75,76 @@ public class IgnoreUnrelatedSignatureFiles { // This path resides in a subdirectory of META-INF, so it should not be considered signature related public static final String SUBDIR_SF_PATH = "META-INF/subdirectory/META-INF/SIGNER.SF"; + // Jars used for testing. See `setupJars` below for setup + static Path j; + static Path s; + static Path m; + static Path sm; + static Path ca; + static Path cas; - public static void main(String[] args) throws Exception { - + @BeforeAll + static void setupJars() throws Exception { // Regular signed JAR - Path j = createJarFile(); - Path s = signJarFile(j, "SIGNER1", "signed"); + j = createJarFile(); + s = signJarFile(j, "SIGNER1", "signed"); // Singed JAR with unrelated signature files - Path m = moveSignatureRelated(s); - Path sm = signJarFile(m, "SIGNER2", "modified-signed"); + m = moveSignatureRelated(s); + sm = signJarFile(m, "SIGNER2", "modified-signed"); // Signed JAR with custom SIG-* files - Path ca = createCustomAlgJar(); - Path cas = signJarFile(ca, "SIGNER1", "custom-signed"); + ca = createCustomAlgJar(); + cas = signJarFile(ca, "SIGNER1", "custom-signed"); + } - // 0: Sanity check that the basic signed JAR verifies + // Sanity check that the basic signed JAR verifies + @Test + void signedJarVerifyTest() throws IOException { try (JarFile jf = new JarFile(s.toFile(), true)) { Map entries = jf.getManifest().getEntries(); - if (entries.size() != 1) { - throw new Exception("Expected a single manifest entry for the digest of a.txt, instead found entries: " + entries.keySet()); - } + assertEquals(1, entries.size(), + "Expected a single manifest entry for the digest of a.txt, instead found entries: " + entries.keySet()); JarEntry entry = jf.getJarEntry("a.txt"); try (InputStream in = jf.getInputStream(entry)) { in.transferTo(OutputStream.nullOutputStream()); } } - // 1: Check ZipFile.Source.isSignatureRelated + } + + // Check ZipFile.Source.isSignatureRelated + @Test + void zipFileSourceIsSignatureRelatedTest() throws IOException { try (JarFile jarFile = new JarFile(m.toFile())) { List manifestAndSignatureRelatedFiles = JUZA.getManifestAndSignatureRelatedFiles(jarFile); for (String signatureRelatedFile : manifestAndSignatureRelatedFiles) { String dir = signatureRelatedFile.substring(0, signatureRelatedFile.lastIndexOf("/")); - if (!"META-INF".equals(dir)) { - throw new Exception("Signature related file does not reside directly in META-INF/ : " + signatureRelatedFile); - } + assertEquals("META-INF", dir, + "Signature related file does not reside directly in META-INF/ : " + signatureRelatedFile); } } + } - // 2: Check SignatureFileVerifier.isSigningRelated - if (SignatureFileVerifier.isSigningRelated(SUBDIR_SF_PATH)) { - throw new Exception("Signature related file does not reside directly in META-INF/ : " + SUBDIR_SF_PATH); - } + // Check SignatureFileVerifier.isSigningRelated + @Test + void sigFileVerifierIsSigningRelatedTest() { + assertFalse(SignatureFileVerifier.isSigningRelated(SUBDIR_SF_PATH), + "Signature related file does not reside directly in META-INF/ : " + SUBDIR_SF_PATH); + } - // 3: Check JarInputStream with doVerify = true + // Check JarInputStream with doVerify = true + @Test + void jarIStreamDoVerifyTest() throws IOException { try (JarInputStream in = new JarInputStream(Files.newInputStream(m), true)) { - while (in.getNextEntry() != null) { + while (in.getNextEntry() != null) { in.transferTo(OutputStream.nullOutputStream()); } } + } - // 4: Check that a JAR containing unrelated .SF, .RSA files is signed as-if it is unsigned + // Check that a JAR containing unrelated .SF, .RSA files is signed as-if it is unsigned + @Test + void unrelatedFilesUnsignedTest() throws IOException { try (ZipFile zf = new ZipFile(sm.toFile())) { ZipEntry mf = zf.getEntry("META-INF/MANIFEST.MF"); try (InputStream stream = zf.getInputStream(mf)) { @@ -126,19 +152,24 @@ public class IgnoreUnrelatedSignatureFiles { // When JarSigner considers a jar to not be already signed, // the 'Manifest-Version' attributed name will be case-normalized // Assert that manifest-version is not in lowercase - if (manifest.startsWith("manifest-version")) { - throw new Exception("JarSigner unexpectedly treated unsigned jar as signed"); - } + assertFalse(manifest.startsWith("manifest-version"), + "JarSigner unexpectedly treated unsigned jar as signed"); } } + } - // 5: Check that a JAR containing non signature related .SF, .RSA files can be signed + // Check that a JAR containing non signature related .SF, .RSA files can be signed + @Test + void nonSigFileIsSignableTest() throws Exception { try (JarFile jf = new JarFile(sm.toFile(), true)) { checkSignedBy(jf, "a.txt", "CN=SIGNER2"); checkSignedBy(jf, "META-INF/subdirectory/META-INF/SIGNER1.SF", "CN=SIGNER2"); } + } - // 6: Check that JarSigner does not move unrelated [SF,RSA] files to the beginning of signed JARs + // Check that JarSigner does not move unrelated [SF,RSA] files to the beginning of signed JARs + @Test + void jarSignerDoesNotMoveUnrelatedTest() throws IOException { try (JarFile zf = new JarFile(sm.toFile())) { List actualOrder = zf.stream().map(ZipEntry::getName).toList(); @@ -154,23 +185,25 @@ public class IgnoreUnrelatedSignatureFiles { "META-INF/subdirectory2/META-INF/SIGNER1.RSA" ); - if (!expectedOrder.equals(actualOrder)) { - String msg = (""" + assertEquals(expectedOrder, actualOrder, (""" Unexpected file order in JAR with unrelated SF,RSA files Expected order: %s Actual order: %s""") - .formatted(expectedOrder, actualOrder); - throw new Exception(msg); - } + .formatted(expectedOrder, actualOrder)); } + } - // 7: Check that jarsigner ignores unrelated signature files + // Check that jarsigner ignores unrelated signature files + @Test + void jarSignerIgnoresUnrelatedTest() throws Exception { String message = jarSignerVerify(m); - if (message.contains("WARNING")) { - throw new Exception("jarsigner output contains unexpected warning: " +message); - } + assertFalse(message.contains("WARNING"), + "jarsigner output contains unexpected warning: " + message); + } - // 8: Check that SignatureFileVerifier.isSigningRelated handles custom SIG-* files correctly + // Check that SignatureFileVerifier.isSigningRelated handles custom SIG-* files correctly + @Test + void customSIGFilesTest() throws IOException { try (JarFile jf = new JarFile(cas.toFile(), true)) { // These files are not signature-related and should be signed @@ -185,10 +218,9 @@ public class IgnoreUnrelatedSignatureFiles { Set actualSigned = jf.getManifest().getEntries().keySet(); - if (!expectedSigned.equals(actualSigned)) { - throw new Exception("Unexpected MANIFEST entries. Expected %s, got %s" - .formatted(expectedSigned, actualSigned)); - } + assertEquals(expectedSigned, actualSigned, + "Unexpected MANIFEST entries. Expected %s, got %s" + .formatted(expectedSigned, actualSigned)); } } @@ -220,22 +252,17 @@ public class IgnoreUnrelatedSignatureFiles { // Verify that the entry is signed CodeSigner[] signers = je.getCodeSigners(); - if (signers == null) { - throw new Exception(String.format("Expected %s to be signed", name)); - } + assertNotNull(signers, "Expected %s to be signed".formatted(name)); // There should be a single signer - if (signers.length != 1) { - throw new Exception(String.format("Expected %s to be signed by exactly one signer", name)); - } + assertEquals(1, signers.length, + "Expected %s to be signed by exactly one signer".formatted(name)); String actualSigner = ((X509Certificate) signers[0] .getSignerCertPath().getCertificates().get(0)) .getIssuerX500Principal().getName(); - - if (!actualSigner.equals(expectedSigner)) { - throw new Exception(String.format("Expected %s to be signed by %s, was signed by %s", name, expectedSigner, actualSigner)); - } + assertEquals(expectedSigner, actualSigner, + "Expected %s to be signed by %s, was signed by %s".formatted(name, expectedSigner, actualSigner)); } /** diff --git a/test/jdk/java/util/jar/JarFile/JarBacktickManifest.java b/test/jdk/java/util/jar/JarFile/JarBacktickManifest.java index 65d54a67a47..66edf3ae3ee 100644 --- a/test/jdk/java/util/jar/JarFile/JarBacktickManifest.java +++ b/test/jdk/java/util/jar/JarFile/JarBacktickManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -27,7 +27,7 @@ * @summary Make sure scanning manifest doesn't throw AIOOBE on certain strings containing backticks. * @library /test/lib/ * @build jdk.test.lib.util.JarBuilder - * @run testng JarBacktickManifest + * @run junit JarBacktickManifest */ import java.io.File; @@ -35,19 +35,20 @@ import java.io.IOException; import java.nio.file.Files; import java.util.jar.JarFile; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import jdk.test.lib.util.JarBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; + public class JarBacktickManifest { public static final String VERIFY_MANIFEST_JAR = "verifyManifest.jar"; - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { JarBuilder jb = new JarBuilder(VERIFY_MANIFEST_JAR); jb.addAttribute("Test", " Class-`Path` "); jb.addAttribute("Test2", " Multi-`Release "); @@ -55,14 +56,14 @@ public class JarBacktickManifest { } @Test - public void test() throws Exception { + public void backtickTest() throws Exception { try (JarFile jf = new JarFile(VERIFY_MANIFEST_JAR)) { // do not set runtime versioning - Assert.assertFalse(jf.isMultiRelease(), "Shouldn't be multi-release"); + assertFalse(jf.isMultiRelease(), "Shouldn't be multi-release"); } } - @AfterClass - public void close() throws IOException { + @AfterAll + public static void close() throws IOException { Files.delete(new File(VERIFY_MANIFEST_JAR).toPath()); } } diff --git a/test/jdk/java/util/jar/JarFile/JarNoManifest.java b/test/jdk/java/util/jar/JarFile/JarNoManifest.java index 971ce92f9e9..31301e28975 100644 --- a/test/jdk/java/util/jar/JarFile/JarNoManifest.java +++ b/test/jdk/java/util/jar/JarFile/JarNoManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -24,18 +24,25 @@ /* @test @bug 4771616 @summary JarFile.maybeInstantiateVerifier must check for absence of manifest + @run junit JarNoManifest */ +import org.junit.jupiter.api.Test; + import java.io.*; import java.util.jar.*; import java.util.zip.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + public class JarNoManifest { - public static void main(String[] args) throws Exception { - File f = new File(System.getProperty("test.src","."), "no-manifest.jar"); - JarFile jar = new JarFile(f); - ZipEntry entry = jar.getEntry("JarNoManifest.java"); - // The following throws a NullPointerException when the bug is present - InputStream in = jar.getInputStream(entry); - } + + @Test + void absentManifestTest() throws IOException { + File f = new File(System.getProperty("test.src", "."), "no-manifest.jar"); + JarFile jar = new JarFile(f); + ZipEntry entry = jar.getEntry("JarNoManifest.java"); + // The following throws a NullPointerException when the bug is present + assertDoesNotThrow(() -> jar.getInputStream(entry)); + } } diff --git a/test/jdk/java/util/jar/JarFile/MevNPE.java b/test/jdk/java/util/jar/JarFile/MevNPE.java index f8627d33324..20ac9f2d796 100644 --- a/test/jdk/java/util/jar/JarFile/MevNPE.java +++ b/test/jdk/java/util/jar/JarFile/MevNPE.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -24,22 +24,27 @@ /* @test * @bug 7023056 * @summary NPE from sun.security.util.ManifestEntryVerifier.verify during Maven build + * @run junit MevNPE */ +import org.junit.jupiter.api.Test; + import java.io.*; import java.util.jar.*; public class MevNPE { - public static void main(String[] args) throws Exception { + + @Test + void noNpeTest() throws IOException { File f = new File(System.getProperty("test.src", "."), "Signed.jar"); try (JarFile jf = new JarFile(f, true)) { try (InputStream s1 = jf.getInputStream( jf.getJarEntry(JarFile.MANIFEST_NAME))) { s1.read(new byte[10000]); - }; + } try (InputStream s2 = jf.getInputStream( jf.getJarEntry(JarFile.MANIFEST_NAME))) { s2.read(new byte[10000]); - }; + } } } } diff --git a/test/jdk/java/util/jar/JarFile/ScanSignedJar.java b/test/jdk/java/util/jar/JarFile/ScanSignedJar.java index fe49cea24aa..75b4732645d 100644 --- a/test/jdk/java/util/jar/JarFile/ScanSignedJar.java +++ b/test/jdk/java/util/jar/JarFile/ScanSignedJar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, 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 @@ -21,29 +21,35 @@ * questions. */ -/** +/* * @test * @bug 4953126 * @summary Check that a signed JAR file containing an unsupported signer info * attribute can be parsed successfully. + * @run junit ScanSignedJar */ +import org.junit.jupiter.api.Test; + import java.io.File; +import java.io.IOException; import java.io.InputStream; -import java.security.cert.Certificate; import java.util.Enumeration; import java.util.jar.*; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class ScanSignedJar { - public static void main(String[] args) throws Exception { + @Test + void unsupportedSignerTest() throws IOException { boolean isSigned = false; try (JarFile file = new JarFile(new File(System.getProperty("test.src","."), - "bogus-signerinfo-attr.jar"))) { + "bogus-signerinfo-attr.jar"))) { byte[] buffer = new byte[8192]; - for (Enumeration entries = file.entries(); entries.hasMoreElements();) { - JarEntry entry = (JarEntry) entries.nextElement(); + for (Enumeration entries = file.entries(); entries.hasMoreElements();) { + JarEntry entry = entries.nextElement(); try (InputStream jis = file.getInputStream(entry)) { while (jis.read(buffer, 0, buffer.length) != -1) { // read the jar entry @@ -53,14 +59,9 @@ public class ScanSignedJar { isSigned = true; } System.out.println((isSigned ? "[signed] " : "\t ") + - entry.getName()); + entry.getName()); } } - - if (isSigned) { - System.out.println("\nJAR file has signed entries"); - } else { - throw new Exception("Failed to detect that the JAR file is signed"); - } + assertTrue(isSigned, "Failed to detect that the JAR file is signed"); } } diff --git a/test/jdk/java/util/jar/JarFile/SignedJarFileGetInputStream.java b/test/jdk/java/util/jar/JarFile/SignedJarFileGetInputStream.java index 84ad357079d..9e524d5afd0 100644 --- a/test/jdk/java/util/jar/JarFile/SignedJarFileGetInputStream.java +++ b/test/jdk/java/util/jar/JarFile/SignedJarFileGetInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -24,45 +24,38 @@ /* @test * @bug 4845692 8206863 * @summary JarFile.getInputStream should not throw when jar file is signed - * @author Martin Buchholz + * @run junit SignedJarFileGetInputStream */ +import org.junit.jupiter.api.Test; + import java.io.*; import java.util.*; import java.util.jar.*; import java.util.zip.*; -public class SignedJarFileGetInputStream { - public static void main(String args[]) throws Throwable { - JarFile jar = new JarFile( - new File(System.getProperty("test.src", "."), "Signed.jar")); +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +public class SignedJarFileGetInputStream { + + @Test + void signedJarTest() throws IOException { + JarFile jar = new JarFile( + new File(System.getProperty("test.src", "."), "Signed.jar")); for (Enumeration e = jar.entries(); e.hasMoreElements();) { JarEntry entry = (JarEntry) e.nextElement(); - InputStream is = jar.getInputStream(new ZipEntry(entry.getName())); + InputStream is = assertDoesNotThrow(() -> jar.getInputStream(new ZipEntry(entry.getName()))); is.close(); } - // read(), available() on closed stream should throw IOException InputStream is = jar.getInputStream(new ZipEntry("Test.class")); is.close(); byte[] buffer = new byte[1]; - try { - is.read(); - throw new AssertionError("Should have thrown IOException"); - } catch (IOException success) {} - try { - is.read(buffer); - throw new AssertionError("Should have thrown IOException"); - } catch (IOException success) {} - try { - is.read(buffer, 0, buffer.length); - throw new AssertionError("Should have thrown IOException"); - } catch (IOException success) {} - try { - is.available(); - throw new AssertionError("Should have thrown IOException"); - } catch (IOException success) {} + assertThrows(IOException.class, () -> is.read()); + assertThrows(IOException.class, () -> is.read(buffer)); + assertThrows(IOException.class, () -> is.read(buffer, 0, buffer.length)); + assertThrows(IOException.class, () -> is.available()); } } diff --git a/test/jdk/java/util/jar/JarFile/SignedJarPendingBlock.java b/test/jdk/java/util/jar/JarFile/SignedJarPendingBlock.java index a6f9955a507..a6326be622a 100644 --- a/test/jdk/java/util/jar/JarFile/SignedJarPendingBlock.java +++ b/test/jdk/java/util/jar/JarFile/SignedJarPendingBlock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -21,15 +21,21 @@ * questions. */ -/** +/* * @test * @modules java.base/sun.security.tools.keytool * @summary JARs with pending block files (where .RSA comes before .SF) should verify correctly + * @run junit SignedJarPendingBlock */ import jdk.security.jarsigner.JarSigner; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.FieldSource; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -42,32 +48,47 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class SignedJarPendingBlock { - public static void main(String[] args) throws Exception { + static Path signed; + static Path pendingBlocks; + static Path invalid; + + // Construct the test data + @BeforeAll + static void setup() throws Exception { Path jar = createJarFile(); - Path signed = signJarFile(jar); - Path pendingBlocks = moveBlockFirst(signed); - Path invalid = invalidate(pendingBlocks); - - // 1: Regular signed JAR with no pending blocks should verify - checkSigned(signed); - - // 2: Signed jar with pending blocks should verify - checkSigned(pendingBlocks); - - // 3: Invalid signed jar with pending blocks should throw SecurityException - try { - checkSigned(invalid); - throw new Exception("Expected invalid digest to be detected"); - } catch (SecurityException se) { - // Ignore - } + signed = signJarFile(jar); + pendingBlocks = moveBlockFirst(signed); + invalid = invalidate(pendingBlocks); } - private static void checkSigned(Path b) throws Exception { - try (JarFile jf = new JarFile(b.toFile(), true)) { + // Regular signed JAR with no pending blocks should verify + @Test + void checkValidSignedJar() { + assertDoesNotThrow(() -> checkSigned(signed), + "Valid digest should not fail"); + } + // Signed jar with pending blocks should verify + @Test + void checkValidSignedPendingJar() { + assertDoesNotThrow(() -> checkSigned(pendingBlocks), + "Valid digest should not fail"); + } + + // Invalid signed jar with pending blocks should throw SecurityException + @Test + void checkInvalidSignedJar() { + assertThrows(SecurityException.class, () -> checkSigned(invalid), + "Expected invalid digest to be detected"); + } + + private static void checkSigned(Path b) throws IOException { + try (JarFile jf = new JarFile(b.toFile(), true)) { JarEntry je = jf.getJarEntry("a.txt"); try (InputStream in = jf.getInputStream(je)) { in.transferTo(OutputStream.nullOutputStream()); diff --git a/test/jdk/java/util/jar/JarFile/SorryClosed.java b/test/jdk/java/util/jar/JarFile/SorryClosed.java index 19481347394..38950539c57 100644 --- a/test/jdk/java/util/jar/JarFile/SorryClosed.java +++ b/test/jdk/java/util/jar/JarFile/SorryClosed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 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 @@ -24,43 +24,49 @@ /* @test * @bug 4910572 * @summary Accessing a closed jar file should generate IllegalStateException. - * @author Martin Buchholz + * @run junit SorryClosed */ +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.io.File; import java.util.jar.JarFile; import java.util.zip.ZipEntry; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class SorryClosed { - public static void main(String args[]) throws IOException { - File file = new File(System.getProperty("test.src","."), "test.jar"); - String testEntryName = "test.class"; + private static final File file = new File(System.getProperty("test.src", "."), "test.jar"); + private static final String testEntryName = "test.class"; - try { - JarFile f = new JarFile(file); - ZipEntry e = f.getEntry(testEntryName); - f.close(); - f.getInputStream(e); - } catch (IllegalStateException e) {} // OK + @Test + void getInputStreamTest() throws IOException { + JarFile f = new JarFile(file); + ZipEntry e = f.getEntry(testEntryName); + f.close(); + assertThrows(IllegalStateException.class, () -> f.getInputStream(e)); + } - try { - JarFile f = new JarFile(file); - f.close(); - f.getEntry(testEntryName); - } catch (IllegalStateException e) {} // OK + @Test + void getEntryTest() throws IOException { + JarFile f = new JarFile(file); + f.close(); + assertThrows(IllegalStateException.class, () -> f.getEntry(testEntryName)); + } - try { - JarFile f = new JarFile(file); - f.close(); - f.getJarEntry(testEntryName); - } catch (IllegalStateException e) {} // OK + @Test + void getJarEntryTest() throws IOException { + JarFile f = new JarFile(file); + f.close(); + assertThrows(IllegalStateException.class, () -> f.getJarEntry(testEntryName)); + } - try { - JarFile f = new JarFile(file); - f.close(); - f.getManifest(); - } catch (IllegalStateException e) {} // OK + @Test + void getManifestTest() throws IOException { + JarFile f = new JarFile(file); + f.close(); + assertThrows(IllegalStateException.class, f::getManifest); } } diff --git a/test/jdk/java/util/jar/JarFile/TurkCert.java b/test/jdk/java/util/jar/JarFile/TurkCert.java index 68e3d83e002..216fd535e2b 100644 --- a/test/jdk/java/util/jar/JarFile/TurkCert.java +++ b/test/jdk/java/util/jar/JarFile/TurkCert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -21,38 +21,35 @@ * questions. */ -/** +/* * @test * @bug 4624534 * @summary Make sure jar certificates work for Turkish locale - * @author kladko + * @run junit/othervm -Duser.language=tr -Duser.country=TR TurkCert */ +import org.junit.jupiter.api.Test; + import java.util.*; import java.util.jar.*; import java.security.cert.*; import java.io.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; + public class TurkCert { - public static void main(String[] args) throws Exception{ - Locale reservedLocale = Locale.getDefault(); - try { - Locale.setDefault(Locale.of("tr", "TR")); - File f = new File(System.getProperty("test.src","."), "test.jar"); - try (JarFile jf = new JarFile(f, true)) { - JarEntry je = (JarEntry)jf.getEntry("test.class"); - try (InputStream is = jf.getInputStream(je)) { - byte[] b = new byte[1024]; - while (is.read(b) != -1) { - } - } - if (je.getCertificates() == null) { - throw new Exception("Null certificate for test.class."); + + @Test + void turkishLocaleTest() throws IOException { + File f = new File(System.getProperty("test.src", "."), "test.jar"); + try (JarFile jf = new JarFile(f, true)) { + JarEntry je = (JarEntry)jf.getEntry("test.class"); + try (InputStream is = jf.getInputStream(je)) { + byte[] b = new byte[1024]; + while (is.read(b) != -1) { } } - } finally { - // restore the default locale - Locale.setDefault(reservedLocale); + assertNotNull(je.getCertificates(), "Null certificate for test.class."); } } } diff --git a/test/jdk/java/util/jar/JarFile/VerifySignedJar.java b/test/jdk/java/util/jar/JarFile/VerifySignedJar.java index bd5490502c9..e1602c0aa46 100644 --- a/test/jdk/java/util/jar/JarFile/VerifySignedJar.java +++ b/test/jdk/java/util/jar/JarFile/VerifySignedJar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -21,18 +21,20 @@ * questions. */ -/** +/* * @test - * @library /test/lib * @modules java.base/sun.security.x509 * @modules java.base/sun.security.tools.keytool * @bug 4419266 4842702 * @summary Make sure verifying signed Jar doesn't throw SecurityException + * @run junit VerifySignedJar */ import jdk.security.jarsigner.JarSigner; +import org.junit.jupiter.api.Test; import sun.security.tools.keytool.CertAndKeyGen; import sun.security.x509.X500Name; +import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -40,21 +42,24 @@ import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Collections; -import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; -import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import static jdk.test.lib.Utils.runAndCheckException; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class VerifySignedJar { - public static void main(String[] args) throws Exception { - + @Test + void signedJarSecurityExceptionTest() throws Exception { Path j = createJar(); Path s = signJar(j, keyEntry("cn=duke")); @@ -70,38 +75,30 @@ public class VerifySignedJar { } // Read ZIP and JAR entries by name - Objects.requireNonNull(jf.getEntry("getprop.class")); - Objects.requireNonNull(jf.getJarEntry("getprop.class")); + assertNotNull(jf.getEntry("getprop.class")); + assertNotNull(jf.getJarEntry("getprop.class")); // Make sure we throw NPE on null parameters - runAndCheckException(() -> jf.getEntry(null), NullPointerException.class); - runAndCheckException(() -> jf.getJarEntry(null), NullPointerException.class); - runAndCheckException(() -> jf.getInputStream(null), NullPointerException.class); + assertThrows(NullPointerException.class, () -> jf.getEntry(null)); + assertThrows(NullPointerException.class, () -> jf.getJarEntry(null)); + assertThrows(NullPointerException.class, () -> jf.getInputStream(null)); } catch (SecurityException se) { - throw new Exception("Got SecurityException when verifying signed " + - "jar:" + se); + fail("Got SecurityException when verifying signed jar:" + se); } } // Check that a JAR entry is signed by an expected DN - private static void checkSignedBy(JarEntry e, String expectedDn) throws Exception { + private static void checkSignedBy(JarEntry e, String expectedDn) { Certificate[] certs = e.getCertificates(); - if (certs == null || certs.length == 0) { - throw new Exception("JarEntry has no certificates: " + e.getName()); - } - - if (certs[0] instanceof X509Certificate x) { - String name = x.getSubjectX500Principal().getName(); - if (!name.equalsIgnoreCase(expectedDn)) { - throw new Exception("Expected entry signed by %s, was %s".formatted(name, expectedDn)); - } - } else { - throw new Exception("Expected JarEntry.getCertificate to return X509Certificate"); - } + assertNotNull(certs, "JarEntry has no certificates: " + e.getName()); + assertNotEquals(0, certs.length, "JarEntry has no certificates: " + e.getName()); + var x = assertInstanceOf(X509Certificate.class, certs[0], "Expected JarEntry.getCertificate to return X509Certificate"); + String name = x.getSubjectX500Principal().getName(); + assertTrue(name.equalsIgnoreCase(expectedDn), "Expected entry signed by %s, was %s".formatted(name, expectedDn)); } - private static Path createJar() throws Exception { + private static Path createJar() throws IOException { Path j = Path.of("unsigned.jar"); try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(j))){ out.putNextEntry(new JarEntry("getprop.class")); diff --git a/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java b/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java index 4d191d7b3cd..b4b7fda081e 100644 --- a/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java +++ b/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -33,8 +33,7 @@ * jdk.test.lib.JDKToolLauncher * MultiThreadLoad FooService * @modules java.base/jdk.internal.access:+open - * @run main MultiProviderTest - * @run main MultiProviderTest sign + * @run junit MultiProviderTest */ import java.io.File; @@ -51,27 +50,33 @@ import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; public class MultiProviderTest { private static final String METAINFO = "META-INF/services/FooService"; - private static String COMBO_CP = Utils.TEST_CLASS_PATH + File.pathSeparator; private static String TEST_CLASS_PATH = System.getProperty("test.classes", "."); - private static boolean signJars = false; static final int NUM_JARS = 5; + // Reset per each test run under JUnit default lifecycle + private boolean signJars = false; + private String COMBO_CP = Utils.TEST_CLASS_PATH + File.pathSeparator; private static final String KEYSTORE = "keystore.jks"; private static final String ALIAS = "JavaTest"; private static final String STOREPASS = "changeit"; private static final String KEYPASS = "changeit"; - public static void main(String[] args) throws Throwable { - signJars = args.length >=1 && args[0].equals("sign"); + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void classLoadingTest(boolean sign) throws Throwable { + signJars = sign; initialize(); List cmds = new ArrayList<>(); cmds.add(JDKToolFinder.getJDKTool("java")); @@ -86,18 +91,16 @@ public class MultiProviderTest { "MultiThreadLoad", TEST_CLASS_PATH)); - try { + assertDoesNotThrow(() -> { OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(cmds.stream() - .filter(t -> !t.isEmpty()) - .toArray(String[]::new)) + .filter(t -> !t.isEmpty()) + .toArray(String[]::new)) .shouldHaveExitValue(0); System.out.println("Output:" + outputAnalyzer.getOutput()); - } catch (Throwable t) { - throw new RuntimeException("Unexpected fail.", t); - } + }); } - public static void initialize() throws Throwable { + public void initialize() throws Throwable { if (signJars) { genKey(); } diff --git a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java index e8abec354ed..1ba25ef6985 100644 --- a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java +++ b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -31,7 +31,7 @@ * CreateMultiReleaseTestJars * jdk.test.lib.compiler.Compiler * jdk.test.lib.util.JarBuilder - * @run testng MultiReleaseJarAPI + * @run junit MultiReleaseJarAPI */ import java.io.File; @@ -44,37 +44,41 @@ import java.util.Map; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.jar.JarFile; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import jdk.test.lib.RandomFactory; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; public class MultiReleaseJarAPI { private static final Random RANDOM = RandomFactory.getRandom(); - String userdir = System.getProperty("user.dir","."); - CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); - File unversioned = new File(userdir, "unversioned.jar"); - File multirelease = new File(userdir, "multi-release.jar"); - File signedmultirelease = new File(userdir, "signed-multi-release.jar"); + private static final String userdir = System.getProperty("user.dir", "."); + private static final CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); + private static final File unversioned = new File(userdir, "unversioned.jar"); + private static final File multirelease = new File(userdir, "multi-release.jar"); + private static final File signedmultirelease = new File(userdir, "signed-multi-release.jar"); - - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { creator.compileEntries(); creator.buildUnversionedJar(); creator.buildMultiReleaseJar(); creator.buildSignedMultiReleaseJar(); } - @AfterClass - public void close() throws IOException { + @AfterAll + public static void close() throws IOException { Files.delete(unversioned.toPath()); Files.delete(multirelease.toPath()); Files.delete(signedmultirelease.toPath()); @@ -83,19 +87,19 @@ public class MultiReleaseJarAPI { @Test public void isMultiReleaseJar() throws Exception { try (JarFile jf = new JarFile(unversioned)) { - Assert.assertFalse(jf.isMultiRelease()); + assertFalse(jf.isMultiRelease()); } try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Runtime.version())) { - Assert.assertFalse(jf.isMultiRelease()); + assertFalse(jf.isMultiRelease()); } try (JarFile jf = new JarFile(multirelease)) { - Assert.assertTrue(jf.isMultiRelease()); + assertTrue(jf.isMultiRelease()); } try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.version())) { - Assert.assertTrue(jf.isMultiRelease()); + assertTrue(jf.isMultiRelease()); } testCustomMultiReleaseValue("true", true); @@ -155,45 +159,46 @@ public class MultiReleaseJarAPI { creator.buildCustomMultiReleaseJar(fileName, value, extraAttributes); File custom = new File(userdir, fileName); try (JarFile jf = new JarFile(custom, true, ZipFile.OPEN_READ, Runtime.version())) { - Assert.assertEquals(jf.isMultiRelease(), expected); + assertEquals(expected, jf.isMultiRelease()); } Files.delete(custom.toPath()); } - @DataProvider(name = "versions") - public Object[][] createVersionData() throws Exception { - return new Object[][]{ - {JarFile.baseVersion(), 8}, - {JarFile.runtimeVersion(), Runtime.version().major()}, - {Runtime.version(), Runtime.version().major()}, - {Runtime.Version.parse("7.1"), JarFile.baseVersion().major()}, - {Runtime.Version.parse("9"), 9}, - {Runtime.Version.parse("9.1.5-ea+200"), 9} - }; + public static Stream createVersionData() { + return Stream.of( + Arguments.of(JarFile.baseVersion(), 8), + Arguments.of(JarFile.runtimeVersion(), Runtime.version().major()), + Arguments.of(Runtime.version(), Runtime.version().major()), + Arguments.of(Runtime.Version.parse("7.1"), JarFile.baseVersion().major()), + Arguments.of(Runtime.Version.parse("9"), 9), + Arguments.of(Runtime.Version.parse("9.1.5-ea+200"), 9) + ); } - @Test(dataProvider="versions") + @ParameterizedTest + @MethodSource("createVersionData") public void testVersioning(Runtime.Version value, int xpected) throws Exception { Runtime.Version expected = Runtime.Version.parse(String.valueOf(xpected)); Runtime.Version base = JarFile.baseVersion(); // multi-release jar, opened as unversioned try (JarFile jar = new JarFile(multirelease)) { - Assert.assertEquals(jar.getVersion(), base); + assertEquals(base, jar.getVersion()); } System.err.println("test versioning for Release " + value); try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, value)) { - Assert.assertEquals(jf.getVersion(), expected); + assertEquals(expected, jf.getVersion()); } // regular, unversioned, jar try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, value)) { - Assert.assertEquals(jf.getVersion(), base); + assertEquals(base, jf.getVersion()); } } - @Test(dataProvider="versions") + @ParameterizedTest + @MethodSource("createVersionData") public void testAliasing(Runtime.Version version, int xpected) throws Exception { int n = Math.max(version.major(), JarFile.baseVersion().major()); Runtime.Version value = Runtime.Version.parse(String.valueOf(n)); @@ -231,7 +236,7 @@ public class MultiReleaseJarAPI { } assert versionedBytes.length > 0; - Assert.assertTrue(Arrays.equals(baseBytes, versionedBytes)); + assertTrue(Arrays.equals(baseBytes, versionedBytes)); } @Test @@ -243,11 +248,11 @@ public class MultiReleaseJarAPI { try (JarFile jf = new JarFile(multirelease)) { ze1 = jf.getEntry(vname); } - Assert.assertEquals(ze1.getName(), vname); + assertEquals(vname, ze1.getName()); try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.Version.parse("9"))) { ze2 = jf.getEntry(rname); } - Assert.assertEquals(ze2.getName(), rname); - Assert.assertNotEquals(ze1.getName(), ze2.getName()); + assertEquals(rname, ze2.getName()); + assertNotEquals(ze2.getName(), ze1.getName()); } } diff --git a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java index 16f764c6674..7d3a933a619 100644 --- a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java +++ b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarHttpProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -31,17 +31,17 @@ * @build CreateMultiReleaseTestJars * jdk.test.lib.compiler.Compiler * jdk.test.lib.util.JarBuilder - * @run testng MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=0 MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=8 MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=9 MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=100 MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties - * @run testng/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties + * @run junit MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=0 MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=8 MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=9 MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=100 MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties + * @run junit/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties */ import java.io.IOException; @@ -56,16 +56,19 @@ import java.util.concurrent.Executors; import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.SimpleFileServer; import jdk.test.lib.net.URIBuilder; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties { private HttpServer server; private ExecutorService executor; static final String TESTCONTEXT = "/multi-release.jar"; //mapped to local file path - @BeforeClass + @BeforeAll public void initialize() throws Exception { server = SimpleFileServer.createFileServer(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), Path.of(System.getProperty("user.dir", ".")), SimpleFileServer.OutputLevel.INFO); @@ -86,7 +89,7 @@ public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties { rootClass = cldr.loadClass("version.Main"); } - @AfterClass + @AfterAll public void close() throws IOException { // Windows requires server to stop before file is deleted if (server != null) { diff --git a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarProperties.java b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarProperties.java index cbc9516542e..a742a593868 100644 --- a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarProperties.java +++ b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -29,17 +29,17 @@ * @build CreateMultiReleaseTestJars * jdk.test.lib.compiler.Compiler * jdk.test.lib.util.JarBuilder - * @run testng MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=0 MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=8 MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=9 MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=100 MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties - * @run testng/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties + * @run junit MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=0 MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=8 MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=9 MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=100 MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties + * @run junit/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties */ import java.io.File; @@ -54,12 +54,15 @@ import java.nio.file.Files; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import static org.junit.jupiter.api.Assertions.*; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class MultiReleaseJarProperties { final static int BASE_VERSION = JarFile.baseVersion().major(); @@ -70,7 +73,7 @@ public class MultiReleaseJarProperties { protected ClassLoader cldr; protected Class rootClass; - @BeforeClass + @BeforeAll public void initialize() throws Exception { CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); creator.compileEntries(); @@ -97,7 +100,7 @@ public class MultiReleaseJarProperties { rootClass = cldr.loadClass("version.Main"); } - @AfterClass + @AfterAll public void close() throws IOException { ((URLClassLoader) cldr).close(); Files.delete(multirelease.toPath()); @@ -115,7 +118,7 @@ public class MultiReleaseJarProperties { protected void invokeMethod(Class vcls, int expected) throws Throwable { MethodType mt = MethodType.methodType(int.class); MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt); - Assert.assertEquals(expected, (int) mh.invoke(vcls.newInstance())); + assertEquals(expected, (int) mh.invoke(vcls.newInstance())); } /* @@ -177,7 +180,7 @@ public class MultiReleaseJarProperties { resource = new String(bytes); } String match = "return " + rtVersion + ";"; - Assert.assertTrue(resource.contains(match)); + assertTrue(resource.contains(match)); } @Test @@ -194,6 +197,6 @@ public class MultiReleaseJarProperties { resource = new String(bytes); } String match = "return " + rtVersion + ";"; - Assert.assertTrue(resource.contains(match)); + assertTrue(resource.contains(match)); } } diff --git a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarSecurity.java b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarSecurity.java index ec18cad0817..7e0bb2c1ca7 100644 --- a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarSecurity.java +++ b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -29,7 +29,7 @@ * @build CreateMultiReleaseTestJars * jdk.test.lib.compiler.Compiler * jdk.test.lib.util.JarBuilder - * @run testng MultiReleaseJarSecurity + * @run junit MultiReleaseJarSecurity */ import java.io.File; @@ -38,45 +38,45 @@ import java.io.InputStream; import java.nio.file.Files; import java.security.CodeSigner; import java.security.cert.Certificate; -import java.util.Arrays; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipFile; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class MultiReleaseJarSecurity { static final int MAJOR_VERSION = Runtime.version().major(); - String userdir = System.getProperty("user.dir","."); - File multirelease = new File(userdir, "multi-release.jar"); - File signedmultirelease = new File(userdir, "signed-multi-release.jar"); + static final String USER_DIR = System.getProperty("user.dir", "."); + static final File MULTI_RELEASE = new File(USER_DIR, "multi-release.jar"); + static final File SIGNED_MULTI_RELEASE = new File(USER_DIR, "signed-multi-release.jar"); - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); creator.compileEntries(); creator.buildMultiReleaseJar(); creator.buildSignedMultiReleaseJar(); } - @AfterClass - public void close() throws IOException { - Files.delete(multirelease.toPath()); - Files.delete(signedmultirelease.toPath()); + @AfterAll + public static void close() throws IOException { + Files.delete(MULTI_RELEASE.toPath()); + Files.delete(SIGNED_MULTI_RELEASE.toPath()); } @Test public void testCertsAndSigners() throws IOException { - try (JarFile jf = new JarFile(signedmultirelease, true, ZipFile.OPEN_READ, Runtime.version())) { + try (JarFile jf = new JarFile(SIGNED_MULTI_RELEASE, true, ZipFile.OPEN_READ, Runtime.version())) { CertsAndSigners vcas = new CertsAndSigners(jf, jf.getJarEntry("version/Version.class")); CertsAndSigners rcas = new CertsAndSigners(jf, jf.getJarEntry("META-INF/versions/" + MAJOR_VERSION + "/version/Version.class")); - Assert.assertTrue(Arrays.equals(rcas.getCertificates(), vcas.getCertificates())); - Assert.assertTrue(Arrays.equals(rcas.getCodeSigners(), vcas.getCodeSigners())); + assertArrayEquals(rcas.getCertificates(), vcas.getCertificates()); + assertArrayEquals(rcas.getCodeSigners(), vcas.getCodeSigners()); } } diff --git a/test/jdk/java/util/jar/JarFile/mrjar/TestVersionedStream.java b/test/jdk/java/util/jar/JarFile/mrjar/TestVersionedStream.java index dbff39f814e..f88ab0821ae 100644 --- a/test/jdk/java/util/jar/JarFile/mrjar/TestVersionedStream.java +++ b/test/jdk/java/util/jar/JarFile/mrjar/TestVersionedStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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,7 +28,7 @@ * @library /test/lib * @build jdk.test.lib.Platform * jdk.test.lib.util.FileUtils - * @run testng TestVersionedStream + * @run junit TestVersionedStream */ import java.io.File; @@ -41,7 +41,6 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -54,18 +53,20 @@ import java.util.stream.Stream; import java.util.zip.ZipFile; import jdk.test.lib.util.FileUtils; -import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; public class TestVersionedStream { - private final Path userdir; - private final Set unversionedEntryNames; + private static final Path userdir; + private static final Set unversionedEntryNames; private static final int LATEST_VERSION = Runtime.version().feature(); - public TestVersionedStream() throws IOException { + static { userdir = Paths.get(System.getProperty("user.dir", ".")); // These are not real class files even though they end with .class. @@ -91,8 +92,7 @@ public class TestVersionedStream { "--release " + LATEST_VERSION + " -C v" + LATEST_VERSION + " ."); System.out.println("Contents of mmr.jar\n======="); - - try(JarFile jf = new JarFile("mmr.jar")) { + try (JarFile jf = new JarFile("mmr.jar")) { unversionedEntryNames = jf.stream() .map(je -> je.getName()) .peek(System.out::println) @@ -100,13 +100,14 @@ public class TestVersionedStream { ? nm.replaceFirst("META-INF/versions/\\d+/", "") : nm) .collect(Collectors.toCollection(LinkedHashSet::new)); + } catch (IOException e) { + throw new RuntimeException("Failed to init \"unversionedEntryNames\"", e); } - System.out.println("======="); } - @AfterClass - public void close() throws IOException { + @AfterAll + public static void close() throws IOException { Files.walk(userdir, 1) .filter(p -> !p.equals(userdir)) .forEach(p -> { @@ -122,53 +123,41 @@ public class TestVersionedStream { }); } - @DataProvider - public Object[][] data() { - return new Object[][] { - {Runtime.Version.parse("8")}, - {Runtime.Version.parse("9")}, - {Runtime.Version.parse("10")}, - {Runtime.Version.parse(Integer.toString(LATEST_VERSION))}, - {JarFile.baseVersion()}, - {JarFile.runtimeVersion()} - }; + public static Stream arguments() { + return Stream.of( + Runtime.Version.parse("8"), + Runtime.Version.parse("9"), + Runtime.Version.parse("10"), + Runtime.Version.parse(Integer.toString(LATEST_VERSION)), + JarFile.baseVersion(), + JarFile.runtimeVersion() + ); } - @Test(dataProvider="data") - public void test(Runtime.Version version) throws Exception { + @ParameterizedTest + @MethodSource("arguments") + public void versionTest(Runtime.Version version) throws Exception { try (JarFile jf = new JarFile(new File("mmr.jar"), false, ZipFile.OPEN_READ, version); Stream jes = jf.versionedStream()) { - Assert.assertNotNull(jes); + assertNotNull(jes); // put versioned entries in list so we can reuse them List versionedEntries = jes.collect(Collectors.toList()); - Assert.assertTrue(versionedEntries.size() > 0); + assertTrue(versionedEntries.size() > 0); // also keep the names - List versionedNames = new ArrayList<>(versionedEntries.size()); + List versionedNames = versionedEntries.stream() + .map(JarEntry::getName) + .collect(Collectors.toList()); // verify the correct order while building enames - Iterator allIt = unversionedEntryNames.iterator(); - Iterator verIt = versionedEntries.iterator(); - boolean match = false; + List unversionedOrder = new ArrayList<>(unversionedEntryNames); + unversionedOrder.retainAll(versionedNames); - while (verIt.hasNext()) { - match = false; - if (!allIt.hasNext()) break; - String name = verIt.next().getName(); - versionedNames.add(name); - while (allIt.hasNext()) { - if (name.equals(allIt.next())) { - match = true; - break; - } - } - } - if (!match) { - Assert.fail("versioned entries not in same order as unversioned entries"); - } + assertIterableEquals(unversionedOrder, versionedNames, + "versioned entries not in same order as unversioned entries"); // verify the contents: // value.[0] end of the path @@ -205,21 +194,21 @@ public class TestVersionedStream { expected.put("q/Bar.class", new String[] { "q/Bar.class", "META-INF/versions/10/q/Bar.class"}); } else { - Assert.fail("Test out of date, please add more cases"); + fail("Test out of date, please add more cases"); } } expected.entrySet().stream().forEach(e -> { String name = e.getKey(); int i = versionedNames.indexOf(name); - Assert.assertTrue(i != -1, name + " not in enames"); + assertTrue(i != -1, name + " not in enames"); JarEntry je = versionedEntries.get(i); try (InputStream is = jf.getInputStream(je)) { String s = new String(is.readAllBytes()).replaceAll(System.lineSeparator(), ""); // end of the path - Assert.assertTrue(s.endsWith(e.getValue()[0]), s); + assertTrue(s.endsWith(e.getValue()[0]), s); // getRealName() - Assert.assertTrue(je.getRealName().equals(e.getValue()[1])); + assertTrue(je.getRealName().equals(e.getValue()[1])); } catch (IOException x) { throw new UncheckedIOException(x); } @@ -227,12 +216,12 @@ public class TestVersionedStream { if (!unversionedEntryNames.contains("META-INF/Foo.class") || versionedNames.indexOf("META-INF/Foo.class") != -1) { - Assert.fail("versioned META-INF/Foo.class test failed"); + fail("versioned META-INF/Foo.class test failed"); } } } - private void createFiles(String... files) { + private static void createFiles(String... files) { ArrayList list = new ArrayList(); Arrays.stream(files) .map(f -> Paths.get(userdir.toAbsolutePath().toString(), f)) @@ -248,7 +237,7 @@ public class TestVersionedStream { }}); } - private void jar(String args) { + private static void jar(String args) { ToolProvider jar = ToolProvider.findFirst("jar").orElseThrow(); jar.run(System.out, System.err, args.split(" +")); } diff --git a/test/jdk/java/util/jar/JarInputStream/EmptyJar.java b/test/jdk/java/util/jar/JarInputStream/EmptyJar.java index 9762455460c..ff961844685 100644 --- a/test/jdk/java/util/jar/JarInputStream/EmptyJar.java +++ b/test/jdk/java/util/jar/JarInputStream/EmptyJar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,18 +26,22 @@ @summary Make sure JarInputStream constructor will not throw NullPointerException when the JAR file is empty. - */ + @run junit EmptyJar + */ + +import org.junit.jupiter.api.Test; import java.util.jar.*; import java.io.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + public class EmptyJar { - public static void main(String args[]) throws Exception { - try { - JarInputStream is = new JarInputStream - (new ByteArrayInputStream(new byte[0])); - } catch (NullPointerException e) { - throw new Exception("unexpected NullPointerException"); - } + + @Test + void npeTest() { + // Ensure no NPE is thrown + assertDoesNotThrow(() -> new JarInputStream(new ByteArrayInputStream(new byte[0])), + "unexpected NullPointerException"); } } diff --git a/test/jdk/java/util/jar/JarInputStream/ExtraFileInMetaInf.java b/test/jdk/java/util/jar/JarInputStream/ExtraFileInMetaInf.java index 45865ec0b8f..fe88d225444 100644 --- a/test/jdk/java/util/jar/JarInputStream/ExtraFileInMetaInf.java +++ b/test/jdk/java/util/jar/JarInputStream/ExtraFileInMetaInf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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 @@ -27,16 +27,24 @@ * @summary JarInputStream doesn't provide certificates for some file under META-INF * @modules java.base/sun.security.tools.keytool * jdk.jartool/sun.security.tools.jarsigner + * @run junit ExtraFileInMetaInf */ +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import java.util.jar.*; import java.io.*; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -public class ExtraFileInMetaInf { - public static void main(String args[]) throws Exception { +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +public class ExtraFileInMetaInf { + + @BeforeAll + static void setup() throws Exception { // Create a zip file with 2 entries try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("x.jar"))) { @@ -44,7 +52,6 @@ public class ExtraFileInMetaInf { zos.write(new byte[10]); zos.putNextEntry(new ZipEntry("x")); zos.write(new byte[10]); - zos.close(); } // Sign it @@ -54,7 +61,10 @@ public class ExtraFileInMetaInf { "-keyalg rsa -alias a -dname CN=A -genkeypair").split(" ")); sun.security.tools.jarsigner.Main.main( "-keystore ks -storepass changeit x.jar a".split(" ")); + } + @Test + void checkSignedTest() throws IOException { // Check if the entries are signed try (JarInputStream jis = new JarInputStream(new FileInputStream("x.jar"))) { @@ -63,9 +73,7 @@ public class ExtraFileInMetaInf { String name = je.toString(); if (name.equals("META-INF/SUB/file") || name.equals("x")) { while (jis.read(new byte[1000]) >= 0); - if (je.getCertificates() == null) { - throw new Exception(name + " not signed"); - } + assertNotNull(je.getCertificates(), name + " not signed"); } } } diff --git a/test/jdk/java/util/jar/JarInputStream/ScanSignedJar.java b/test/jdk/java/util/jar/JarInputStream/ScanSignedJar.java index 86dbf793e74..23e0a81c4cf 100644 --- a/test/jdk/java/util/jar/JarInputStream/ScanSignedJar.java +++ b/test/jdk/java/util/jar/JarInputStream/ScanSignedJar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, 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 @@ -25,13 +25,19 @@ * @test * @bug 6284489 * @summary Confirm that JarEntry.getCertificates identifies signed entries. + * @run junit ScanSignedJar */ +import org.junit.jupiter.api.Test; + +import java.io.IOException; import java.net.URL; import java.security.CodeSigner; import java.security.cert.Certificate; import java.util.jar.*; +import static org.junit.jupiter.api.Assertions.fail; + /* * Confirm that the signed entries in a JAR file are identified correctly * when JarEntry.getCertificates is used to extract the signer's certificates. @@ -43,13 +49,13 @@ import java.util.jar.*; public class ScanSignedJar { private static final String JAR_LOCATION = - "file:" + - System.getProperty("test.src", ".") + - System.getProperty("file.separator") + - "signed.jar"; - - public static void main(String[] args) throws Exception { + "file:" + + System.getProperty("test.src", ".") + + System.getProperty("file.separator") + + "signed.jar"; + @Test + void signedJarTest() throws IOException { System.out.println("Opening " + JAR_LOCATION + "..."); JarInputStream inStream = new JarInputStream(new URL(JAR_LOCATION).openStream(), true); @@ -71,7 +77,7 @@ public class ScanSignedJar { System.out.println("[unsigned]\t" + name + "\t(" + size + " bytes)"); if (name.equals("Count.class")) { - throw new Exception("Count.class should be signed"); + fail("Count.class should be signed"); } } else if (signers != null && certificates != null) { System.out.println("[" + signers.length + @@ -80,7 +86,7 @@ public class ScanSignedJar { } else { System.out.println("[*ERROR*]\t" + name + "\t(" + size + " bytes)"); - throw new Exception("Cannot determine whether the entry is " + + fail("Cannot determine whether the entry is " + "signed or unsigned (signers[] doesn't match certs[])."); } } diff --git a/test/jdk/java/util/jar/JarInputStream/TestIndexedJarWithBadSignature.java b/test/jdk/java/util/jar/JarInputStream/TestIndexedJarWithBadSignature.java index 2e74eb10069..47dc572aa6b 100644 --- a/test/jdk/java/util/jar/JarInputStream/TestIndexedJarWithBadSignature.java +++ b/test/jdk/java/util/jar/JarInputStream/TestIndexedJarWithBadSignature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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,32 +26,32 @@ * @bug 6544278 * @summary Confirm the JarInputStream throws the SecurityException when * verifying an indexed jar file with corrupted signature + * @run junit TestIndexedJarWithBadSignature */ +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.io.FileInputStream; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class TestIndexedJarWithBadSignature { - public static void main(String...args) throws Throwable { + @Test + void securityExceptionTest() throws IOException { try (JarInputStream jis = new JarInputStream( - new FileInputStream(System.getProperty("test.src", ".") + - System.getProperty("file.separator") + - "BadSignedJar.jar"))) - { - JarEntry je1 = jis.getNextJarEntry(); - while(je1!=null){ - System.out.println("Jar Entry1==>"+je1.getName()); - je1 = jis.getNextJarEntry(); // This should throw Security Exception - } - throw new RuntimeException( - "Test Failed:Security Exception not being thrown"); - } catch (IOException ie){ - ie.printStackTrace(); - } catch (SecurityException e) { - System.out.println("Test passed: Security Exception thrown as expected"); + new FileInputStream(System.getProperty("test.src", ".") + + System.getProperty("file.separator") + + "BadSignedJar.jar"))) { + assertThrows(SecurityException.class, () -> { + JarEntry je1; + while ((je1 = jis.getNextJarEntry()) != null) { + System.out.println("Jar Entry1==>" + je1.getName()); + } + }); } } } diff --git a/test/jdk/java/util/jar/Manifest/CreateManifest.java b/test/jdk/java/util/jar/Manifest/CreateManifest.java index 6655089b56d..2eee24fb0be 100644 --- a/test/jdk/java/util/jar/Manifest/CreateManifest.java +++ b/test/jdk/java/util/jar/Manifest/CreateManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -27,40 +27,43 @@ * @summary Jar tools fails to generate manifest correctly when boundary condition hit * @modules jdk.jartool/sun.tools.jar * @compile -XDignore.symbol.file=true CreateManifest.java - * @run main CreateManifest + * @run junit CreateManifest */ +import org.junit.jupiter.api.Test; + +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.jar.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; + public class CreateManifest { -public static void main(String arg[]) throws Exception { + @Test + void boundaryTest() throws IOException { + String jarFileName = "test.jar"; + String ManifestName = "MANIFEST.MF"; - String jarFileName = "test.jar"; - String ManifestName = "MANIFEST.MF"; + // create the MANIFEST.MF file + Files.write(Paths.get(ManifestName), FILE_CONTENTS.getBytes()); - // create the MANIFEST.MF file - Files.write(Paths.get(ManifestName), FILE_CONTENTS.getBytes()); + String [] args = new String [] { "cvfm", jarFileName, ManifestName}; + sun.tools.jar.Main jartool = + new sun.tools.jar.Main(System.out, System.err, "jar"); + jartool.run(args); - String [] args = new String [] { "cvfm", jarFileName, ManifestName}; - sun.tools.jar.Main jartool = - new sun.tools.jar.Main(System.out, System.err, "jar"); - jartool.run(args); - - try (JarFile jf = new JarFile(jarFileName)) { - Manifest m = jf.getManifest(); - String result = m.getMainAttributes().getValue("Class-path"); - if (result == null) - throw new RuntimeException("Failed to add Class-path attribute to manifest"); - } finally { - Files.deleteIfExists(Paths.get(jarFileName)); - Files.deleteIfExists(Paths.get(ManifestName)); + try (JarFile jf = new JarFile(jarFileName)) { + Manifest m = jf.getManifest(); + String result = m.getMainAttributes().getValue("Class-path"); + assertNotNull(result, "Failed to add Class-path attribute to manifest"); + } finally { + Files.deleteIfExists(Paths.get(jarFileName)); + Files.deleteIfExists(Paths.get(ManifestName)); + } } -} - private static final String FILE_CONTENTS = "Class-path: \n" + " /ade/dtsao_re/oracle/emcore//lib/em-core-testconsole-uimodel.jar \n" + diff --git a/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java b/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java index 1e272015087..bb3cd6e23ef 100644 --- a/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java +++ b/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 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 @@ -21,7 +21,9 @@ * questions. */ -import java.io.File; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; @@ -30,23 +32,29 @@ import java.util.concurrent.Callable; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; +import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; -/** +/* * @test * @bug 8216362 - * @run main/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest yes - * @run main/othervm IncludeInExceptionsTest - * @summary Verify that the property jdk.net.includeInExceptions works as expected + * @run junit/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest + * @run junit/othervm IncludeInExceptionsTest + * @summary Verify that the property jdk.includeInExceptions works as expected * when an error occurs while reading an invalid Manifest file. */ + /* * @see Manifest#Manifest(JarVerifier,InputStream,String) * @see Manifest#getErrorPosition */ public class IncludeInExceptionsTest { + private static final boolean includeInExceptions = System.getProperty("jdk.includeInExceptions") != null; + static final String FILENAME = "Unique-Filename-Expected-In_Msg.jar"; static final byte[] INVALID_MANIFEST = ( @@ -66,36 +74,26 @@ public class IncludeInExceptionsTest { return jar; } - static void test(Callable attempt, boolean includeInExceptions) throws Exception { - try { - attempt.call(); - throw new AssertionError("Excpected Exception not thrown"); - } catch (IOException e) { - boolean foundFileName = e.getMessage().contains(FILENAME); - if(includeInExceptions && !foundFileName) { - throw new AssertionError("JAR file name expected but not found in error message"); - } else if (foundFileName && !includeInExceptions) { - throw new AssertionError("JAR file name found but should not be in error message"); - } + @ParameterizedTest + @MethodSource("manifests") + void testInvalidManifest(Callable attempt) { + var ioException = assertThrows(IOException.class, attempt::call); + boolean foundFileName = ioException.getMessage().contains(FILENAME); + if (includeInExceptions && !foundFileName) { + fail("JAR file name expected but not found in error message"); + } else if (foundFileName && !includeInExceptions) { + fail("JAR file name found but should not be in error message"); } } - public static void main(String[] args) throws Exception { - - boolean includeInExceptions; - if(args.length > 0) { - includeInExceptions = true; - System.out.println("**** Running test WITH -Djdk.includeInExceptions=jar"); - } else { - includeInExceptions = false; - System.out.println("**** Running test WITHOUT -Djdk.includeInExceptions=jar"); - } - - test(() -> new JarFile(createJarInvalidManifest(FILENAME)).getManifest(), - includeInExceptions); - test(() -> new JarFile(createJarInvalidManifest("Verifying-" + FILENAME), - true).getManifest(), includeInExceptions); + static Stream> manifests() { + Callable jarName = () -> new JarFile(createJarInvalidManifest(FILENAME)).getManifest(); + Callable jarNameVerify = () -> new JarFile(createJarInvalidManifest("Verifying-" + FILENAME), + true).getManifest(); + return Stream.of( + jarName, + jarNameVerify + ); } - } diff --git a/test/jdk/java/util/jar/Manifest/LineBreakLineWidth.java b/test/jdk/java/util/jar/Manifest/LineBreakLineWidth.java index 8009db75601..f33bd7a276f 100644 --- a/test/jdk/java/util/jar/Manifest/LineBreakLineWidth.java +++ b/test/jdk/java/util/jar/Manifest/LineBreakLineWidth.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -21,23 +21,22 @@ * questions. */ -import static java.nio.charset.StandardCharsets.UTF_8; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.jar.Manifest; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.*; /** * @test * @bug 6372077 - * @run testng LineBreakLineWidth + * @run junit LineBreakLineWidth * @summary write valid manifests with respect to line breaks * and read any line width */ @@ -99,16 +98,10 @@ public class LineBreakLineWidth { assertMainAndSectionValues(mf, name, value); } - static void writeInvalidManifestThrowsException(String name, String value) - throws IOException { - try { - writeManifest(name, value); - } catch (IllegalArgumentException e) { - // no invalid manifest was produced which is considered acceptable - return; - } - - fail("no error writing manifest considered invalid"); + static void writeInvalidManifestThrowsException(String name, String value) { + // no invalid manifest was produced which is considered acceptable + assertThrows(IllegalArgumentException.class, + () -> writeManifest(name, value), "no error writing manifest considered invalid"); } /** @@ -278,8 +271,8 @@ public class LineBreakLineWidth { String mainValue = mf.getMainAttributes().getValue(name); String sectionValue = mf.getAttributes(name).getValue(name); - assertEquals(value, mainValue, "value different in main section"); - assertEquals(value, sectionValue, "value different in named section"); + assertEquals(mainValue, value, "value different in main section"); + assertEquals(sectionValue, value, "value different in named section"); } } diff --git a/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java b/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java index 28921bba868..cad0048f098 100644 --- a/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java +++ b/test/jdk/java/util/jar/Manifest/ValueUtf8Coding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -21,8 +21,6 @@ * questions. */ -import static java.nio.charset.StandardCharsets.UTF_8; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -32,13 +30,15 @@ import java.util.jar.Manifest; import java.util.List; import java.util.ArrayList; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.*; /** * @test * @bug 8066619 8351567 - * @run testng ValueUtf8Coding + * @run junit ValueUtf8Coding * @summary Tests encoding and decoding manifest header values to and from * UTF-8 with the complete Unicode character set. */ /* @@ -187,11 +187,11 @@ public class ValueUtf8Coding { String value = values.get(i); Name name = azName(i); - assertEquals(mf.getMainAttributes().getValue(name), value, + assertEquals(value, mf.getMainAttributes().getValue(name), "main attributes header value"); Attributes attributes = mf.getAttributes(value); assertNotNull(attributes, "named section"); - assertEquals(attributes.getValue(name), value, + assertEquals(value, attributes.getValue(name), "named section attributes value"); } } diff --git a/test/jdk/java/util/jar/Manifest/WriteBinaryStructure.java b/test/jdk/java/util/jar/Manifest/WriteBinaryStructure.java index 379de6b1adb..f91b518aa6c 100644 --- a/test/jdk/java/util/jar/Manifest/WriteBinaryStructure.java +++ b/test/jdk/java/util/jar/Manifest/WriteBinaryStructure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -21,22 +21,22 @@ * questions. */ -import static java.nio.charset.StandardCharsets.UTF_8; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.util.jar.Manifest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; -/** +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.*; + +/* * @test * @bug 8066619 - * @run testng WriteBinaryStructure * @summary Tests that jar manifests are written in a particular structure + * @run junit WriteBinaryStructure */ public class WriteBinaryStructure { @@ -47,10 +47,11 @@ public class WriteBinaryStructure { mf.getMainAttributes().put(new Name("Key"), "Value"); ByteArrayOutputStream buf = new ByteArrayOutputStream(); mf.write(buf); - assertEquals(buf.toByteArray(), ( + assertArrayEquals(( "Manifest-Version: 1.0\r\n" + "Key: Value\r\n" + - "\r\n").getBytes(UTF_8)); + "\r\n").getBytes(UTF_8), + buf.toByteArray()); } @Test @@ -62,12 +63,12 @@ public class WriteBinaryStructure { attributes.put(new Name("Key"), "Value"); ByteArrayOutputStream buf = new ByteArrayOutputStream(); mf.write(buf); - assertEquals(buf.toByteArray(), ( + assertArrayEquals(( "Manifest-Version: 1.0\r\n" + "\r\n" + "Name: Individual-Section-Name\r\n" + "Key: Value\r\n" + - "\r\n").getBytes(UTF_8)); + "\r\n").getBytes(UTF_8), + buf.toByteArray()); } - } diff --git a/test/jdk/java/util/jar/TestExtra.java b/test/jdk/java/util/jar/TestExtra.java index fa6408ba570..776b5e67d14 100644 --- a/test/jdk/java/util/jar/TestExtra.java +++ b/test/jdk/java/util/jar/TestExtra.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -21,180 +21,84 @@ * questions. */ -/** +/* * @test * @bug 6480504 6303183 * @summary Test that client-provided data in the extra field is written and * read correctly, taking into account the JAR_MAGIC written into the extra - * field of the first entry of JAR files. - * @author Dave Bristor + * field of the first entry of JAR files. ZIP file specific. + * @run junit TestExtra */ +import org.junit.jupiter.api.Test; + import java.io.*; import java.nio.charset.Charset; -import java.util.Arrays; +import java.nio.charset.StandardCharsets; import java.util.jar.*; import java.util.zip.*; -/** - * Tests that the get/set operations on extra data in zip and jar files work - * as advertised. The base class tests ZIP files, the member class - * TestJarExtra checks JAR files. - */ -public class TestExtra { - static final int JAR_MAGIC = 0xcafe; // private IN JarOutputStream.java - static final int TEST_HEADER = 0xbabe; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; - static final Charset ascii = Charset.forName("ASCII"); +// Tests that the get/set operations on extra data in ZIP files work as advertised. +public class TestExtra { + + static final int TEST_HEADER = 0xbabe; + static final Charset ascii = StandardCharsets.US_ASCII; // ZipEntry extra data static final byte[][] extra = new byte[][] { - ascii.encode("hello, world").array(), - ascii.encode("foo bar").array() + ascii.encode("hello, world").array(), + ascii.encode("foo bar").array() }; - // For naming entries in JAR/ZIP streams - int count = 1; + // For naming entries in ZIP streams + static int count = 1; // Use byte arrays instead of files - ByteArrayOutputStream baos; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); - // JAR/ZIP content written here. - ZipOutputStream zos; + // ZIP content written here. + ZipOutputStream zos = assertDoesNotThrow(() -> getOutputStream(baos)); - public static void realMain(String[] args) throws Throwable{ - new TestExtra().testHeaderPlusData(); - - new TestJarExtra().testHeaderPlusData(); - new TestJarExtra().testHeaderOnly(); - new TestJarExtra().testClientJarMagic(); - } - - TestExtra() { - try { - baos = new ByteArrayOutputStream(); - zos = getOutputStream(baos); - } catch (Throwable t) { - unexpected(t); - } - } - - /** Test that a header + data set by client works. */ + // Test that a header + data set by client works. + @Test void testHeaderPlusData() throws IOException { for (byte[] b : extra) { ZipEntry ze = getEntry(); byte[] data = new byte[b.length + 4]; set16(data, 0, TEST_HEADER); set16(data, 2, b.length); - for (int i = 0; i < b.length; i++) { - data[i + 4] = b[i]; - } + System.arraycopy(b, 0, data, 4, b.length); ze.setExtra(data); zos.putNextEntry(ze); } zos.close(); - ZipInputStream zis = getInputStream(); - ZipEntry ze = zis.getNextEntry(); checkEntry(ze, 0, extra[0].length); - ze = zis.getNextEntry(); checkEntry(ze, 1, extra[1].length); } - /** Test that a header only (i.e., no extra "data") set by client works. */ - void testHeaderOnly() throws IOException { - ZipEntry ze = getEntry(); - byte[] data = new byte[4]; - set16(data, 0, TEST_HEADER); - set16(data, 2, 0); // Length of data is 0. - ze.setExtra(data); - zos.putNextEntry(ze); - - zos.close(); - - ZipInputStream zis = getInputStream(); - - ze = zis.getNextEntry(); - checkExtra(data, ze.getExtra()); - checkEntry(ze, 0, 0); - } - - /** Tests the client providing extra data which uses JAR_MAGIC header. */ - void testClientJarMagic() throws IOException { - ZipEntry ze = getEntry(); - byte[] data = new byte[8]; - - set16(data, 0, TEST_HEADER); - set16(data, 2, 0); // Length of data is 0. - set16(data, 4, JAR_MAGIC); - set16(data, 6, 0); // Length of data is 0. - - ze.setExtra(data); - zos.putNextEntry(ze); - - zos.close(); - - ZipInputStream zis = getInputStream(); - ze = zis.getNextEntry(); - byte[] e = ze.getExtra(); - checkExtra(data, ze.getExtra()); - checkEntry(ze, 0, 0); - } - - // check if all "expected" extra fields equal to their - // corresponding fields in "extra". The "extra" might have - // timestamp fields added by ZOS. - static void checkExtra(byte[] expected, byte[] extra) { - if (expected == null) - return; - int off = 0; - int len = expected.length; - while (off + 4 < len) { - int tag = get16(expected, off); - int sz = get16(expected, off + 2); - int off0 = 0; - int len0 = extra.length; - boolean matched = false; - while (off0 + 4 < len0) { - int tag0 = get16(extra, off0); - int sz0 = get16(extra, off0 + 2); - if (tag == tag0 && sz == sz0) { - matched = true; - for (int i = 0; i < sz; i++) { - if (expected[off + i] != extra[off0 +i]) - matched = false; - } - break; - } - off0 += (4 + sz0); - } - if (!matched) { - fail("Expected extra data [tag=" + tag + "sz=" + sz + "] check failed"); - } - off += (4 + sz); - } - } - - /** Check that the entry's extra data is correct. */ + // Check that the entry's extra data is correct. void checkEntry(ZipEntry ze, int count, int dataLength) { byte[] extraData = ze.getExtra(); byte[] data = getField(TEST_HEADER, extraData); - if (!check(data != null, "unexpected null data for TEST_HEADER")) { - return; - } - + assertNotNull(data, "unexpected null data for TEST_HEADER"); if (dataLength == 0) { - check(data.length == 0, "unexpected non-zero data length for TEST_HEADER"); + assertEquals(0, data.length, "unexpected non-zero data length for TEST_HEADER"); } else { - check(Arrays.equals(extra[count], data), - "failed to get entry " + ze.getName() - + ", expected " + new String(extra[count]) + ", got '" + new String(data) + "'"); + assertArrayEquals(data, extra[count], + "failed to get entry " + ze.getName() + + ", expected " + new String(extra[count]) + ", got '" + new String(data) + "'"); } } - /** Look up descriptor in data, returning corresponding byte[]. */ + // Look up descriptor in data, returning corresponding byte[]. static byte[] getField(int descriptor, byte[] data) { byte[] rc = null; try { @@ -218,69 +122,23 @@ public class TestExtra { ZipInputStream getInputStream() { return new ZipInputStream( - new ByteArrayInputStream(baos.toByteArray())); + new ByteArrayInputStream(baos.toByteArray())); } - ZipOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException { + ZipOutputStream getOutputStream(ByteArrayOutputStream baos) { return new ZipOutputStream(baos); } - ZipInputStream getInputStream(ByteArrayInputStream bais) throws IOException { - return new ZipInputStream(bais); + ZipEntry getEntry() { + return new ZipEntry("zip" + count++ + ".txt"); } - ZipEntry getEntry() { return new ZipEntry("zip" + count++ + ".txt"); } - - - private static int get16(byte[] b, int off) { - return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8); + static int get16(byte[] b, int off) { + return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); } - private static void set16(byte[] b, int off, int value) { - b[off+0] = (byte)value; - b[off+1] = (byte)(value >> 8); + static void set16(byte[] b, int off, int value) { + b[off + 0] = (byte) value; + b[off + 1] = (byte) (value >> 8); } - - /** Test extra field of a JAR file. */ - static class TestJarExtra extends TestExtra { - ZipOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException { - return new JarOutputStream(baos); - } - - ZipInputStream getInputStream(ByteArrayInputStream bais) throws IOException { - return new JarInputStream(bais); - } - - ZipEntry getEntry() { return new ZipEntry("jar" + count++ + ".txt"); } - - void checkEntry(ZipEntry ze, int count, int dataLength) { - // zeroth entry should have JAR_MAGIC - if (count == 0) { - byte[] extraData = ze.getExtra(); - byte[] data = getField(JAR_MAGIC, extraData); - if (!check(data != null, "unexpected null data for JAR_MAGIC")) { - check(data.length != 0, "unexpected non-zero data length for JAR_MAGIC"); - } - } - // In a jar file, the first ZipEntry should have both JAR_MAGIC - // and the TEST_HEADER, so check that also. - super.checkEntry(ze, count, dataLength); - } - } - - //--------------------- Infrastructure --------------------------- - static volatile int passed = 0, failed = 0; - static void pass() {passed++;} - static void fail() {failed++; Thread.dumpStack();} - static void fail(String msg) {System.out.println(msg); fail();} - static void unexpected(Throwable t) {failed++; t.printStackTrace();} - static void check(boolean cond) {if (cond) pass(); else fail();} - static boolean check(boolean cond, String msg) {if (cond) pass(); else fail(msg); return cond; } - static void equal(Object x, Object y) { - if (x == null ? y == null : x.equals(y)) pass(); - else fail(x + " not equal to " + y);} - public static void main(String[] args) throws Throwable { - try {realMain(args);} catch (Throwable t) {unexpected(t);} - System.out.println("\nPassed = " + passed + " failed = " + failed); - if (failed > 0) throw new Error("Some tests failed");} } diff --git a/test/jdk/java/util/jar/TestJarExtra.java b/test/jdk/java/util/jar/TestJarExtra.java new file mode 100644 index 00000000000..6619727bfe8 --- /dev/null +++ b/test/jdk/java/util/jar/TestJarExtra.java @@ -0,0 +1,224 @@ +/* + * 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 6480504 6303183 + * @summary Test that client-provided data in the extra field is written and + * read correctly, taking into account the JAR_MAGIC written into the extra + * field of the first entry of JAR files. Jar file specific. + * @run junit TestJarExtra + */ + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +// Tests that the get/set operations on extra data in JAR files work as advertised. +public class TestJarExtra { + + static final int JAR_MAGIC = 0xcafe; // private IN JarOutputStream.java + static final int TEST_HEADER = 0xbabe; + // For naming entries in JAR streams + static int count = 1; + static final Charset ascii = StandardCharsets.US_ASCII; + // ZipEntry extra data + static final byte[][] extra = new byte[][] { + ascii.encode("hello, world").array(), + ascii.encode("foo bar").array() + }; + + // Use byte arrays instead of files + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // JAR content written here. + JarOutputStream jos = assertDoesNotThrow(() -> getOutputStream(baos)); + + // Test that a header + data set by client works. + @Test + void testHeaderPlusData() throws IOException { + for (byte[] b : extra) { + ZipEntry ze = getEntry(); + byte[] data = new byte[b.length + 4]; + set16(data, 0, TEST_HEADER); + set16(data, 2, b.length); + System.arraycopy(b, 0, data, 4, b.length); + ze.setExtra(data); + jos.putNextEntry(ze); + } + jos.close(); + ZipInputStream zis = getInputStream(); + ZipEntry ze = zis.getNextEntry(); + checkEntry(ze, 0, extra[0].length); + ze = zis.getNextEntry(); + checkEntry(ze, 1, extra[1].length); + } + + // Test that a header only (i.e., no extra "data") set by client works. + @Test + void testHeaderOnly() throws IOException { + ZipEntry ze = getEntry(); + byte[] data = new byte[4]; + set16(data, 0, TEST_HEADER); + set16(data, 2, 0); // Length of data is 0. + ze.setExtra(data); + jos.putNextEntry(ze); + jos.close(); + ZipInputStream zis = getInputStream(); + ze = zis.getNextEntry(); + checkExtra(data, ze.getExtra()); + checkEntry(ze, 0, 0); + } + + // Tests the client providing extra data which uses JAR_MAGIC header. + @Test + void testClientJarMagic() throws IOException { + ZipEntry ze = getEntry(); + byte[] data = new byte[8]; + set16(data, 0, TEST_HEADER); + set16(data, 2, 0); // Length of data is 0. + set16(data, 4, JAR_MAGIC); + set16(data, 6, 0); // Length of data is 0. + ze.setExtra(data); + jos.putNextEntry(ze); + jos.close(); + ZipInputStream zis = getInputStream(); + ze = zis.getNextEntry(); + byte[] e = ze.getExtra(); + checkExtra(data, ze.getExtra()); + checkEntry(ze, 0, 0); + } + + JarOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException { + return new JarOutputStream(baos); + } + + ZipInputStream getInputStream() { + return new ZipInputStream( + new ByteArrayInputStream(baos.toByteArray())); + } + + ZipEntry getEntry() { + return new ZipEntry("jar" + count++ + ".txt"); + } + + // check if all "expected" extra fields equal to their + // corresponding fields in "extra". The "extra" might have + // timestamp fields added by ZOS. + static void checkExtra(byte[] expected, byte[] extra) { + if (expected == null) + return; + int off = 0; + int len = expected.length; + while (off + 4 < len) { + int tag = get16(expected, off); + int sz = get16(expected, off + 2); + int off0 = 0; + int len0 = extra.length; + boolean matched = false; + while (off0 + 4 < len0) { + int tag0 = get16(extra, off0); + int sz0 = get16(extra, off0 + 2); + if (tag == tag0 && sz == sz0) { + matched = true; + for (int i = 0; i < sz; i++) { + if (expected[off + i] != extra[off0 + i]) + matched = false; + } + break; + } + off0 += (4 + sz0); + } + if (!matched) { + fail("Expected extra data [tag=" + tag + "sz=" + sz + "] check failed"); + } + off += (4 + sz); + } + } + + void checkEntry(ZipEntry ze, int count, int dataLength) { + // zeroth entry should have JAR_MAGIC + if (count == 0) { + byte[] extraData = ze.getExtra(); + byte[] data = getField(JAR_MAGIC, extraData); + assertNotNull(data, "unexpected null data for JAR_MAGIC"); + assertEquals(0, data.length, "unexpected non-zero data length for JAR_MAGIC"); + } + // In a JAR file, the first ZipEntry should have both JAR_MAGIC + // and the TEST_HEADER, so check that also. + byte[] extraData = ze.getExtra(); + byte[] data = getField(TEST_HEADER, extraData); + assertNotNull(data, "unexpected null data for TEST_HEADER"); + if (dataLength == 0) { + assertEquals(0, data.length, "unexpected non-zero data length for TEST_HEADER"); + } else { + assertArrayEquals(data, extra[count], + "failed to get entry " + ze.getName() + + ", expected " + new String(extra[count]) + ", got '" + new String(data) + "'"); + } + } + + // Look up descriptor in data, returning corresponding byte[]. + static byte[] getField(int descriptor, byte[] data) { + byte[] rc = null; + try { + int i = 0; + while (i < data.length) { + if (get16(data, i) == descriptor) { + int length = get16(data, i + 2); + rc = new byte[length]; + for (int j = 0; j < length; j++) { + rc[j] = data[i + 4 + j]; + } + return rc; + } + i += get16(data, i + 2) + 4; + } + } catch (ArrayIndexOutOfBoundsException e) { + // descriptor not found + } + return rc; + } + + static int get16(byte[] b, int off) { + return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); + } + + static void set16(byte[] b, int off, int value) { + b[off + 0] = (byte) value; + b[off + 1] = (byte) (value >> 8); + } +} From 63c1cb3ad1cc3be22fd8b401894acf37683dad67 Mon Sep 17 00:00:00 2001 From: Frederic Thevenet Date: Mon, 2 Mar 2026 18:30:29 +0000 Subject: [PATCH 120/636] 8378702: jdk.test.lib.Platform.isMusl() may return false negative on Alpine Linux Reviewed-by: stuefe, rriggs --- test/lib/jdk/test/lib/Platform.java | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 75fdef048bc..170e53930d8 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, 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 @@ -33,6 +33,7 @@ import java.nio.file.Paths; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; import static java.util.Locale.ROOT; public class Platform { @@ -188,14 +189,17 @@ public class Platform { } public static boolean isMusl() { - try { - ProcessBuilder pb = new ProcessBuilder("ldd", "--version"); + var lddPath = Stream.of("/bin/ldd", "/usr/bin/ldd").filter(p -> Files.exists(Path.of(p))).findFirst(); + if (lddPath.isPresent()) { + ProcessBuilder pb = new ProcessBuilder(lddPath.get(), "--version"); pb.redirectErrorStream(true); - Process p = pb.start(); - BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream())); - String l = b.readLine(); - if (l != null && l.contains("musl")) { return true; } - } catch(Exception e) { + try (Process p = pb.start()) { + BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream())); + String l = b.readLine(); + return (l != null && l.contains("musl")); + } catch (Exception e) { + e.printStackTrace(); + } } return false; } From 8009a714ba81af8b6a7b422f510ae5d6509a73a7 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 3 Mar 2026 03:25:23 +0000 Subject: [PATCH 121/636] 8378810: Enable missing FFM test via jtreg requires for RISC-V Reviewed-by: fyang --- .../jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java index 2877e8a4a85..ff853d174b1 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java @@ -27,7 +27,8 @@ * @summary guarantee(loc != NULL) failed: missing saved register with native invoke * * @requires vm.flavor == "server" - * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "ppc64" | os.arch == "ppc64le" + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | + os.arch == "aarch64" | os.arch == "ppc64" | os.arch == "ppc64le" | os.arch == "riscv64" * @requires vm.gc.Shenandoah * * @run main/native/othervm --enable-native-access=ALL-UNNAMED -XX:+UnlockDiagnosticVMOptions From 9e240554194e9c2be2425eb983d248923c217da5 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 3 Mar 2026 06:49:10 +0000 Subject: [PATCH 122/636] 8372246: LogOutputList gtests should not use LogConfiguration LogOutputs Reviewed-by: jsjolen, aartemov --- .../gtest/logging/test_logOutputList.cpp | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/test/hotspot/gtest/logging/test_logOutputList.cpp b/test/hotspot/gtest/logging/test_logOutputList.cpp index 64becbd7ef7..df3bb2c3227 100644 --- a/test/hotspot/gtest/logging/test_logOutputList.cpp +++ b/test/hotspot/gtest/logging/test_logOutputList.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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,16 @@ static LogOutput* dummy_output(size_t id) { return reinterpret_cast(id + 1); } +static LogStdoutOutput* dummy_stdout() { + static LogStdoutOutput dummy_stdout; + return &dummy_stdout; +} + +static LogStderrOutput* dummy_stderr() { + static LogStderrOutput dummy_stderr; + return &dummy_stderr; +} + // Randomly update and verify some outputs some number of times TEST(LogOutputList, set_output_level_update) { const size_t TestOutputCount = 10; @@ -174,7 +184,7 @@ TEST(LogOutputList, is_level_single_output) { for (size_t i = LogLevel::First; i < LogLevel::Count; i++) { LogLevelType level = static_cast(i); LogOutputList list; - list.set_output_level(LogConfiguration::StdoutLog, level); + list.set_output_level(dummy_stdout(), level); for (size_t j = LogLevel::First; j < LogLevel::Count; j++) { LogLevelType other = static_cast(j); // Verify that levels finer than the current level for stdout are reported as disabled, @@ -202,8 +212,8 @@ TEST(LogOutputList, is_level_empty) { // Test is_level() on lists with two outputs on different levels TEST(LogOutputList, is_level_multiple_outputs) { for (size_t i = LogLevel::First; i < LogLevel::Count - 1; i++) { - LogOutput* dummy1 = LogConfiguration::StdoutLog; - LogOutput* dummy2 = LogConfiguration::StderrLog; + LogOutput* dummy1 = dummy_stdout(); + LogOutput* dummy2 = dummy_stderr(); LogLevelType first = static_cast(i); LogLevelType second = static_cast(i + 1); LogOutputList list; @@ -227,19 +237,19 @@ TEST(LogOutputList, level_for) { LogOutputList list; // Ask the empty list about stdout, stderr - EXPECT_EQ(LogLevel::Off, list.level_for(LogConfiguration::StdoutLog)); - EXPECT_EQ(LogLevel::Off, list.level_for(LogConfiguration::StderrLog)); + EXPECT_EQ(LogLevel::Off, list.level_for(dummy_stdout())); + EXPECT_EQ(LogLevel::Off, list.level_for(dummy_stderr())); // Ask for level in a list with two outputs on different levels - list.set_output_level(LogConfiguration::StdoutLog, LogLevel::Info); - list.set_output_level(LogConfiguration::StderrLog, LogLevel::Trace); - EXPECT_EQ(LogLevel::Info, list.level_for(LogConfiguration::StdoutLog)); - EXPECT_EQ(LogLevel::Trace, list.level_for(LogConfiguration::StderrLog)); + list.set_output_level(dummy_stdout(), LogLevel::Info); + list.set_output_level(dummy_stderr(), LogLevel::Trace); + EXPECT_EQ(LogLevel::Info, list.level_for(dummy_stdout())); + EXPECT_EQ(LogLevel::Trace, list.level_for(dummy_stderr())); // Remove and ask again - list.set_output_level(LogConfiguration::StdoutLog, LogLevel::Off); - EXPECT_EQ(LogLevel::Off, list.level_for(LogConfiguration::StdoutLog)); - EXPECT_EQ(LogLevel::Trace, list.level_for(LogConfiguration::StderrLog)); + list.set_output_level(dummy_stdout(), LogLevel::Off); + EXPECT_EQ(LogLevel::Off, list.level_for(dummy_stdout())); + EXPECT_EQ(LogLevel::Trace, list.level_for(dummy_stderr())); // Ask about an unknown output LogOutput* dummy = dummy_output(4711); @@ -252,5 +262,5 @@ TEST(LogOutputList, level_for) { } // Make sure the stderr level is still the same - EXPECT_EQ(LogLevel::Trace, list.level_for(LogConfiguration::StderrLog)); + EXPECT_EQ(LogLevel::Trace, list.level_for(dummy_stderr())); } From 545cf60763feba343b8eed42332a8dc0e51c50f6 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 3 Mar 2026 06:49:24 +0000 Subject: [PATCH 123/636] 8372242: Gtest LogTagSet.defaults should run in OTHER VM Reviewed-by: jsjolen, aartemov --- test/hotspot/gtest/logging/test_logTagSet.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/hotspot/gtest/logging/test_logTagSet.cpp b/test/hotspot/gtest/logging/test_logTagSet.cpp index 4c00644d25b..b53272eafdb 100644 --- a/test/hotspot/gtest/logging/test_logTagSet.cpp +++ b/test/hotspot/gtest/logging/test_logTagSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -30,8 +30,9 @@ #include "utilities/ostream.hpp" #include "unittest.hpp" -// Test the default level for each tagset -TEST_VM(LogTagSet, defaults) { +// Test the default level for each tagset, runs in other VM to ensure no other +// test have changed a LogTagSet. +TEST_OTHER_VM(LogTagSet, defaults) { for (LogTagSet* ts = LogTagSet::first(); ts != nullptr; ts = ts->next()) { char buf[256]; ts->label(buf, sizeof(buf)); From f4da2d56b7785569e1b88625bb766675b20438cc Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Tue, 3 Mar 2026 08:38:42 +0000 Subject: [PATCH 124/636] 8378684: Fix -Wdeprecated-declarations warnings from gtest by clang23 Reviewed-by: erikj, kbarrett --- make/hotspot/lib/CompileGtest.gmk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index 327014b1e9d..4b21d481049 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -63,6 +63,10 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \ unused-result zero-as-null-pointer-constant, \ DISABLED_WARNINGS_clang := format-nonliteral undef unused-result \ zero-as-null-pointer-constant, \ + $(comment Disable deprecated-declarations warnings to workaround) \ + $(comment clang18+glibc12 bug https://github.com/llvm/llvm-project/issues/76515) \ + $(comment until the clang bug has been fixed) \ + DISABLED_WARNINGS_clang_gtest-all.cc := deprecated-declarations, \ DISABLED_WARNINGS_microsoft := 4530, \ DEFAULT_CFLAGS := false, \ CFLAGS := $(JVM_CFLAGS) \ From 7e9e64966b47c788c91f934b5fca5cd31ad465b3 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Tue, 3 Mar 2026 08:39:04 +0000 Subject: [PATCH 125/636] 8378083: Mark shenandoah/generational/TestOldGrowthTriggers.java as flagless Reviewed-by: wkemper --- .../gc/shenandoah/generational/TestOldGrowthTriggers.java | 2 +- test/lib/jdk/test/lib/process/ProcessTools.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java index a66b9161c7e..7af81fabe13 100644 --- a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java @@ -27,6 +27,7 @@ * @summary Test that growth of old-gen triggers old-gen marking * @key intermittent * @requires vm.gc.Shenandoah + * @requires vm.flagless * @library /test/lib * @run driver TestOldGrowthTriggers */ @@ -34,7 +35,6 @@ import java.util.*; import java.math.BigInteger; -import jdk.test.lib.Asserts; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; diff --git a/test/lib/jdk/test/lib/process/ProcessTools.java b/test/lib/jdk/test/lib/process/ProcessTools.java index fe9c1de9f30..e7dd20c6286 100644 --- a/test/lib/jdk/test/lib/process/ProcessTools.java +++ b/test/lib/jdk/test/lib/process/ProcessTools.java @@ -575,7 +575,7 @@ public final class ProcessTools { * "test.vm.opts" and "test.java.opts" and this method will * not do that. * - *

If you still chose to use + *

If you still choose to use * createLimitedTestJavaProcessBuilder() you should probably use * it in combination with @requires vm.flagless JTREG * anotation as to not waste energy and test resources. @@ -609,7 +609,7 @@ public final class ProcessTools { * "test.vm.opts" and "test.java.opts" and this method will * not do that. * - *

If you still chose to use + *

If you still choose to use * createLimitedTestJavaProcessBuilder() you should probably use * it in combination with @requires vm.flagless JTREG * anotation as to not waste energy and test resources. From c0c8bdd294c5ca56307123c7f10ec10ba33c4bca Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Tue, 3 Mar 2026 09:23:22 +0000 Subject: [PATCH 126/636] 8378948: Remove unused local variable in RunnerGSInserterThread Reviewed-by: syan, jiefu --- test/hotspot/gtest/utilities/test_concurrentHashtable.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp b/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp index 77055e92256..8094e93b944 100644 --- a/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp +++ b/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp @@ -888,7 +888,6 @@ public: virtual ~RunnerGSInserterThread(){} void premain() { - volatile bool timeout = false; _start = START; _range = RANGE; CHTTestThread* tt[GSTEST_THREAD_COUNT]; From 0b183bf2d608bedf118607b1471fbf1e68813a08 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Tue, 3 Mar 2026 09:39:06 +0000 Subject: [PATCH 127/636] 8312116: GenShen: make instantaneous allocation rate triggers more timely Reviewed-by: wkemper --- .../shenandoahAdaptiveHeuristics.cpp | 559 ++++++++++++++++-- .../shenandoahAdaptiveHeuristics.hpp | 76 +++ .../shenandoahGenerationalHeuristics.cpp | 6 + .../shenandoahGenerationalHeuristics.hpp | 2 + .../heuristics/shenandoahHeuristics.cpp | 30 +- .../heuristics/shenandoahHeuristics.hpp | 33 +- .../heuristics/shenandoahYoungHeuristics.cpp | 3 +- .../gc/shenandoah/shenandoahConcurrentGC.cpp | 1 + .../gc/shenandoah/shenandoahControlThread.cpp | 19 +- .../gc/shenandoah/shenandoahDegeneratedGC.cpp | 1 + .../share/gc/shenandoah/shenandoahFreeSet.cpp | 62 +- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 70 ++- .../share/gc/shenandoah/shenandoahFullGC.cpp | 4 +- .../gc/shenandoah/shenandoahGeneration.cpp | 7 +- .../gc/shenandoah/shenandoahGeneration.hpp | 2 +- .../shenandoahGenerationalControlThread.cpp | 3 +- .../shenandoah/shenandoahGenerationalHeap.cpp | 22 +- .../shenandoah/shenandoahGenerationalHeap.hpp | 3 + .../share/gc/shenandoah/shenandoahHeap.cpp | 27 +- .../share/gc/shenandoah/shenandoahHeap.hpp | 3 + .../gc/shenandoah/shenandoahOldGeneration.cpp | 2 + .../shenandoah/shenandoahRegulatorThread.cpp | 16 +- .../shenandoah/shenandoahRegulatorThread.hpp | 3 + .../gc/shenandoah/shenandoah_globals.hpp | 53 ++ 24 files changed, 899 insertions(+), 108 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 7a8bd55c795..ac8b3ebdf37 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -33,6 +33,7 @@ #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" #include "runtime/globals.hpp" @@ -59,14 +60,95 @@ const double ShenandoahAdaptiveHeuristics::HIGHEST_EXPECTED_AVAILABLE_AT_END = 0 const double ShenandoahAdaptiveHeuristics::MINIMUM_CONFIDENCE = 0.319; // 25% const double ShenandoahAdaptiveHeuristics::MAXIMUM_CONFIDENCE = 3.291; // 99.9% + +// To enable detection of GC time trends, we keep separate track of the recent history of gc time. During initialization, +// for example, the amount of live memory may be increasing, which is likely to cause the GC times to increase. This history +// allows us to predict increasing GC times rather than always assuming average recent GC time is the best predictor. +const size_t ShenandoahAdaptiveHeuristics::GC_TIME_SAMPLE_SIZE = 3; + +// We also keep separate track of recently sampled allocation rates for two purposes: +// 1. The number of samples examined to determine acceleration of allocation is represented by +// ShenandoahRateAccelerationSampleSize +// 2. The number of most recent samples averaged to determine a momentary allocation spike is represented by +// ShenandoahMomentaryAllocationRateSpikeSampleSize + +// Allocation rates are sampled by the regulator thread, which typically runs every ms. There may be jitter in the scheduling +// of the regulator thread. To reduce signal noise and synchronization overhead, we do not sample allocation rate with every +// iteration of the regulator. We prefer sample time longer than 1 ms so that there can be a statistically significant number +// of allocations occuring within each sample period. The regulator thread samples allocation rate only if at least +// ShenandoahAccelerationSamplePeriod ms have passed since it previously sampled the allocation rate. +// +// This trigger responds much more quickly than the traditional trigger, which monitors 100 ms spans. When acceleration is +// detected, the impact of acceleration on anticipated consumption of available memory is also much more impactful +// than the assumed constant allocation rate consumption of available memory. + ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics(ShenandoahSpaceInfo* space_info) : ShenandoahHeuristics(space_info), _margin_of_error_sd(ShenandoahAdaptiveInitialConfidence), _spike_threshold_sd(ShenandoahAdaptiveInitialSpikeThreshold), _last_trigger(OTHER), - _available(Moving_Average_Samples, ShenandoahAdaptiveDecayFactor) { } + _available(Moving_Average_Samples, ShenandoahAdaptiveDecayFactor), + _free_set(nullptr), + _previous_acceleration_sample_timestamp(0.0), + _gc_time_first_sample_index(0), + _gc_time_num_samples(0), + _gc_time_timestamps(NEW_C_HEAP_ARRAY(double, GC_TIME_SAMPLE_SIZE, mtGC)), + _gc_time_samples(NEW_C_HEAP_ARRAY(double, GC_TIME_SAMPLE_SIZE, mtGC)), + _gc_time_xy(NEW_C_HEAP_ARRAY(double, GC_TIME_SAMPLE_SIZE, mtGC)), + _gc_time_xx(NEW_C_HEAP_ARRAY(double, GC_TIME_SAMPLE_SIZE, mtGC)), + _gc_time_sum_of_timestamps(0), + _gc_time_sum_of_samples(0), + _gc_time_sum_of_xy(0), + _gc_time_sum_of_xx(0), + _gc_time_m(0.0), + _gc_time_b(0.0), + _gc_time_sd(0.0), + _spike_acceleration_buffer_size(MAX2(ShenandoahRateAccelerationSampleSize, 1+ShenandoahMomentaryAllocationRateSpikeSampleSize)), + _spike_acceleration_first_sample_index(0), + _spike_acceleration_num_samples(0), + _spike_acceleration_rate_samples(NEW_C_HEAP_ARRAY(double, _spike_acceleration_buffer_size, mtGC)), + _spike_acceleration_rate_timestamps(NEW_C_HEAP_ARRAY(double, _spike_acceleration_buffer_size, mtGC)) { + } -ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() {} +ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() { + FREE_C_HEAP_ARRAY(double, _spike_acceleration_rate_samples); + FREE_C_HEAP_ARRAY(double, _spike_acceleration_rate_timestamps); + FREE_C_HEAP_ARRAY(double, _gc_time_timestamps); + FREE_C_HEAP_ARRAY(double, _gc_time_samples); + FREE_C_HEAP_ARRAY(double, _gc_time_xy); + FREE_C_HEAP_ARRAY(double, _gc_time_xx); +} + +void ShenandoahAdaptiveHeuristics::initialize() { + ShenandoahHeuristics::initialize(); +} + +void ShenandoahAdaptiveHeuristics::post_initialize() { + ShenandoahHeuristics::post_initialize(); + _free_set = ShenandoahHeap::heap()->free_set(); + assert(!ShenandoahHeap::heap()->mode()->is_generational(), "ShenandoahGenerationalHeuristics overrides this method"); + compute_headroom_adjustment(); +} + +void ShenandoahAdaptiveHeuristics::compute_headroom_adjustment() { + // The trigger threshold represents mutator available - "head room". + // We plan for GC to finish before the amount of allocated memory exceeds trigger threshold. This is the same as saying we + // intend to finish GC before the amount of available memory is less than the allocation headroom. Headroom is the planned + // safety buffer to allow a small amount of additional allocation to take place in case we were overly optimistic in delaying + // our trigger. + size_t capacity = ShenandoahHeap::heap()->soft_max_capacity(); + size_t spike_headroom = capacity / 100 * ShenandoahAllocSpikeFactor; + size_t penalties = capacity / 100 * _gc_time_penalties; + _headroom_adjustment = spike_headroom + penalties; +} + +void ShenandoahAdaptiveHeuristics::start_idle_span() { + compute_headroom_adjustment(); +} + +void ShenandoahAdaptiveHeuristics::adjust_penalty(intx step) { + ShenandoahHeuristics::adjust_penalty(step); +} void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, RegionData* data, size_t size, @@ -76,8 +158,8 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand // The logic for cset selection in adaptive is as follows: // // 1. We cannot get cset larger than available free space. Otherwise we guarantee OOME - // during evacuation, and thus guarantee full GC. In practice, we also want to let - // application to allocate something. This is why we limit CSet to some fraction of + // during evacuation, and thus guarantee full GC. In practice, we also want to let the + // application allocate during concurrent GC. This is why we limit CSet to some fraction of // available space. In non-overloaded heap, max_cset would contain all plausible candidates // over garbage threshold. // @@ -108,6 +190,7 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand size_t cur_cset = 0; size_t cur_garbage = 0; + // Regions are sorted in order of decreasing garbage for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); @@ -126,6 +209,88 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand } } +void ShenandoahAdaptiveHeuristics::add_degenerated_gc_time(double timestamp, double gc_time) { + // Conservatively add sample into linear model If this time is above the predicted concurrent gc time + if (predict_gc_time(timestamp) < gc_time) { + add_gc_time(timestamp, gc_time); + } +} + +void ShenandoahAdaptiveHeuristics::add_gc_time(double timestamp, double gc_time) { + // Update best-fit linear predictor of GC time + uint index = (_gc_time_first_sample_index + _gc_time_num_samples) % GC_TIME_SAMPLE_SIZE; + if (_gc_time_num_samples == GC_TIME_SAMPLE_SIZE) { + _gc_time_sum_of_timestamps -= _gc_time_timestamps[index]; + _gc_time_sum_of_samples -= _gc_time_samples[index]; + _gc_time_sum_of_xy -= _gc_time_xy[index]; + _gc_time_sum_of_xx -= _gc_time_xx[index]; + } + _gc_time_timestamps[index] = timestamp; + _gc_time_samples[index] = gc_time; + _gc_time_xy[index] = timestamp * gc_time; + _gc_time_xx[index] = timestamp * timestamp; + + _gc_time_sum_of_timestamps += _gc_time_timestamps[index]; + _gc_time_sum_of_samples += _gc_time_samples[index]; + _gc_time_sum_of_xy += _gc_time_xy[index]; + _gc_time_sum_of_xx += _gc_time_xx[index]; + + if (_gc_time_num_samples < GC_TIME_SAMPLE_SIZE) { + _gc_time_num_samples++; + } else { + _gc_time_first_sample_index = (_gc_time_first_sample_index + 1) % GC_TIME_SAMPLE_SIZE; + } + + if (_gc_time_num_samples == 1) { + // The predictor is constant (horizontal line) + _gc_time_m = 0; + _gc_time_b = gc_time; + _gc_time_sd = 0.0; + } else if (_gc_time_num_samples == 2) { + // Two points define a line + double delta_y = gc_time - _gc_time_samples[_gc_time_first_sample_index]; + double delta_x = timestamp - _gc_time_timestamps[_gc_time_first_sample_index]; + _gc_time_m = delta_y / delta_x; + + // y = mx + b + // so b = y0 - mx0 + _gc_time_b = gc_time - _gc_time_m * timestamp; + _gc_time_sd = 0.0; + } else { + _gc_time_m = ((_gc_time_num_samples * _gc_time_sum_of_xy - _gc_time_sum_of_timestamps * _gc_time_sum_of_samples) / + (_gc_time_num_samples * _gc_time_sum_of_xx - _gc_time_sum_of_timestamps * _gc_time_sum_of_timestamps)); + _gc_time_b = (_gc_time_sum_of_samples - _gc_time_m * _gc_time_sum_of_timestamps) / _gc_time_num_samples; + double sum_of_squared_deviations = 0.0; + for (size_t i = 0; i < _gc_time_num_samples; i++) { + uint index = (_gc_time_first_sample_index + i) % GC_TIME_SAMPLE_SIZE; + double x = _gc_time_timestamps[index]; + double predicted_y = _gc_time_m * x + _gc_time_b; + double deviation = predicted_y - _gc_time_samples[index]; + sum_of_squared_deviations += deviation * deviation; + } + _gc_time_sd = sqrt(sum_of_squared_deviations / _gc_time_num_samples); + } +} + +double ShenandoahAdaptiveHeuristics::predict_gc_time(double timestamp_at_start) { + return _gc_time_m * timestamp_at_start + _gc_time_b + _gc_time_sd * _margin_of_error_sd;; +} + +void ShenandoahAdaptiveHeuristics::add_rate_to_acceleration_history(double timestamp, double rate) { + uint new_sample_index = + (_spike_acceleration_first_sample_index + _spike_acceleration_num_samples) % _spike_acceleration_buffer_size; + _spike_acceleration_rate_timestamps[new_sample_index] = timestamp; + _spike_acceleration_rate_samples[new_sample_index] = rate; + if (_spike_acceleration_num_samples == _spike_acceleration_buffer_size) { + _spike_acceleration_first_sample_index++; + if (_spike_acceleration_first_sample_index == _spike_acceleration_buffer_size) { + _spike_acceleration_first_sample_index = 0; + } + } else { + _spike_acceleration_num_samples++; + } +} + void ShenandoahAdaptiveHeuristics::record_cycle_start() { ShenandoahHeuristics::record_cycle_start(); _allocation_rate.allocation_counter_reset(); @@ -133,6 +298,10 @@ void ShenandoahAdaptiveHeuristics::record_cycle_start() { void ShenandoahAdaptiveHeuristics::record_success_concurrent() { ShenandoahHeuristics::record_success_concurrent(); + double now = os::elapsedTime(); + + // Should we not add GC time if this was an abbreviated cycle? + add_gc_time(_cycle_start, elapsed_cycle_time()); size_t available = _space_info->available(); @@ -185,6 +354,7 @@ void ShenandoahAdaptiveHeuristics::record_success_concurrent() { void ShenandoahAdaptiveHeuristics::record_degenerated() { ShenandoahHeuristics::record_degenerated(); + add_degenerated_gc_time(_precursor_cycle_start, elapsed_degenerated_cycle_time()); // Adjust both trigger's parameters in the case of a degenerated GC because // either of them should have triggered earlier to avoid this case. adjust_margin_of_error(DEGENERATE_PENALTY_SD); @@ -236,6 +406,24 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() { size_t available = _space_info->soft_mutator_available(); size_t allocated = _space_info->bytes_allocated_since_gc_start(); + double avg_cycle_time = 0; + double avg_alloc_rate = 0; + double now = get_most_recent_wake_time(); + size_t allocatable_words = this->allocatable(available); + double predicted_future_accelerated_gc_time = 0.0; + size_t allocated_bytes_since_last_sample = 0; + double instantaneous_rate_words_per_second = 0.0; + size_t consumption_accelerated = 0; + double acceleration = 0.0; + double current_rate_by_acceleration = 0.0; + size_t min_threshold = min_free_threshold(); + double predicted_future_gc_time = 0; + double future_planned_gc_time = 0; + bool future_planned_gc_time_is_average = false; + double avg_time_to_deplete_available = 0.0; + bool is_spiking = false; + double spike_time_to_deplete_available = 0.0; + log_debug(gc, ergo)("should_start_gc calculation: available: " PROPERFMT ", soft_max_capacity: " PROPERFMT ", " "allocated_since_gc_start: " PROPERFMT, PROPERFMTARGS(available), PROPERFMTARGS(capacity), PROPERFMTARGS(allocated)); @@ -250,7 +438,6 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() { _last_trigger = OTHER; - size_t min_threshold = min_free_threshold(); if (available < min_threshold) { log_trigger("Free (Soft) (" PROPERFMT ") is below minimum threshold (" PROPERFMT ")", PROPERFMTARGS(available), PROPERFMTARGS(min_threshold)); @@ -271,55 +458,227 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() { return true; } } - // Check if allocation headroom is still okay. This also factors in: - // 1. Some space to absorb allocation spikes (ShenandoahAllocSpikeFactor) - // 2. Accumulated penalties from Degenerated and Full GC - size_t allocation_headroom = available; - size_t spike_headroom = capacity / 100 * ShenandoahAllocSpikeFactor; - size_t penalties = capacity / 100 * _gc_time_penalties; + // The test (3 * allocated > available) below is intended to prevent triggers from firing so quickly that there + // has not been sufficient time to create garbage that can be reclaimed during the triggered GC cycle. If we trigger before + // garbage has been created, the concurrent GC will find no garbage. This has been observed to result in degens which + // experience OOM during evac or that experience "bad progress", both of which escalate to Full GC. Note that garbage that + // was allocated following the start of the current GC cycle cannot be reclaimed in this GC cycle. Here is the derivation + // of the expression: + // + // Let R (runway) represent the total amount of memory that can be allocated following the start of GC(N). The runway + // represents memory available at the start of the current GC plus garbage reclaimed by the current GC. In a balanced, + // fully utilized configuration, we will be starting each new GC cycle immediately following completion of the preceding + // GC cycle. In this configuration, we would expect half of R to be consumed during concurrent cycle GC(N) and half + // to be consumed during concurrent GC(N+1). + // + // Assume we want to delay GC trigger until: A/V > 0.33 + // This is equivalent to enforcing that: A > 0.33V + // which is: 3A > V + // Since A+V equals R, we have: A + 3A > A + V = R + // which is to say that: A > R/4 + // + // Postponing the trigger until at least 1/4 of the runway has been consumed helps to improve the efficiency of the + // triggered GC. Under heavy steady state workload, this delay condition generally has no effect: if the allocation + // runway is divided "equally" between the current GC and the next GC, then at any potential trigger point (which cannot + // happen any sooner than completion of the first GC), it is already the case that roughly A > R/2. + if (3 * allocated <= available) { + // Even though we will not issue an adaptive trigger unless a minimum threshold of memory has been allocated, + // we still allow more generic triggers, such as guaranteed GC intervals, to act. + return ShenandoahHeuristics::should_start_gc(); + } - allocation_headroom -= MIN2(allocation_headroom, spike_headroom); - allocation_headroom -= MIN2(allocation_headroom, penalties); + avg_cycle_time = _gc_cycle_time_history->davg() + (_margin_of_error_sd * _gc_cycle_time_history->dsd()); + avg_alloc_rate = _allocation_rate.upper_bound(_margin_of_error_sd); + if ((now - _previous_acceleration_sample_timestamp) >= (ShenandoahAccelerationSamplePeriod / 1000.0)) { + predicted_future_accelerated_gc_time = + predict_gc_time(now + MAX2(get_planned_sleep_interval(), ShenandoahAccelerationSamplePeriod / 1000.0)); + double future_accelerated_planned_gc_time; + bool future_accelerated_planned_gc_time_is_average; + if (predicted_future_accelerated_gc_time > avg_cycle_time) { + future_accelerated_planned_gc_time = predicted_future_accelerated_gc_time; + future_accelerated_planned_gc_time_is_average = false; + } else { + future_accelerated_planned_gc_time = avg_cycle_time; + future_accelerated_planned_gc_time_is_average = true; + } + allocated_bytes_since_last_sample = _free_set->get_bytes_allocated_since_previous_sample(); + instantaneous_rate_words_per_second = + (allocated_bytes_since_last_sample / HeapWordSize) / (now - _previous_acceleration_sample_timestamp); - double avg_cycle_time = _gc_cycle_time_history->davg() + (_margin_of_error_sd * _gc_cycle_time_history->dsd()); - double avg_alloc_rate = _allocation_rate.upper_bound(_margin_of_error_sd); + _previous_acceleration_sample_timestamp = now; + add_rate_to_acceleration_history(now, instantaneous_rate_words_per_second); + current_rate_by_acceleration = instantaneous_rate_words_per_second; + consumption_accelerated = + accelerated_consumption(acceleration, current_rate_by_acceleration, avg_alloc_rate / HeapWordSize, + (ShenandoahAccelerationSamplePeriod / 1000.0) + future_accelerated_planned_gc_time); - log_debug(gc)("average GC time: %.2f ms, allocation rate: %.0f %s/s", - avg_cycle_time * 1000, byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate)); - if (avg_cycle_time * avg_alloc_rate > allocation_headroom) { - log_trigger("Average GC time (%.2f ms) is above the time for average allocation rate (%.0f %sB/s)" - " to deplete free headroom (%zu%s) (margin of error = %.2f)", - avg_cycle_time * 1000, - byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate), - byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom), - _margin_of_error_sd); - log_info(gc, ergo)("Free headroom: %zu%s (free) - %zu%s (spike) - %zu%s (penalties) = %zu%s", - byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), - byte_size_in_proper_unit(spike_headroom), proper_unit_for_byte_size(spike_headroom), - byte_size_in_proper_unit(penalties), proper_unit_for_byte_size(penalties), - byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom)); + // Note that even a single thread that wakes up and begins to allocate excessively can manifest as accelerating allocation + // rate. This thread will initially allocate a TLAB of minimum size. Then it will allocate a TLAB twice as big a bit later, + // and then twice as big again after another short delay. When a phase change causes many threads to increase their + // allocation behavior, this effect is multiplied, and compounded by jitter in the times that individual threads experience + // the phase change. + // + // The following trace represents an actual workload, with allocation rates sampled at 10 Hz, the default behavior before + // introduction of accelerated allocation rate detection. Though the allocation rate is seen to be increasing at times + // 101.907 and 102.007 and 102.108, the newly sampled allocation rate is not enough to trigger GC because the headroom is + // still quite large. In fact, GC is not triggered until time 102.409s, and this GC degenerates. + // + // Sample Time (s) Allocation Rate (MB/s) Headroom (GB) + // 101.807 0.0 26.93 + // <--- accelerated spike can trigger here, around time 101.9s + // 101.907 477.6 26.85 + // 102.007 3,206.0 26.35 + // 102.108 23,797.8 24.19 + // 102.208 24,164.5 21.83 + // 102.309 23,965.0 19.47 + // 102.409 24,624.35 17.05 <--- without accelerated rate detection, we trigger here + // + // Though the above measurements are from actual workload, the following details regarding sampled allocation rates at 3ms + // period were not measured directly for this run-time sample. These are hypothetical, though they represent a plausible + // result that correlates with the actual measurements. + // + // For most of the 100 ms time span that precedes the sample at 101.907, the allocation rate still remains at zero. The phase + // change that causes increasing allocations occurs near the end ot this time segment. When sampled with a 3 ms period, + // acceration of allocation can be triggered at approximately time 101.88s. + // + // In the default configuration, accelerated allocation rate is detected by examining a sequence of 8 allocation rate samples. + // + // Even a single allocation rate sample above the norm can be interpreted as acceleration of allocation rate. For example, the + // the best-fit line for the following samples has an acceleration rate of 3,553.3 MB/s/s. This is not enough to trigger GC, + // especially given the abundance of Headroom at this moment in time. + // + // TimeStamp (s) Alloc rate (MB/s) + // 101.857 0 + // 101.860 0 + // 101.863 0 + // 101.866 0 + // 101.869 53.3 + // + // At the next sample time, we will compute a slightly higher acceration, 9,150 MB/s/s. This is also insufficient to trigger + // GC. + // + // TimeStamp (s) Alloc rate (MB/s) + // 101.860 0 + // 101.863 0 + // 101.866 0 + // 101.869 53.3 + // 101.872 110.6 + // + // Eventually, we will observe a full history of accelerating rate samples, computing acceleration of 18,500 MB/s/s. This will + // trigger GC over 500 ms earlier than was previously possible. + // + // TimeStamp (s) Alloc rate (MB/s) + // 101.866 0 + // 101.869 53.3 + // 101.872 110.6 + // 101.875 165.9 + // 101.878 221.2 + // + // The accelerated rate heuristic is based on the following idea: + // + // Assume allocation rate is accelerating at a constant rate. If we postpone the spike trigger until the subsequent + // sample point, will there be enough memory to satisfy allocations that occur during the anticipated concurrent GC + // cycle? If not, we should trigger right now. + // + // Outline of this heuristic triggering technique: + // + // 1. We remember the N (e.g. N=3) most recent samples of spike allocation rate r0, r1, r2 samples at t0, t1, and t2 + // 2. if r1 < r0 or r2 < r1, approximate Acceleration = 0.0, Rate = Average(r0, r1, r2) + // 3. Otherwise, use least squares method to compute best-fit line of rate vs time + // 4. The slope of this line represents Acceleration. The y-intercept of this line represents "initial rate" + // 5. Use r2 to rrpresent CurrentRate + // 6. Use Consumption = CurrentRate * GCTime + 1/2 * Acceleration * GCTime * GCTime + // (See High School physics discussions on constant acceleration: D = v0 * t + 1/2 * a * t^2) + // 7. if Consumption exceeds headroom, trigger now + // + // Though larger sample size may improve quality of predictor, it also delays trigger response. Smaller sample sizes + // are more susceptible to false triggers based on random noise. The default configuration uses a sample size of 8 and + // a sample period of roughly 15 ms, spanning approximately 120 ms of execution. + if (consumption_accelerated > allocatable_words) { + size_t size_t_alloc_rate = (size_t) current_rate_by_acceleration * HeapWordSize; + if (acceleration > 0) { + size_t size_t_acceleration = (size_t) acceleration * HeapWordSize; + log_trigger("Accelerated consumption (" PROPERFMT ") exceeds free headroom (" PROPERFMT ") at " + "current rate (" PROPERFMT "/s) with acceleration (" PROPERFMT "/s/s) for planned %s GC time (%.2f ms)", + PROPERFMTARGS(consumption_accelerated * HeapWordSize), + PROPERFMTARGS(allocatable_words * HeapWordSize), + PROPERFMTARGS(size_t_alloc_rate), + PROPERFMTARGS(size_t_acceleration), + future_accelerated_planned_gc_time_is_average? "(from average)": "(by linear prediction)", + future_accelerated_planned_gc_time * 1000); + } else { + log_trigger("Momentary spike consumption (" PROPERFMT ") exceeds free headroom (" PROPERFMT ") at " + "current rate (" PROPERFMT "/s) for planned %s GC time (%.2f ms) (spike threshold = %.2f)", + PROPERFMTARGS(consumption_accelerated * HeapWordSize), + PROPERFMTARGS(allocatable_words * HeapWordSize), + PROPERFMTARGS(size_t_alloc_rate), + future_accelerated_planned_gc_time_is_average? "(from average)": "(by linear prediction)", + future_accelerated_planned_gc_time * 1000, _spike_threshold_sd); + + + } + _spike_acceleration_num_samples = 0; + _spike_acceleration_first_sample_index = 0; + + // Count this as a form of RATE trigger for purposes of adjusting heuristic triggering configuration because this + // trigger is influenced more by margin_of_error_sd than by spike_threshold_sd. + accept_trigger_with_type(RATE); + return true; + } + } + + // Suppose we don't trigger now, but decide to trigger in the next regulator cycle. What will be the GC time then? + predicted_future_gc_time = predict_gc_time(now + get_planned_sleep_interval()); + if (predicted_future_gc_time > avg_cycle_time) { + future_planned_gc_time = predicted_future_gc_time; + future_planned_gc_time_is_average = false; + } else { + future_planned_gc_time = avg_cycle_time; + future_planned_gc_time_is_average = true; + } + + log_debug(gc)("%s: average GC time: %.2f ms, predicted GC time: %.2f ms, allocation rate: %.0f %s/s", + _space_info->name(), avg_cycle_time * 1000, predicted_future_gc_time * 1000, + byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate)); + size_t allocatable_bytes = allocatable_words * HeapWordSize; + avg_time_to_deplete_available = allocatable_bytes / avg_alloc_rate; + + if (future_planned_gc_time > avg_time_to_deplete_available) { + log_trigger("%s GC time (%.2f ms) is above the time for average allocation rate (%.0f %sB/s)" + " to deplete free headroom (%zu%s) (margin of error = %.2f)", + future_planned_gc_time_is_average? "Average": "Linear prediction of", future_planned_gc_time * 1000, + byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate), + byte_size_in_proper_unit(allocatable_bytes), proper_unit_for_byte_size(allocatable_bytes), + _margin_of_error_sd); + + size_t spike_headroom = capacity / 100 * ShenandoahAllocSpikeFactor; + size_t penalties = capacity / 100 * _gc_time_penalties; + size_t allocation_headroom = available; + allocation_headroom -= MIN2(allocation_headroom, spike_headroom); + allocation_headroom -= MIN2(allocation_headroom, penalties); + log_info(gc, ergo)("Free headroom: " PROPERFMT " (free) - " PROPERFMT "(spike) - " PROPERFMT " (penalties) = " PROPERFMT, + PROPERFMTARGS(available), + PROPERFMTARGS(spike_headroom), + PROPERFMTARGS(penalties), + PROPERFMTARGS(allocation_headroom)); accept_trigger_with_type(RATE); return true; } - bool is_spiking = _allocation_rate.is_spiking(rate, _spike_threshold_sd); - if (is_spiking && avg_cycle_time > allocation_headroom / rate) { - log_trigger("Average GC time (%.2f ms) is above the time for instantaneous allocation rate (%.0f %sB/s) to deplete free headroom (%zu%s) (spike threshold = %.2f)", - avg_cycle_time * 1000, - byte_size_in_proper_unit(rate), proper_unit_for_byte_size(rate), - byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom), - _spike_threshold_sd); + is_spiking = _allocation_rate.is_spiking(rate, _spike_threshold_sd); + spike_time_to_deplete_available = (rate == 0)? 0: allocatable_bytes / rate; + if (is_spiking && (rate != 0) && (future_planned_gc_time > spike_time_to_deplete_available)) { + log_trigger("%s GC time (%.2f ms) is above the time for instantaneous allocation rate (%.0f %sB/s)" + " to deplete free headroom (%zu%s) (spike threshold = %.2f)", + future_planned_gc_time_is_average? "Average": "Linear prediction of", future_planned_gc_time * 1000, + byte_size_in_proper_unit(rate), proper_unit_for_byte_size(rate), + byte_size_in_proper_unit(allocatable_bytes), proper_unit_for_byte_size(allocatable_bytes), + _spike_threshold_sd); accept_trigger_with_type(SPIKE); return true; } - - if (ShenandoahHeuristics::should_start_gc()) { - _start_gc_is_pending = true; - return true; - } else { - return false; - } + return ShenandoahHeuristics::should_start_gc(); } void ShenandoahAdaptiveHeuristics::adjust_last_trigger_parameters(double amount) { @@ -352,6 +711,112 @@ size_t ShenandoahAdaptiveHeuristics::min_free_threshold() { return ShenandoahHeap::heap()->soft_max_capacity() / 100 * ShenandoahMinFreeThreshold; } +// This is called each time a new rate sample has been gathered, as governed by ShenandoahAccelerationSamplePeriod. +// Unlike traditional calculation of average allocation rate, there is no adjustment for standard deviation of the +// accelerated rate prediction. +size_t ShenandoahAdaptiveHeuristics::accelerated_consumption(double& acceleration, double& current_rate, + double avg_alloc_rate_words_per_second, + double predicted_cycle_time) const +{ + double *x_array = (double *) alloca(ShenandoahRateAccelerationSampleSize * sizeof(double)); + double *y_array = (double *) alloca(ShenandoahRateAccelerationSampleSize * sizeof(double)); + double x_sum = 0.0; + double y_sum = 0.0; + + assert(_spike_acceleration_num_samples > 0, "At minimum, we should have sample from this period"); + + double weighted_average_alloc; + if (_spike_acceleration_num_samples >= ShenandoahRateAccelerationSampleSize) { + double weighted_y_sum = 0; + double total_weight = 0; + double previous_x = 0; + uint delta = _spike_acceleration_num_samples - ShenandoahRateAccelerationSampleSize; + for (uint i = 0; i < ShenandoahRateAccelerationSampleSize; i++) { + uint index = (_spike_acceleration_first_sample_index + delta + i) % _spike_acceleration_buffer_size; + x_array[i] = _spike_acceleration_rate_timestamps[index]; + x_sum += x_array[i]; + y_array[i] = _spike_acceleration_rate_samples[index]; + if (i > 0) { + // first sample not included in weighted average because it has no weight. + double sample_weight = x_array[i] - x_array[i-1]; + weighted_y_sum = y_array[i] * sample_weight; + total_weight += sample_weight; + } + y_sum += y_array[i]; + } + weighted_average_alloc = (total_weight > 0)? weighted_y_sum / total_weight: 0; + } else { + weighted_average_alloc = 0; + } + + double momentary_rate; + if (_spike_acceleration_num_samples > ShenandoahMomentaryAllocationRateSpikeSampleSize) { + // Num samples must be strictly greater than sample size, because we need one extra sample to compute rate and weights + // In this context, the weight of a y value (an allocation rate) is the duration for which this allocation rate was + // active (the time since previous y value was reported). An allocation rate measured over a span of 300 ms (e.g. during + // concurrent GC) has much more "weight" than an allocation rate measured over a span of 15 s. + double weighted_y_sum = 0; + double total_weight = 0; + double sum_for_average = 0.0; + uint delta = _spike_acceleration_num_samples - ShenandoahMomentaryAllocationRateSpikeSampleSize; + for (uint i = 0; i < ShenandoahMomentaryAllocationRateSpikeSampleSize; i++) { + uint sample_index = (_spike_acceleration_first_sample_index + delta + i) % _spike_acceleration_buffer_size; + uint preceding_index = (sample_index == 0)? _spike_acceleration_buffer_size - 1: sample_index - 1; + double sample_weight = (_spike_acceleration_rate_timestamps[sample_index] + - _spike_acceleration_rate_timestamps[preceding_index]); + weighted_y_sum += _spike_acceleration_rate_samples[sample_index] * sample_weight; + total_weight += sample_weight; + } + momentary_rate = weighted_y_sum / total_weight; + bool is_spiking = _allocation_rate.is_spiking(momentary_rate, _spike_threshold_sd); + if (!is_spiking) { + // Disable momentary spike trigger unless allocation rate delta from average exceeds sd + momentary_rate = 0.0; + } + } else { + momentary_rate = 0.0; + } + + // By default, use momentary_rate for current rate and zero acceleration. Overwrite iff best-fit line has positive slope. + current_rate = momentary_rate; + acceleration = 0.0; + if ((_spike_acceleration_num_samples >= ShenandoahRateAccelerationSampleSize) + && (weighted_average_alloc >= avg_alloc_rate_words_per_second)) { + // If the average rate across the acceleration samples is below the overall average, this sample is not eligible to + // represent acceleration of allocation rate. We may just be catching up with allocations after a lull. + + double *xy_array = (double *) alloca(ShenandoahRateAccelerationSampleSize * sizeof(double)); + double *x2_array = (double *) alloca(ShenandoahRateAccelerationSampleSize * sizeof(double)); + double xy_sum = 0.0; + double x2_sum = 0.0; + for (uint i = 0; i < ShenandoahRateAccelerationSampleSize; i++) { + xy_array[i] = x_array[i] * y_array[i]; + xy_sum += xy_array[i]; + x2_array[i] = x_array[i] * x_array[i]; + x2_sum += x2_array[i]; + } + // Find the best-fit least-squares linear representation of rate vs time + double m; /* slope */ + double b; /* y-intercept */ + + m = ((ShenandoahRateAccelerationSampleSize * xy_sum - x_sum * y_sum) + / (ShenandoahRateAccelerationSampleSize * x2_sum - x_sum * x_sum)); + b = (y_sum - m * x_sum) / ShenandoahRateAccelerationSampleSize; + + if (m > 0) { + double proposed_current_rate = m * x_array[ShenandoahRateAccelerationSampleSize - 1] + b; + acceleration = m; + current_rate = proposed_current_rate; + } + // else, leave current_rate = momentary_rate, acceleration = 0 + } + // and here also, leave current_rate = momentary_rate, acceleration = 0 + + double time_delta = get_planned_sleep_interval() + predicted_cycle_time; + size_t words_to_be_consumed = (size_t) (current_rate * time_delta + 0.5 * acceleration * time_delta * time_delta); + return words_to_be_consumed; +} + ShenandoahAllocationRate::ShenandoahAllocationRate() : _last_sample_time(os::elapsedTime()), _last_sample_value(0), @@ -363,7 +828,7 @@ ShenandoahAllocationRate::ShenandoahAllocationRate() : double ShenandoahAllocationRate::force_sample(size_t allocated, size_t &unaccounted_bytes_allocated) { const double MinSampleTime = 0.002; // Do not sample if time since last update is less than 2 ms double now = os::elapsedTime(); - double time_since_last_update = now -_last_sample_time; + double time_since_last_update = now - _last_sample_time; if (time_since_last_update < MinSampleTime) { unaccounted_bytes_allocated = allocated - _last_sample_value; _last_sample_value = 0; @@ -412,8 +877,10 @@ bool ShenandoahAllocationRate::is_spiking(double rate, double threshold) const { double sd = _rate.sd(); if (sd > 0) { - // There is a small chance that that rate has already been sampled, but it - // seems not to matter in practice. + // There is a small chance that that rate has already been sampled, but it seems not to matter in practice. + // Note that z_score reports how close the rate is to the average. A value between -1 and 1 means we are within one + // standard deviation. A value between -3 and +3 means we are within 3 standard deviations. We only check for z_score + // greater than threshold because we are looking for an allocation spike which is greater than the mean. double z_score = (rate - _rate.avg()) / sd; if (z_score > threshold) { return true; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index 9b7824a50d7..c761f2a82f3 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -27,7 +27,9 @@ #define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" +#include "gc/shenandoah/shenandoahRegulatorThread.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "memory/allocation.hpp" #include "utilities/numberSeq.hpp" @@ -108,6 +110,26 @@ public: virtual ~ShenandoahAdaptiveHeuristics(); + virtual void initialize() override; + + virtual void post_initialize() override; + + virtual void adjust_penalty(intx step) override; + + // At the end of GC(N), we idle GC until necessary to start the next GC. Compute the threshold of memory that can be allocated + // before we need to start the next GC. + void start_idle_span() override; + + // Having observed a new allocation rate sample, add this to the acceleration history so that we can determine if allocation + // rate is accelerating. + void add_rate_to_acceleration_history(double timestamp, double rate); + + // Compute and return the current allocation rate, the current rate of acceleration, and the amount of memory that we expect + // to consume if we start GC right now and gc takes predicted_cycle_time to complete. + size_t accelerated_consumption(double& acceleration, double& current_rate, + double avg_rate_words_per_sec, double predicted_cycle_time) const; + + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, RegionData* data, size_t size, size_t actual_free) override; @@ -136,6 +158,8 @@ public: const static double LOWEST_EXPECTED_AVAILABLE_AT_END; const static double HIGHEST_EXPECTED_AVAILABLE_AT_END; + const static size_t GC_TIME_SAMPLE_SIZE; + friend class ShenandoahAllocationRate; // Used to record the last trigger that signaled to start a GC. @@ -150,9 +174,19 @@ public: void adjust_margin_of_error(double amount); void adjust_spike_threshold(double amount); + // Returns number of words that can be allocated before we need to trigger next GC, given available in bytes. + inline size_t allocatable(size_t available) const { + return (available > _headroom_adjustment)? (available - _headroom_adjustment) / HeapWordSize: 0; + } + protected: ShenandoahAllocationRate _allocation_rate; + // Invocations of should_start_gc() happen approximately once per ms. Queries of allocation rate only happen if a + // a certain amount of time has passed since the previous query. + size_t _allocated_at_previous_query; + double _time_of_previous_allocation_query; + // The margin of error expressed in standard deviations to add to our // average cycle time and allocation rate. As this value increases we // tend to overestimate the rate at which mutators will deplete the @@ -179,6 +213,48 @@ protected: // source of feedback to adjust trigger parameters. TruncatedSeq _available; + ShenandoahFreeSet* _free_set; + + // This represents the time at which the allocation rate was most recently sampled for the purpose of detecting acceleration. + double _previous_acceleration_sample_timestamp; + size_t _total_allocations_at_start_of_idle; + + // bytes of headroom at which we should trigger GC + size_t _headroom_adjustment; + + // Keep track of GC_TIME_SAMPLE_SIZE most recent concurrent GC cycle times + uint _gc_time_first_sample_index; + uint _gc_time_num_samples; + double* const _gc_time_timestamps; + double* const _gc_time_samples; + double* const _gc_time_xy; // timestamp * sample + double* const _gc_time_xx; // timestamp squared + double _gc_time_sum_of_timestamps; + double _gc_time_sum_of_samples; + double _gc_time_sum_of_xy; + double _gc_time_sum_of_xx; + + double _gc_time_m; // slope + double _gc_time_b; // y-intercept + double _gc_time_sd; // sd on deviance from prediction + + // In preparation for a span during which GC will be idle, compute the headroom adjustment that will be used to + // detect when GC needs to trigger. + void compute_headroom_adjustment() override; + + void add_gc_time(double timestamp_at_start, double duration); + void add_degenerated_gc_time(double timestamp_at_start, double duration); + double predict_gc_time(double timestamp_at_start); + + // Keep track of SPIKE_ACCELERATION_SAMPLE_SIZE most recent spike allocation rate measurements. Note that it is + // typical to experience a small spike following end of GC cycle, as mutator threads refresh their TLABs. But + // there is generally an abundance of memory at this time as well, so this will not generally trigger GC. + uint _spike_acceleration_buffer_size; + uint _spike_acceleration_first_sample_index; + uint _spike_acceleration_num_samples; + double* const _spike_acceleration_rate_samples; // holds rates in words/second + double* const _spike_acceleration_rate_timestamps; + // A conservative minimum threshold of free space that we'll try to maintain when possible. // For example, we might trigger a concurrent gc if we are likely to drop below // this threshold, or we might consider this when dynamically resizing generations diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index 029b917deab..f3d31b8d0e1 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -52,6 +52,12 @@ static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) { return 0; } +void ShenandoahGenerationalHeuristics::post_initialize() { + ShenandoahHeuristics::post_initialize(); + _free_set = ShenandoahHeap::heap()->free_set(); + compute_headroom_adjustment(); +} + inline void assert_no_in_place_promotions() { #ifdef ASSERT class ShenandoahNoInPlacePromotions : public ShenandoahHeapRegionClosure { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index 74d657feab7..da883d0d26f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -49,6 +49,8 @@ public: void choose_collection_set(ShenandoahCollectionSet* collection_set) override; + virtual void post_initialize() override; + private: // Compute evacuation budgets prior to choosing collection set. void compute_evacuation_budgets(ShenandoahHeap* const heap); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 8fc744112bf..603e00c401d 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -46,13 +46,16 @@ int ShenandoahHeuristics::compare_by_garbage(RegionData a, RegionData b) { } ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahSpaceInfo* space_info) : + _most_recent_trigger_evaluation_time(os::elapsedTime()), + _most_recent_planned_sleep_interval(0.0), _start_gc_is_pending(false), _declined_trigger_count(0), _most_recent_declined_trigger_count(0), _space_info(space_info), _region_data(nullptr), _guaranteed_gc_interval(0), - _cycle_start(os::elapsedTime()), + _precursor_cycle_start(os::elapsedTime()), + _cycle_start(_precursor_cycle_start), _last_cycle_end(0), _gc_times_learned(0), _gc_time_penalties(0), @@ -156,6 +159,19 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); } +void ShenandoahHeuristics::start_idle_span() { + // do nothing +} + +void ShenandoahHeuristics::record_degenerated_cycle_start(bool out_of_cycle) { + if (out_of_cycle) { + _precursor_cycle_start = _cycle_start = os::elapsedTime(); + } else { + _precursor_cycle_start = _cycle_start; + _cycle_start = os::elapsedTime(); + } +} + void ShenandoahHeuristics::record_cycle_start() { _cycle_start = os::elapsedTime(); } @@ -197,7 +213,6 @@ bool ShenandoahHeuristics::should_degenerate_cycle() { void ShenandoahHeuristics::adjust_penalty(intx step) { assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100, "In range before adjustment: %zd", _gc_time_penalties); - if ((_most_recent_declined_trigger_count <= Penalty_Free_Declinations) && (step > 0)) { // Don't penalize if heuristics are not responsible for a negative outcome. Allow Penalty_Free_Declinations following // previous GC for self calibration without penalty. @@ -274,6 +289,17 @@ void ShenandoahHeuristics::initialize() { // Nothing to do by default. } +void ShenandoahHeuristics::post_initialize() { + // Nothing to do by default. +} + double ShenandoahHeuristics::elapsed_cycle_time() const { return os::elapsedTime() - _cycle_start; } + + +// Includes the time spent in abandoned concurrent GC cycle that may have triggered this degenerated cycle. +double ShenandoahHeuristics::elapsed_degenerated_cycle_time() const { + double now = os::elapsedTime(); + return now - _precursor_cycle_start; +} diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index 633c4e87126..5bfba4f52d5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -78,6 +78,10 @@ class ShenandoahHeuristics : public CHeapObj { }; #endif +private: + double _most_recent_trigger_evaluation_time; + double _most_recent_planned_sleep_interval; + protected: static const uint Moving_Average_Samples = 10; // Number of samples to store in moving averages @@ -85,14 +89,13 @@ protected: size_t _declined_trigger_count; // This counts how many times since previous GC finished that this // heuristic has answered false to should_start_gc(). size_t _most_recent_declined_trigger_count; - ; // This represents the value of _declined_trigger_count as captured at the + // This represents the value of _declined_trigger_count as captured at the // moment the most recent GC effort was triggered. In case the most recent // concurrent GC effort degenerates, the value of this variable allows us to // differentiate between degeneration because heuristic was overly optimistic // in delaying the trigger vs. degeneration for other reasons (such as the // most recent GC triggered "immediately" after previous GC finished, but the // free headroom has already been depleted). - class RegionData { private: ShenandoahHeapRegion* _region; @@ -103,6 +106,7 @@ protected: #ifdef ASSERT UnionTag _union_tag; #endif + public: inline void clear() { @@ -171,6 +175,7 @@ protected: size_t _guaranteed_gc_interval; + double _precursor_cycle_start; double _cycle_start; double _last_cycle_end; @@ -188,7 +193,7 @@ protected: RegionData* data, size_t data_size, size_t free) = 0; - void adjust_penalty(intx step); + virtual void adjust_penalty(intx step); inline void accept_trigger() { _most_recent_declined_trigger_count = _declined_trigger_count; @@ -200,6 +205,14 @@ protected: _declined_trigger_count++; } + inline double get_most_recent_wake_time() const { + return _most_recent_trigger_evaluation_time; + } + + inline double get_planned_sleep_interval() const { + return _most_recent_planned_sleep_interval; + } + public: ShenandoahHeuristics(ShenandoahSpaceInfo* space_info); virtual ~ShenandoahHeuristics(); @@ -212,10 +225,22 @@ public: _guaranteed_gc_interval = guaranteed_gc_interval; } + virtual void start_idle_span(); + virtual void compute_headroom_adjustment() { + // Default implementation does nothing. + } + virtual void record_cycle_start(); + void record_degenerated_cycle_start(bool out_of_cycle); + virtual void record_cycle_end(); + void update_should_start_query_times(double now, double planned_sleep_interval) { + _most_recent_trigger_evaluation_time = now; + _most_recent_planned_sleep_interval = planned_sleep_interval; + } + virtual bool should_start_gc(); inline void cancel_trigger_request() { @@ -248,8 +273,10 @@ public: virtual bool is_diagnostic() = 0; virtual bool is_experimental() = 0; virtual void initialize(); + virtual void post_initialize(); double elapsed_cycle_time() const; + double elapsed_degenerated_cycle_time() const; virtual size_t force_alloc_rate_sample(size_t bytes_allocated) { // do nothing diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index beff2200d90..09508a1163f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -137,6 +137,7 @@ bool ShenandoahYoungHeuristics::should_start_gc() { // inherited triggers have already decided to start a cycle, so no further evaluation is required if (ShenandoahAdaptiveHeuristics::should_start_gc()) { + // ShenandoahAdaptiveHeuristics::should_start_gc() has already accepted trigger, or declined it. return true; } @@ -178,7 +179,7 @@ size_t ShenandoahYoungHeuristics::bytes_of_allocation_runway_before_gc_trigger(s size_t capacity = _space_info->max_capacity(); size_t usage = _space_info->used(); size_t available = (capacity > usage)? capacity - usage: 0; - size_t allocated = _space_info->bytes_allocated_since_gc_start(); + size_t allocated = _free_set->get_bytes_allocated_since_gc_start(); size_t available_young_collected = ShenandoahHeap::heap()->collection_set()->get_young_available_bytes_collected(); size_t anticipated_available = diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 5206a0558e8..f0125c38cae 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1215,6 +1215,7 @@ void ShenandoahConcurrentGC::op_final_update_refs() { } heap->rebuild_free_set(true /*concurrent*/); + _generation->heuristics()->start_idle_span(); { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_update_refs_propagate_gc_state); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index bc11659c5e5..c5607421265 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -59,6 +59,7 @@ void ShenandoahControlThread::run_service() { ShenandoahCollectorPolicy* const policy = heap->shenandoah_policy(); ShenandoahHeuristics* const heuristics = heap->heuristics(); + double most_recent_wake_time = os::elapsedTime(); while (!should_terminate()) { const GCCause::Cause cancelled_cause = heap->cancelled_cause(); if (cancelled_cause == GCCause::_shenandoah_stop_vm) { @@ -222,16 +223,26 @@ void ShenandoahControlThread::run_service() { // Wait before performing the next action. If allocation happened during this wait, // we exit sooner, to let heuristics re-evaluate new conditions. If we are at idle, // back off exponentially. - const double current = os::elapsedTime(); + const double before_sleep = most_recent_wake_time; if (heap->has_changed()) { sleep = ShenandoahControlIntervalMin; - } else if ((current - last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ + } else if ((before_sleep - last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ sleep = MIN2(ShenandoahControlIntervalMax, MAX2(1, sleep * 2)); - last_sleep_adjust_time = current; + last_sleep_adjust_time = before_sleep; } - MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); ml.wait(sleep); + // Record a conservative estimate of the longest anticipated sleep duration until we sample again. + double planned_sleep_interval = MIN2(ShenandoahControlIntervalMax, MAX2(1, sleep * 2)) / 1000.0; + most_recent_wake_time = os::elapsedTime(); + heuristics->update_should_start_query_times(most_recent_wake_time, planned_sleep_interval); + if (LogTarget(Debug, gc, thread)::is_enabled()) { + double elapsed = most_recent_wake_time - before_sleep; + double hiccup = elapsed - double(sleep); + if (hiccup > 0.001) { + log_debug(gc, thread)("Control Thread hiccup time: %.3fs", hiccup); + } + } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 99776e38bfe..8cd8a390c4a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -314,6 +314,7 @@ void ShenandoahDegenGC::op_degenerated() { if (progress) { heap->notify_gc_progress(); _generation->heuristics()->record_degenerated(); + heap->start_idle_span(); } else if (policy->should_upgrade_degenerated_gc()) { // Upgrade to full GC, register full-GC impact on heuristics. op_degenerated_futile(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 961800f20d9..c39e2e7bb79 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -287,9 +287,25 @@ void ShenandoahFreeSet::resize_old_collector_capacity(size_t regions) { // else, old generation is already appropriately sized } + void ShenandoahFreeSet::reset_bytes_allocated_since_gc_start(size_t initial_bytes_allocated) { shenandoah_assert_heaplocked(); + // Future inquiries of get_total_bytes_allocated() will return the sum of + // _total_bytes_previously_allocated and _mutator_bytes_allocated_since_gc_start. + // Since _mutator_bytes_allocated_since_gc_start does not start at zero, we subtract initial_bytes_allocated so as + // to not double count these allocated bytes. + size_t original_mutator_bytes_allocated_since_gc_start = _mutator_bytes_allocated_since_gc_start; + + // Setting _mutator_bytes_allocated_since_gc_start before _total_bytes_previously_allocated reduces the damage + // in the case that the control or regulator thread queries get_bytes_allocated_since_previous_sample() between + // the two assignments. + // + // These are not declared as volatile so the compiler or hardware may reorder the assignments. The implementation of + // get_bytes_allocated_since_previous_cycle() is robust to this possibility, as are triggering heuristics. The current + // implementation assumes we are better off to tolerate the very rare race rather than impose a synchronization penalty + // on every update and fetch. (Perhaps it would be better to make the opposite tradeoff for improved maintainability.) _mutator_bytes_allocated_since_gc_start = initial_bytes_allocated; + _total_bytes_previously_allocated += original_mutator_bytes_allocated_since_gc_start - initial_bytes_allocated; } void ShenandoahFreeSet::increase_bytes_allocated(size_t bytes) { @@ -1211,6 +1227,8 @@ inline void ShenandoahRegionPartitions::assert_bounds_sanity() { ShenandoahFreeSet::ShenandoahFreeSet(ShenandoahHeap* heap, size_t max_regions) : _heap(heap), _partitions(max_regions, this), + _total_bytes_previously_allocated(0), + _mutator_bytes_at_last_sample(0), _total_humongous_waste(0), _alloc_bias_weight(0), _total_young_used(0), @@ -1676,9 +1694,6 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // Regardless of whether this allocation succeeded, if the remaining memory is less than PLAB:min_size(), retire this region. // Note that retire_from_partition() increases used to account for waste. - // Also, if this allocation request failed and the consumed within this region * ShenandoahEvacWaste > region size, - // then retire the region so that subsequent searches can find available memory more quickly. - size_t idx = r->index(); size_t waste_bytes = _partitions.retire_from_partition(orig_partition, idx, r->used()); DEBUG_ONLY(boundary_changed = true;) @@ -1796,7 +1811,6 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req, bo // found the match break; } - end++; } @@ -2036,7 +2050,8 @@ void ShenandoahFreeSet::clear_internal() { _partitions.set_bias_from_left_to_right(ShenandoahFreeSetPartitionId::OldCollector, false); } -void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_regions, size_t &old_trashed_regions, +// Returns total allocatable words in Mutator partition +size_t ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_regions, size_t &old_trashed_regions, size_t &first_old_region, size_t &last_old_region, size_t &old_region_count) { // This resets all state information, removing all regions from all sets. @@ -2054,6 +2069,8 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_r size_t region_size_bytes = _partitions.region_size_bytes(); size_t max_regions = _partitions.max(); + size_t mutator_alloc_capacity_in_words = 0; + size_t mutator_leftmost = max_regions; size_t mutator_rightmost = 0; size_t mutator_leftmost_empty = max_regions; @@ -2123,6 +2140,7 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_r if (region->is_trash() || !region->is_old()) { // Both young and old (possibly immediately) collected regions (trashed) are placed into the Mutator set _partitions.raw_assign_membership(idx, ShenandoahFreeSetPartitionId::Mutator); + mutator_alloc_capacity_in_words += ac / HeapWordSize; if (idx < mutator_leftmost) { mutator_leftmost = idx; } @@ -2279,6 +2297,7 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_r _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator), _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector), _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector)); + return mutator_alloc_capacity_in_words; } void ShenandoahFreeSet::transfer_humongous_regions_from_mutator_to_old_collector(size_t xfer_regions, @@ -2583,19 +2602,20 @@ void ShenandoahFreeSet::prepare_to_rebuild(size_t &young_trashed_regions, size_t clear(); log_debug(gc, free)("Rebuilding FreeSet"); - // This places regions that have alloc_capacity into the old_collector set if they identify as is_old() or the - // mutator set otherwise. All trashed (cset) regions are affiliated young and placed in mutator set. - find_regions_with_alloc_capacity(young_trashed_regions, old_trashed_regions, - first_old_region, last_old_region, old_region_count); + // Place regions that have alloc_capacity into the old_collector set if they identify as is_old() or the + // mutator set otherwise. All trashed (cset) regions are affiliated young and placed in mutator set. Save the + // allocatable words in mutator partition in state variable. + _prepare_to_rebuild_mutator_free = find_regions_with_alloc_capacity(young_trashed_regions, old_trashed_regions, + first_old_region, last_old_region, old_region_count); } - -void ShenandoahFreeSet::finish_rebuild(size_t young_cset_regions, size_t old_cset_regions, size_t old_region_count) { +// Return mutator free +void ShenandoahFreeSet::finish_rebuild(size_t young_trashed_regions, size_t old_trashed_regions, size_t old_region_count) { shenandoah_assert_heaplocked(); size_t young_reserve(0), old_reserve(0); if (_heap->mode()->is_generational()) { - compute_young_and_old_reserves(young_cset_regions, old_cset_regions, young_reserve, old_reserve); + compute_young_and_old_reserves(young_trashed_regions, old_trashed_regions, young_reserve, old_reserve); } else { young_reserve = (_heap->max_capacity() / 100) * ShenandoahEvacReserve; old_reserve = 0; @@ -2744,10 +2764,13 @@ void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_trashed_regi // into the collector set or old collector set in order to assure that the memory available for allocations within // the collector set is at least to_reserve and the memory available for allocations within the old collector set // is at least to_reserve_old. -void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old, size_t &old_region_count, - size_t &young_used_regions, size_t &old_used_regions, - size_t &young_used_bytes, size_t &old_used_bytes) { +// +// Returns total mutator alloc capacity, in words. +size_t ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old, size_t &old_region_count, + size_t &young_used_regions, size_t &old_used_regions, + size_t &young_used_bytes, size_t &old_used_bytes) { const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + size_t mutator_allocatable_words = _prepare_to_rebuild_mutator_free; young_used_regions = 0; old_used_regions = 0; @@ -2825,6 +2848,8 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector), _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector)); old_region_count++; + assert(ac = ShenandoahHeapRegion::region_size_bytes(), "Cannot move to old unless entire region is in alloc capacity"); + mutator_allocatable_words -= ShenandoahHeapRegion::region_size_words(); continue; } } @@ -2868,8 +2893,10 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old " Collector range [%zd, %zd]", _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator), _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator), - _partitions.leftmost(ShenandoahFreeSetPartitionId::Collector), - _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector)); + _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector), + _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector)); + + mutator_allocatable_words -= ac / HeapWordSize; continue; } @@ -2977,6 +3004,7 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old PROPERFMTARGS(to_reserve), PROPERFMTARGS(reserve)); } } + return mutator_allocatable_words; } void ShenandoahFreeSet::establish_old_collector_alloc_bias() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index d55a06d5713..2df06432bd2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -437,6 +437,12 @@ private: ShenandoahHeap* const _heap; ShenandoahRegionPartitions _partitions; + size_t _total_bytes_previously_allocated; + size_t _mutator_bytes_at_last_sample; + + // Temporarily holds mutator_Free allocatable bytes between prepare_to_rebuild() and finish_rebuild() + size_t _prepare_to_rebuild_mutator_free; + // This locks the rebuild process (in combination with the global heap lock). Whenever we rebuild the free set, // we first acquire the global heap lock and then we acquire this _rebuild_lock in a nested context. Threads that // need to check available, acquire only the _rebuild_lock to make sure that they are not obtaining the value of @@ -446,10 +452,10 @@ private: // locks will acquire them in the same order: first the global heap lock and then the rebuild lock. ShenandoahRebuildLock _rebuild_lock; - size_t _total_humongous_waste; - HeapWord* allocate_aligned_plab(size_t size, ShenandoahAllocRequest& req, ShenandoahHeapRegion* r); + size_t _total_humongous_waste; + // We re-evaluate the left-to-right allocation bias whenever _alloc_bias_weight is less than zero. Each time // we allocate an object, we decrement the count of this value. Each time we re-evaluate whether to allocate // from right-to-left or left-to-right, we reset the value of this counter to _InitialAllocBiasWeight. @@ -662,10 +668,47 @@ public: void increase_bytes_allocated(size_t bytes); + // Return an approximation of the bytes allocated since GC start. The value returned is monotonically non-decreasing + // in time within each GC cycle. For certain GC cycles, the value returned may include some bytes allocated before + // the start of the current GC cycle. inline size_t get_bytes_allocated_since_gc_start() const { return _mutator_bytes_allocated_since_gc_start; } + inline size_t get_total_bytes_allocated() { + return _mutator_bytes_allocated_since_gc_start + _total_bytes_previously_allocated; + } + + inline size_t get_bytes_allocated_since_previous_sample() { + size_t total_bytes = get_total_bytes_allocated(); + size_t result; + if (total_bytes < _mutator_bytes_at_last_sample) { + // This rare condition may occur if bytes allocated overflows (wraps around) size_t tally of allocations. + // This may also occur in the very rare situation that get_total_bytes_allocated() is queried in the middle of + // reset_bytes_allocated_since_gc_start(). Note that there is no lock to assure that the two global variables + // it modifies are modified atomically (_total_bytes_previously_allocated and _mutator_byts_allocated_since_gc_start) + // This has been observed to occur when an out-of-cycle degenerated cycle is starting (and thus calls + // reset_bytes_allocated_since_gc_start()) at the same time that the control (non-generational mode) or + // regulator (generational-mode) thread calls should_start_gc() (which invokes get_bytes_allocated_since_previous_sample()). + // + // Handle this rare situation by responding with the "innocent" value 0 and resetting internal state so that the + // the next query can recalibrate. + result = 0; + } else { + // Note: there's always the possibility that the tally of total allocations exceeds the 64-bit capacity of our size_t + // counter. We assume that the difference between relevant samples does not exceed this count. Example: + // Suppose _mutator_words_at_last_sample is 0xffff_ffff_ffff_fff0 (18,446,744,073,709,551,600 Decimal) + // and _total_words is 0x0000_0000_0000_0800 ( 32,768 Decimal) + // Then, total_words - _mutator_words_at_last_sample can be done adding 1's complement of subtrahend: + // 1's complement of _mutator_words_at_last_sample is: 0x0000_0000_0000_0010 ( 16 Decimal)) + // plus total_words: 0x0000_0000_0000_0800 (32,768 Decimal) + // sum: 0x0000_0000_0000_0810 (32,784 Decimal) + result = total_bytes - _mutator_bytes_at_last_sample; + } + _mutator_bytes_at_last_sample = total_bytes; + return result; + } + // Public because ShenandoahRegionPartitions assertions require access. inline size_t alloc_capacity(ShenandoahHeapRegion *r) const; inline size_t alloc_capacity(size_t idx) const; @@ -781,15 +824,15 @@ public: // Acquire heap lock and log status, assuming heap lock is not acquired by the caller. void log_status_under_lock(); - // Note that capacity is the number of regions that had available memory at most recent rebuild. It is not the - // entire size of the young or global generation. (Regions within the generation that were fully utilized at time of - // rebuild are not counted as part of capacity.) - - // All three of the following functions may produce stale data if called without owning the global heap lock. + // All four of the following functions may produce stale data if called without owning the global heap lock. // Changes to the values of these variables are performed with a lock. A change to capacity or used "atomically" // adjusts available with respect to lock holders. However, sequential calls to these three functions may produce // inconsistent data: available may not equal capacity - used because the intermediate states of any "atomic" // locked action can be seen by these unlocked functions. + + // Note that capacity is the number of regions that had available memory at most recent rebuild. It is not the + // entire size of the young or global generation. (Regions within the generation that were fully utilized at time of + // rebuild are not counted as part of capacity.) inline size_t capacity_holding_lock() const { shenandoah_assert_heaplocked(); return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Mutator); @@ -808,11 +851,14 @@ public: ShenandoahRebuildLocker locker(rebuild_lock()); return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator); } + inline size_t reserved() const { return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Collector); } inline size_t available() { shenandoah_assert_not_heaplocked(); ShenandoahRebuildLocker locker(rebuild_lock()); return _partitions.available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId::Mutator); } + inline size_t available_holding_lock() const + { return _partitions.available_in(ShenandoahFreeSetPartitionId::Mutator); } // Use this version of available() if the heap lock is held. inline size_t available_locked() const { @@ -880,13 +926,17 @@ public: // first_old_region is the index of the first region that is part of the OldCollector set // last_old_region is the index of the last region that is part of the OldCollector set // old_region_count is the number of regions in the OldCollector set that have memory available to be allocated - void find_regions_with_alloc_capacity(size_t &young_cset_regions, size_t &old_cset_regions, - size_t &first_old_region, size_t &last_old_region, size_t &old_region_count); + // + // Returns allocatable memory within Mutator partition, in words. + size_t find_regions_with_alloc_capacity(size_t &young_cset_regions, size_t &old_cset_regions, + size_t &first_old_region, size_t &last_old_region, size_t &old_region_count); // Ensure that Collector has at least to_reserve bytes of available memory, and OldCollector has at least old_reserve // bytes of available memory. On input, old_region_count holds the number of regions already present in the // OldCollector partition. Upon return, old_region_count holds the updated number of regions in the OldCollector partition. - void reserve_regions(size_t to_reserve, size_t old_reserve, size_t &old_region_count, + // + // Returns allocatable memory within Mutator partition, in words. + size_t reserve_regions(size_t to_reserve, size_t old_reserve, size_t &old_region_count, size_t &young_used_regions, size_t &old_used_regions, size_t &young_used_bytes, size_t &old_used_bytes); // Reserve space for evacuations, with regions reserved for old evacuations placed to the right diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 3c92750cc0c..750f7e9122d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -252,6 +252,7 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { phase5_epilog(); } + heap->start_idle_span(); // Resize metaspace MetaspaceGC::compute_new_size(); @@ -1124,8 +1125,9 @@ void ShenandoahFullGC::phase5_epilog() { if (heap->mode()->is_generational()) { ShenandoahGenerationalFullGC::compute_balances(); } - free_set->finish_rebuild(young_trashed_regions, old_trashed_regions, num_old); + heap->free_set()->finish_rebuild(young_trashed_regions, old_trashed_regions, num_old); } + // Set mark incomplete because the marking bitmaps have been reset except pinned regions. _generation->set_mark_incomplete(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index ddb50ee0020..b2d5e5423dd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -151,6 +151,10 @@ ShenandoahHeuristics* ShenandoahGeneration::initialize_heuristics(ShenandoahMode return _heuristics; } +void ShenandoahGeneration::post_initialize_heuristics() { + _heuristics->post_initialize(); +} + void ShenandoahGeneration::set_evacuation_reserve(size_t new_val) { shenandoah_assert_heaplocked(); _evacuation_reserve = new_val; @@ -358,8 +362,7 @@ void ShenandoahGeneration::cancel_marking() { set_concurrent_mark_in_progress(false); } -ShenandoahGeneration::ShenandoahGeneration(ShenandoahGenerationType type, - uint max_workers) : +ShenandoahGeneration::ShenandoahGeneration(ShenandoahGenerationType type, uint max_workers) : _type(type), _task_queues(new ShenandoahObjToScanQueueSet(max_workers)), _ref_processor(new ShenandoahReferenceProcessor(this, MAX2(max_workers, 1U))), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index 946f2b91520..1a549be8988 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -83,10 +83,10 @@ private: ShenandoahReferenceProcessor* ref_processor() { return _ref_processor; } virtual ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode); + virtual void post_initialize_heuristics(); virtual void post_initialize(ShenandoahHeap* heap); - virtual size_t bytes_allocated_since_gc_start() const override = 0; virtual size_t used() const override = 0; virtual size_t used_regions() const = 0; virtual size_t used_regions_size() const = 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 3b57190cc75..cc7547b8ac1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -622,10 +622,11 @@ void ShenandoahGenerationalControlThread::service_stw_full_cycle(GCCause::Cause void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const ShenandoahGCRequest& request) { assert(_degen_point != ShenandoahGC::_degenerated_unset, "Degenerated point should be set"); + request.generation->heuristics()->record_degenerated_cycle_start(ShenandoahGC::ShenandoahDegenPoint::_degenerated_outside_cycle + == _degen_point); _heap->increment_total_collections(false); ShenandoahGCSession session(request.cause, request.generation); - ShenandoahDegenGC gc(_degen_point, request.generation); gc.collect(request.cause); _degen_point = ShenandoahGC::_degenerated_unset; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 2c2e5533c01..b302bde8510 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -90,11 +90,23 @@ ShenandoahGenerationalHeap::ShenandoahGenerationalHeap(ShenandoahCollectorPolicy assert(is_aligned(_max_plab_size, CardTable::card_size_in_words()), "max_plab_size must be aligned"); } +void ShenandoahGenerationalHeap::initialize_generations() { + ShenandoahHeap::initialize_generations(); + _young_generation->post_initialize(this); + _old_generation->post_initialize(this); +} + void ShenandoahGenerationalHeap::post_initialize() { ShenandoahHeap::post_initialize(); _age_census = new ShenandoahAgeCensus(); } +void ShenandoahGenerationalHeap::post_initialize_heuristics() { + ShenandoahHeap::post_initialize_heuristics(); + _young_generation->post_initialize_heuristics(); + _old_generation->post_initialize_heuristics(); +} + void ShenandoahGenerationalHeap::print_init_logger() const { ShenandoahGenerationalInitLogger logger; logger.print_all(); @@ -110,12 +122,6 @@ void ShenandoahGenerationalHeap::initialize_heuristics() { _old_generation->initialize_heuristics(mode()); } -void ShenandoahGenerationalHeap::post_initialize_heuristics() { - ShenandoahHeap::post_initialize_heuristics(); - _young_generation->post_initialize(this); - _old_generation->post_initialize(this); -} - void ShenandoahGenerationalHeap::initialize_serviceability() { assert(mode()->is_generational(), "Only for the generational mode"); _young_gen_memory_pool = new ShenandoahYoungGenMemoryPool(this); @@ -152,6 +158,10 @@ void ShenandoahGenerationalHeap::stop() { regulator_thread()->stop(); } +void ShenandoahGenerationalHeap::start_idle_span() { + young_generation()->heuristics()->start_idle_span(); +} + bool ShenandoahGenerationalHeap::requires_barriers(stackChunkOop obj) const { if (is_idle()) { return false; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 719bae52a83..7fe0362aa3f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -40,6 +40,7 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap { public: explicit ShenandoahGenerationalHeap(ShenandoahCollectorPolicy* policy); void post_initialize() override; + void initialize_generations() override; void initialize_heuristics() override; void post_initialize_heuristics() override; @@ -82,6 +83,8 @@ public: inline bool is_tenurable(const ShenandoahHeapRegion* r) const; + void start_idle_span() override; + // Ages regions that haven't been used for allocations in the current cycle. // Resets ages for regions that have been used for allocations. void update_region_ages(ShenandoahMarkingContext* ctx); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index d78bdae6a51..c6889351161 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -435,7 +435,7 @@ jint ShenandoahHeap::initialize() { } _free_set = new ShenandoahFreeSet(this, _num_regions); - post_initialize_heuristics(); + initialize_generations(); // We are initializing free set. We ignore cset region tallies. size_t young_trashed_regions, old_trashed_regions, first_old, last_old, num_old; @@ -492,16 +492,17 @@ jint ShenandoahHeap::initialize() { _phase_timings = new ShenandoahPhaseTimings(max_workers()); ShenandoahCodeRoots::initialize(); + // Initialization of controller makes use of variables established by initialize_heuristics. initialize_controller(); + // Certain initialization of heuristics must be deferred until after controller is initialized. + post_initialize_heuristics(); + start_idle_span(); if (ShenandoahUncommit) { _uncommit_thread = new ShenandoahUncommitThread(this); } - print_init_logger(); - FullGCForwarding::initialize(_heap_region); - return JNI_OK; } @@ -545,10 +546,6 @@ void ShenandoahHeap::initialize_heuristics() { _global_generation->initialize_heuristics(mode()); } -void ShenandoahHeap::post_initialize_heuristics() { - _global_generation->post_initialize(this); -} - #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable:4355 ) // 'this' : used in base member initializer list @@ -690,6 +687,11 @@ public: } }; +void ShenandoahHeap::initialize_generations() { + _global_generation->post_initialize(this); +} + +// We do not call this explicitly It is called by Hotspot infrastructure. void ShenandoahHeap::post_initialize() { CollectedHeap::post_initialize(); @@ -717,6 +719,10 @@ void ShenandoahHeap::post_initialize() { JFR_ONLY(ShenandoahJFRSupport::register_jfr_type_serializers();) } +void ShenandoahHeap::post_initialize_heuristics() { + _global_generation->post_initialize_heuristics(); +} + ShenandoahHeuristics* ShenandoahHeap::heuristics() { return _global_generation->heuristics(); } @@ -760,6 +766,7 @@ void ShenandoahHeap::set_soft_max_capacity(size_t v) { "Should be in bounds: %zu <= %zu <= %zu", min_capacity(), v, max_capacity()); _soft_max_size.store_relaxed(v); + heuristics()->compute_headroom_adjustment(); } size_t ShenandoahHeap::min_capacity() const { @@ -835,6 +842,10 @@ void ShenandoahHeap::notify_heap_changed() { _heap_changed.try_set(); } +void ShenandoahHeap::start_idle_span() { + heuristics()->start_idle_span(); +} + void ShenandoahHeap::set_forced_counters_update(bool value) { monitoring_support()->set_forced_counters_update(value); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 85ad339469d..d4604be0aec 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -195,6 +195,7 @@ public: ShenandoahHeap(ShenandoahCollectorPolicy* policy); jint initialize() override; void post_initialize() override; + virtual void initialize_generations(); void initialize_mode(); virtual void initialize_heuristics(); virtual void post_initialize_heuristics(); @@ -393,6 +394,8 @@ public: return _heap_changed.try_unset(); } + virtual void start_idle_span(); + void set_concurrent_young_mark_in_progress(bool in_progress); void set_concurrent_old_mark_in_progress(bool in_progress); void set_evacuation_in_progress(bool in_progress); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 4fda65b4030..1b12909bcaf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -422,6 +422,7 @@ void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent // At the end of old-gen, we may find that we have reclaimed immediate garbage, allowing a longer allocation runway. // We may also find that we have accumulated canddiate regions for mixed evacuation. If so, we will want to expand // the OldCollector reserve in order to make room for these mixed evacuations. + assert(ShenandoahHeap::heap()->mode()->is_generational(), "sanity"); assert(young_trash_regions == 0, "sanity"); ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); @@ -765,6 +766,7 @@ size_t ShenandoahOldGeneration::used_regions_size() const { return used_regions * ShenandoahHeapRegion::region_size_bytes(); } +// For the old generation, max_capacity() equals soft_max_capacity() size_t ShenandoahOldGeneration::max_capacity() const { size_t total_regions = _free_set->total_old_regions(); return total_regions * ShenandoahHeapRegion::region_size_bytes(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index ec4b7c7217c..fe92a3a3e08 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -36,7 +36,8 @@ ShenandoahRegulatorThread::ShenandoahRegulatorThread(ShenandoahGenerationalContr _heap(ShenandoahHeap::heap()), _control_thread(control_thread), _sleep(ShenandoahControlIntervalMin), - _last_sleep_adjust_time(os::elapsedTime()) { + _most_recent_wake_time(os::elapsedTime()), + _last_sleep_adjust_time(_most_recent_wake_time) { shenandoah_assert_generational(); _old_heuristics = _heap->old_generation()->heuristics(); _young_heuristics = _heap->young_generation()->heuristics(); @@ -115,19 +116,22 @@ void ShenandoahRegulatorThread::regulator_sleep() { // Wait before performing the next action. If allocation happened during this wait, // we exit sooner, to let heuristics re-evaluate new conditions. If we are at idle, // back off exponentially. - double current = os::elapsedTime(); - + double before_sleep_time = _most_recent_wake_time; if (ShenandoahHeap::heap()->has_changed()) { _sleep = ShenandoahControlIntervalMin; - } else if ((current - _last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ + } else if ((before_sleep_time - _last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ _sleep = MIN2(ShenandoahControlIntervalMax, MAX2(1u, _sleep * 2)); - _last_sleep_adjust_time = current; + _last_sleep_adjust_time = before_sleep_time; } SuspendibleThreadSetLeaver leaver; os::naked_short_sleep(_sleep); + double wake_time = os::elapsedTime(); + _most_recent_period = wake_time - _most_recent_wake_time; + _most_recent_wake_time = wake_time; + _young_heuristics->update_should_start_query_times(_most_recent_wake_time, double(_sleep) / 1000.0); if (LogTarget(Debug, gc, thread)::is_enabled()) { - double elapsed = os::elapsedTime() - current; + double elapsed = _most_recent_wake_time - before_sleep_time; double hiccup = elapsed - double(_sleep); if (hiccup > 0.001) { log_debug(gc, thread)("Regulator hiccup time: %.3fs", hiccup); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp index 2519025b6fb..cc41bc2c65b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp @@ -79,7 +79,10 @@ class ShenandoahRegulatorThread: public ConcurrentGCThread { ShenandoahOldHeuristics* _old_heuristics; ShenandoahHeuristics* _global_heuristics; + // duration of planned regulator sleep period, in ms uint _sleep; + double _most_recent_wake_time; + double _most_recent_period; double _last_sleep_adjust_time; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 3eb1a06a911..d3e9a1f9fae 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -34,6 +34,59 @@ range, \ constraint) \ \ + product(uint, ShenandoahAccelerationSamplePeriod, 15, EXPERIMENTAL, \ + "When at least this much time (measured in ms) has passed " \ + "since the acceleration allocation rate was most recently " \ + "sampled, capture another allocation rate sample for the purpose "\ + "of detecting acceleration or momentary spikes in allocation " \ + "rate. A smaller value allows quicker response to changes in " \ + "allocation rates but is more vulnerable to noise and requires " \ + "more monitoring effort.") \ + range(1, 1000) \ + \ + product(uint, ShenandoahRateAccelerationSampleSize, 8, EXPERIMENTAL, \ + "In selected ShenandoahControlIntervals " \ + "(if ShenandoahAccelerationSamplePeriod ms have passed " \ + "since previous allocation rate sample), " \ + "we compute the allocation rate since the previous rate was " \ + "sampled. This many samples are analyzed to determine whether " \ + "allocation rates are accelerating. Acceleration may occur " \ + "due to increasing client demand or due to phase changes in " \ + "an application. A larger value reduces sensitivity to " \ + "noise and delays recognition of the accelerating trend. A " \ + "larger value may also cause the heuristic to miss detection " \ + "of very quick accelerations. Smaller values may cause random " \ + "noise to be perceived as acceleration of allocation rate, " \ + "triggering excess collections. Note that the acceleration " \ + "need not last the entire span of the sampled duration to be " \ + "detected. If the last several of all samples are signficantly " \ + "larger than the other samples, the best fit line through all " \ + "sampled values will have an upward slope, manifesting as " \ + "acceleration.") \ + range(1,64) \ + \ + product(uint, ShenandoahMomentaryAllocationRateSpikeSampleSize, \ + 2, EXPERIMENTAL, \ + "In selected ShenandoahControlIntervals " \ + "(if ShenandoahAccelerationSamplePeriod ms have passed " \ + "since previous allocation rate sample), we compute " \ + "the allocation rate since the previous rate was sampled. " \ + "The weighted average of this " \ + "many most recent momentary allocation rate samples is compared " \ + "against current allocation runway and anticipated GC time to " \ + "determine whether a spike in momentary allocation rate " \ + "justifies an early GC trigger. Momentary allocation spike " \ + "detection is in addition to previously implemented " \ + "ShenandoahAdaptiveInitialSpikeThreshold, the latter of which " \ + "is more effective at detecting slower spikes. The latter " \ + "spike detection samples at the rate specifieid by " \ + "ShenandoahAdaptiveSampleFrequencyHz. The value of this " \ + "parameter must be less than the value of " \ + "ShenandoahRateAccelerationSampleSize. A larger value makes " \ + "momentary spike detection less sensitive. A smaller value " \ + "may result in excessive GC triggers.") \ + range(1,64) \ + \ product(uintx, ShenandoahGenerationalMinPIPUsage, 30, EXPERIMENTAL, \ "(Generational mode only) What percent of a heap region " \ "should be used before we consider promoting a region in " \ From 297812eec1ad5c9f48822ace2bd720fd02c6b263 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 3 Mar 2026 09:54:54 +0000 Subject: [PATCH 128/636] 8378867: jpackage references non-existing "message.app-image-requires-identifier" l10n key Reviewed-by: almatvee --- .../internal/MacApplicationBuilder.java | 9 +++--- .../resources/MacResources.properties | 2 ++ .../cli/OptionsValidationFailTest.excludes | 1 + test/jdk/tools/jpackage/share/ErrorTest.java | 31 ++++++++++++++++++- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java index a73a6152f6d..43590fb5e2c 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java @@ -32,7 +32,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import jdk.jpackage.internal.util.RootedPath; import java.util.stream.Stream; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.AppImageSigningConfig; @@ -40,6 +39,8 @@ import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacApplicationMixin; +import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.util.RootedPath; final class MacApplicationBuilder { @@ -183,10 +184,8 @@ final class MacApplicationBuilder { } catch (IOException ex) { throw new UncheckedIOException(ex); } catch (Exception ex) { - throw I18N.buildConfigException("message.app-image-requires-identifier") - .advice("message.app-image-requires-identifier.advice") - .cause(ex) - .create(); + throw new JPackageException( + I18N.format("error.invalid-app-image-plist-file", externalInfoPlistFile), ex); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index e43cadc5782..3aebdc39d41 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -31,6 +31,8 @@ error.app-image.mac-sign.required=--mac-sign option is required with predefined error.invalid-runtime-image-missing-file=Runtime image "{0}" is missing "{1}" file error.invalid-runtime-image-bin-dir=Runtime image "{0}" should not contain "bin" folder error.invalid-runtime-image-bin-dir.advice=Use --strip-native-commands jlink option when generating runtime image used with {0} option +error.invalid-app-image-plist-file=Invalid "{0}" file in the predefined application image + resource.app-info-plist=Application Info.plist resource.app-runtime-info-plist=Embedded Java Runtime Info.plist resource.runtime-info-plist=Java Runtime Info.plist diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes index 0b98c051238..f86fb9a3897 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes @@ -23,6 +23,7 @@ ErrorTest.test(NATIVE; app-desc=Hello; args-add=[--mac-app-store, --runtime-imag ErrorTest.test(NATIVE; app-desc=Hello; args-add=[--runtime-image, @@EMPTY_DIR@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@EMPTY_DIR@@, lib/**/libjli.dylib]]) ErrorTest.test(NATIVE; app-desc=Hello; args-add=[--runtime-image, @@INVALID_MAC_RUNTIME_BUNDLE@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@INVALID_MAC_RUNTIME_BUNDLE@@, Contents/Home/lib/**/libjli.dylib]]) ErrorTest.test(NATIVE; app-desc=Hello; args-add=[--runtime-image, @@INVALID_MAC_RUNTIME_IMAGE@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@INVALID_MAC_RUNTIME_IMAGE@@, lib/**/libjli.dylib]]) +ErrorTest.test(NATIVE; args-add=[--app-image, @@MAC_APP_IMAGE_INVALID_INFO_PLIST@@]; errors=[message.error-header+[error.invalid-app-image-plist-file, @@MAC_APP_IMAGE_INVALID_INFO_PLIST@@]]) ErrorTest.test(NATIVE; args-add=[--runtime-image, @@EMPTY_DIR@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@EMPTY_DIR@@, lib/**/libjli.dylib]]) ErrorTest.test(NATIVE; args-add=[--runtime-image, @@INVALID_MAC_RUNTIME_BUNDLE@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@INVALID_MAC_RUNTIME_BUNDLE@@, Contents/Home/lib/**/libjli.dylib]]) ErrorTest.test(NATIVE; args-add=[--runtime-image, @@INVALID_MAC_RUNTIME_IMAGE@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@INVALID_MAC_RUNTIME_IMAGE@@, lib/**/libjli.dylib]]) diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index 2133823381c..8bc7ba2521f 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -26,6 +26,11 @@ import static java.util.stream.Collectors.toMap; import static jdk.internal.util.OperatingSystem.LINUX; import static jdk.internal.util.OperatingSystem.MACOS; import static jdk.internal.util.OperatingSystem.WINDOWS; +import static jdk.jpackage.internal.util.PListWriter.writeDict; +import static jdk.jpackage.internal.util.PListWriter.writePList; +import static jdk.jpackage.internal.util.XmlUtils.createXml; +import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import static jdk.jpackage.test.JPackageCommand.makeAdvice; import static jdk.jpackage.test.JPackageCommand.makeError; @@ -46,6 +51,7 @@ import java.util.regex.Pattern; import java.util.stream.IntStream; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.TokenReplace; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; @@ -54,6 +60,7 @@ import jdk.jpackage.test.CannedArgument; import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.MacSign; import jdk.jpackage.test.MacSign.CertificateRequest; import jdk.jpackage.test.MacSign.CertificateType; @@ -105,6 +112,11 @@ public final class ErrorTest { final var appImageRoot = TKit.createTempDirectory("appimage"); final var appImageCmd = JPackageCommand.helloAppImage() + // Use the default jpackage tool provider to create an application image. + // The ErrorTest is used from the OptionsValidationFailTest unit tests that override + // the default jpackage tool provider with the implementation that doesn't do packaging + // and can not create a valid application image. + .useToolProvider(JavaTool.JPACKAGE.asToolProvider()) .setFakeRuntime().setArgumentValue("--dest", appImageRoot); appImageCmd.execute(); @@ -124,6 +136,19 @@ public final class ErrorTest { return appImageCmd.outputBundle(); }), + MAC_APP_IMAGE_INVALID_INFO_PLIST(toFunction(cmd -> { + var appImageDir = (Path)APP_IMAGE.expand(cmd).orElseThrow(); + // Replace the default Info.plist file with an empty one. + var plistFile = new MacBundle(appImageDir).infoPlistFile(); + TKit.trace(String.format("Create invalid plist file in [%s]", plistFile)); + createXml(plistFile, xml -> { + writePList(xml, toXmlConsumer(() -> { + writeDict(xml, toXmlConsumer(() -> { + })); + })); + }); + return appImageDir; + })), INVALID_MAC_RUNTIME_BUNDLE(toSupplier(() -> { // Has "Contents/MacOS/libjli.dylib", but missing "Contents/Home/lib/libjli.dylib". final Path root = TKit.createTempDirectory("mac-invalid-runtime-bundle"); @@ -963,7 +988,11 @@ public final class ErrorTest { testSpec().noAppDesc().nativeType().addArgs("--app-image", Token.EMPTY_DIR.token()) .error("error.parameter-not-mac-bundle", JPackageCommand.cannedArgument(cmd -> { return Path.of(cmd.getArgumentValue("--app-image")); - }, Token.EMPTY_DIR.token()), "--app-image") + }, Token.EMPTY_DIR.token()), "--app-image"), + testSpec().nativeType().noAppDesc().addArgs("--app-image", Token.MAC_APP_IMAGE_INVALID_INFO_PLIST.token()) + .error("error.invalid-app-image-plist-file", JPackageCommand.cannedArgument(cmd -> { + return new MacBundle(Path.of(cmd.getArgumentValue("--app-image"))).infoPlistFile(); + }, Token.MAC_APP_IMAGE_INVALID_INFO_PLIST.token())) ).map(TestSpec.Builder::create).toList()); macInvalidRuntime(testCases::add); From 57db48cc21d47475caf2d1fae6bf37eab8d7521e Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Tue, 3 Mar 2026 10:01:00 +0000 Subject: [PATCH 129/636] 8373290: Update FreeType to 2.14.1 Reviewed-by: azvegint, serb, prr --- src/java.desktop/share/legal/freetype.md | 40 +- .../include/freetype/config/ftconfig.h | 2 +- .../include/freetype/config/ftheader.h | 2 +- .../include/freetype/config/ftoption.h | 49 +- .../include/freetype/config/ftstdlib.h | 2 +- .../include/freetype/config/integer-types.h | 31 +- .../include/freetype/config/mac-support.h | 2 +- .../include/freetype/config/public-macros.h | 6 +- .../libfreetype/include/freetype/freetype.h | 98 +- .../libfreetype/include/freetype/ftadvanc.h | 2 +- .../libfreetype/include/freetype/ftbbox.h | 2 +- .../libfreetype/include/freetype/ftbdf.h | 2 +- .../libfreetype/include/freetype/ftbitmap.h | 2 +- .../libfreetype/include/freetype/ftcid.h | 2 +- .../libfreetype/include/freetype/ftcolor.h | 17 +- .../libfreetype/include/freetype/ftdriver.h | 6 +- .../libfreetype/include/freetype/fterrdef.h | 2 +- .../libfreetype/include/freetype/fterrors.h | 2 +- .../libfreetype/include/freetype/ftfntfmt.h | 2 +- .../libfreetype/include/freetype/ftgasp.h | 2 +- .../libfreetype/include/freetype/ftglyph.h | 2 +- .../libfreetype/include/freetype/ftgzip.h | 2 +- .../libfreetype/include/freetype/ftimage.h | 8 +- .../libfreetype/include/freetype/ftincrem.h | 2 +- .../libfreetype/include/freetype/ftlcdfil.h | 2 +- .../libfreetype/include/freetype/ftlist.h | 2 +- .../libfreetype/include/freetype/ftlogging.h | 2 +- .../libfreetype/include/freetype/ftmac.h | 2 +- .../libfreetype/include/freetype/ftmm.h | 256 ++- .../libfreetype/include/freetype/ftmodapi.h | 2 +- .../libfreetype/include/freetype/ftmoderr.h | 2 +- .../libfreetype/include/freetype/ftoutln.h | 2 +- .../libfreetype/include/freetype/ftparams.h | 2 +- .../libfreetype/include/freetype/ftrender.h | 2 +- .../libfreetype/include/freetype/ftsizes.h | 2 +- .../libfreetype/include/freetype/ftsnames.h | 2 +- .../libfreetype/include/freetype/ftstroke.h | 2 +- .../libfreetype/include/freetype/ftsynth.h | 2 +- .../libfreetype/include/freetype/ftsystem.h | 2 +- .../libfreetype/include/freetype/fttrigon.h | 2 +- .../libfreetype/include/freetype/fttypes.h | 2 +- .../include/freetype/internal/autohint.h | 2 +- .../include/freetype/internal/cffotypes.h | 2 +- .../include/freetype/internal/cfftypes.h | 2 +- .../freetype/internal/compiler-macros.h | 6 +- .../include/freetype/internal/ftcalc.h | 300 +-- .../include/freetype/internal/ftdebug.h | 2 +- .../include/freetype/internal/ftdrv.h | 2 +- .../include/freetype/internal/ftgloadr.h | 2 +- .../include/freetype/internal/fthash.h | 23 + .../include/freetype/internal/ftmemory.h | 2 +- .../include/freetype/internal/ftmmtypes.h | 2 +- .../include/freetype/internal/ftobjs.h | 30 +- .../include/freetype/internal/ftpsprop.h | 2 +- .../include/freetype/internal/ftrfork.h | 2 +- .../include/freetype/internal/ftserv.h | 2 +- .../include/freetype/internal/ftstream.h | 2 +- .../include/freetype/internal/fttrace.h | 5 +- .../include/freetype/internal/ftvalid.h | 2 +- .../include/freetype/internal/psaux.h | 2 +- .../include/freetype/internal/pshints.h | 2 +- .../freetype/internal/services/svbdf.h | 2 +- .../freetype/internal/services/svcfftl.h | 2 +- .../freetype/internal/services/svcid.h | 2 +- .../freetype/internal/services/svfntfmt.h | 2 +- .../freetype/internal/services/svgldict.h | 2 +- .../freetype/internal/services/svgxval.h | 2 +- .../freetype/internal/services/svkern.h | 2 +- .../freetype/internal/services/svmetric.h | 4 +- .../include/freetype/internal/services/svmm.h | 2 +- .../freetype/internal/services/svotval.h | 2 +- .../freetype/internal/services/svpfr.h | 2 +- .../freetype/internal/services/svpostnm.h | 2 +- .../freetype/internal/services/svprop.h | 2 +- .../freetype/internal/services/svpscmap.h | 2 +- .../freetype/internal/services/svpsinfo.h | 2 +- .../freetype/internal/services/svsfnt.h | 2 +- .../freetype/internal/services/svttcmap.h | 2 +- .../freetype/internal/services/svtteng.h | 2 +- .../freetype/internal/services/svttglyf.h | 2 +- .../freetype/internal/services/svwinfnt.h | 2 +- .../include/freetype/internal/sfnt.h | 8 +- .../include/freetype/internal/svginterface.h | 2 +- .../include/freetype/internal/t1types.h | 2 +- .../include/freetype/internal/tttypes.h | 41 +- .../include/freetype/internal/wofftypes.h | 2 +- .../libfreetype/include/freetype/otsvg.h | 2 +- .../libfreetype/include/freetype/t1tables.h | 2 +- .../libfreetype/include/freetype/ttnameid.h | 258 +-- .../libfreetype/include/freetype/tttables.h | 13 +- .../libfreetype/include/freetype/tttags.h | 2 +- .../native/libfreetype/include/ft2build.h | 2 +- .../native/libfreetype/src/autofit/afadjust.c | 1612 +++++++++++++++++ .../native/libfreetype/src/autofit/afadjust.h | 130 ++ .../native/libfreetype/src/autofit/afblue.c | 194 +- .../native/libfreetype/src/autofit/afblue.cin | 2 +- .../native/libfreetype/src/autofit/afblue.dat | 194 +- .../native/libfreetype/src/autofit/afblue.h | 11 +- .../native/libfreetype/src/autofit/afblue.hin | 11 +- .../native/libfreetype/src/autofit/afcjk.c | 49 +- .../native/libfreetype/src/autofit/afcjk.h | 2 +- .../native/libfreetype/src/autofit/afcover.h | 2 +- .../native/libfreetype/src/autofit/afdummy.c | 2 +- .../native/libfreetype/src/autofit/afdummy.h | 2 +- .../native/libfreetype/src/autofit/aferrors.h | 2 +- .../native/libfreetype/src/autofit/afglobal.c | 40 +- .../native/libfreetype/src/autofit/afglobal.h | 15 +- .../native/libfreetype/src/autofit/afhints.c | 23 +- .../native/libfreetype/src/autofit/afhints.h | 19 +- .../native/libfreetype/src/autofit/afindic.c | 2 +- .../native/libfreetype/src/autofit/afindic.h | 2 +- .../native/libfreetype/src/autofit/aflatin.c | 1555 +++++++++++++++- .../native/libfreetype/src/autofit/aflatin.h | 25 +- .../native/libfreetype/src/autofit/afloader.c | 2 +- .../native/libfreetype/src/autofit/afloader.h | 2 +- .../native/libfreetype/src/autofit/afmodule.c | 14 +- .../native/libfreetype/src/autofit/afmodule.h | 8 +- .../native/libfreetype/src/autofit/afranges.c | 63 +- .../native/libfreetype/src/autofit/afranges.h | 2 +- .../native/libfreetype/src/autofit/afscript.h | 2 +- .../native/libfreetype/src/autofit/afshaper.c | 340 ++-- .../native/libfreetype/src/autofit/afshaper.h | 19 +- .../native/libfreetype/src/autofit/afstyles.h | 58 +- .../native/libfreetype/src/autofit/aftypes.h | 6 +- .../libfreetype/src/autofit/afws-decl.h | 2 +- .../libfreetype/src/autofit/afws-iter.h | 2 +- .../native/libfreetype/src/autofit/ft-hb.c | 197 ++ .../native/libfreetype/src/autofit/ft-hb.h | 82 + .../native/libfreetype/src/base/ftadvanc.c | 2 +- .../native/libfreetype/src/base/ftbase.h | 4 +- .../native/libfreetype/src/base/ftbbox.c | 2 +- .../native/libfreetype/src/base/ftbitmap.c | 12 +- .../native/libfreetype/src/base/ftcalc.c | 146 +- .../share/native/libfreetype/src/base/ftcid.c | 2 +- .../native/libfreetype/src/base/ftcolor.c | 21 +- .../native/libfreetype/src/base/ftdbgmem.c | 3 +- .../native/libfreetype/src/base/ftdebug.c | 4 +- .../native/libfreetype/src/base/ftfntfmt.c | 2 +- .../native/libfreetype/src/base/ftfstype.c | 2 +- .../native/libfreetype/src/base/ftgasp.c | 2 +- .../native/libfreetype/src/base/ftgloadr.c | 2 +- .../native/libfreetype/src/base/ftglyph.c | 2 +- .../native/libfreetype/src/base/fthash.c | 104 +- .../native/libfreetype/src/base/ftinit.c | 2 +- .../native/libfreetype/src/base/ftlcdfil.c | 2 +- .../share/native/libfreetype/src/base/ftmac.c | 2 +- .../share/native/libfreetype/src/base/ftmm.c | 37 +- .../native/libfreetype/src/base/ftobjs.c | 64 +- .../native/libfreetype/src/base/ftoutln.c | 2 +- .../native/libfreetype/src/base/ftpatent.c | 2 +- .../native/libfreetype/src/base/ftpsprop.c | 2 +- .../native/libfreetype/src/base/ftrfork.c | 10 +- .../native/libfreetype/src/base/ftsnames.c | 2 +- .../native/libfreetype/src/base/ftstream.c | 4 +- .../native/libfreetype/src/base/ftstroke.c | 24 +- .../native/libfreetype/src/base/ftsynth.c | 4 +- .../native/libfreetype/src/base/ftsystem.c | 4 +- .../native/libfreetype/src/base/fttrigon.c | 2 +- .../native/libfreetype/src/base/fttype1.c | 2 +- .../native/libfreetype/src/base/ftutil.c | 2 +- .../native/libfreetype/src/cff/cffcmap.c | 2 +- .../native/libfreetype/src/cff/cffcmap.h | 2 +- .../native/libfreetype/src/cff/cffdrivr.c | 162 +- .../native/libfreetype/src/cff/cffdrivr.h | 2 +- .../native/libfreetype/src/cff/cfferrs.h | 2 +- .../native/libfreetype/src/cff/cffgload.c | 70 +- .../native/libfreetype/src/cff/cffgload.h | 2 +- .../native/libfreetype/src/cff/cffload.c | 16 +- .../native/libfreetype/src/cff/cffload.h | 2 +- .../native/libfreetype/src/cff/cffobjs.c | 16 +- .../native/libfreetype/src/cff/cffobjs.h | 2 +- .../native/libfreetype/src/cff/cffparse.c | 10 +- .../native/libfreetype/src/cff/cffparse.h | 2 +- .../native/libfreetype/src/cff/cfftoken.h | 2 +- .../native/libfreetype/src/cid/ciderrs.h | 2 +- .../native/libfreetype/src/cid/cidgload.c | 46 +- .../native/libfreetype/src/cid/cidgload.h | 2 +- .../native/libfreetype/src/cid/cidload.c | 2 +- .../native/libfreetype/src/cid/cidload.h | 2 +- .../native/libfreetype/src/cid/cidobjs.c | 2 +- .../native/libfreetype/src/cid/cidobjs.h | 2 +- .../native/libfreetype/src/cid/cidparse.c | 2 +- .../native/libfreetype/src/cid/cidparse.h | 2 +- .../native/libfreetype/src/cid/cidriver.c | 2 +- .../native/libfreetype/src/cid/cidriver.h | 2 +- .../native/libfreetype/src/cid/cidtoken.h | 2 +- .../native/libfreetype/src/psaux/afmparse.c | 2 +- .../native/libfreetype/src/psaux/afmparse.h | 2 +- .../native/libfreetype/src/psaux/cffdecode.c | 6 +- .../native/libfreetype/src/psaux/cffdecode.h | 2 +- .../native/libfreetype/src/psaux/psauxerr.h | 2 +- .../native/libfreetype/src/psaux/psauxmod.c | 2 +- .../native/libfreetype/src/psaux/psauxmod.h | 5 +- .../native/libfreetype/src/psaux/psconv.c | 2 +- .../native/libfreetype/src/psaux/psconv.h | 2 +- .../native/libfreetype/src/psaux/psintrp.c | 2 +- .../native/libfreetype/src/psaux/psobjs.c | 9 +- .../native/libfreetype/src/psaux/psobjs.h | 2 +- .../native/libfreetype/src/psaux/t1cmap.c | 2 +- .../native/libfreetype/src/psaux/t1cmap.h | 2 +- .../native/libfreetype/src/psaux/t1decode.c | 4 +- .../native/libfreetype/src/psaux/t1decode.h | 2 +- .../native/libfreetype/src/pshinter/pshalgo.c | 4 +- .../native/libfreetype/src/pshinter/pshalgo.h | 2 +- .../native/libfreetype/src/pshinter/pshglob.c | 2 +- .../native/libfreetype/src/pshinter/pshglob.h | 2 +- .../native/libfreetype/src/pshinter/pshmod.c | 2 +- .../native/libfreetype/src/pshinter/pshmod.h | 2 +- .../libfreetype/src/pshinter/pshnterr.h | 2 +- .../native/libfreetype/src/pshinter/pshrec.c | 12 +- .../native/libfreetype/src/pshinter/pshrec.h | 2 +- .../native/libfreetype/src/psnames/psmodule.c | 2 +- .../native/libfreetype/src/psnames/psmodule.h | 2 +- .../native/libfreetype/src/psnames/psnamerr.h | 2 +- .../native/libfreetype/src/psnames/pstables.h | 2 +- .../native/libfreetype/src/raster/ftmisc.h | 2 +- .../native/libfreetype/src/raster/ftraster.c | 18 +- .../native/libfreetype/src/raster/ftraster.h | 2 +- .../native/libfreetype/src/raster/ftrend1.c | 2 +- .../native/libfreetype/src/raster/ftrend1.h | 2 +- .../native/libfreetype/src/raster/rasterrs.h | 2 +- .../native/libfreetype/src/sfnt/pngshim.c | 2 +- .../native/libfreetype/src/sfnt/pngshim.h | 2 +- .../native/libfreetype/src/sfnt/sfdriver.c | 4 +- .../native/libfreetype/src/sfnt/sfdriver.h | 2 +- .../native/libfreetype/src/sfnt/sferrors.h | 2 +- .../native/libfreetype/src/sfnt/sfobjs.c | 9 +- .../native/libfreetype/src/sfnt/sfobjs.h | 2 +- .../native/libfreetype/src/sfnt/sfwoff.c | 2 +- .../native/libfreetype/src/sfnt/sfwoff.h | 2 +- .../native/libfreetype/src/sfnt/sfwoff2.c | 8 +- .../native/libfreetype/src/sfnt/sfwoff2.h | 2 +- .../native/libfreetype/src/sfnt/ttcmap.c | 22 +- .../native/libfreetype/src/sfnt/ttcmap.h | 2 +- .../native/libfreetype/src/sfnt/ttcmapc.h | 2 +- .../native/libfreetype/src/sfnt/ttcolr.c | 4 +- .../native/libfreetype/src/sfnt/ttcolr.h | 2 +- .../native/libfreetype/src/sfnt/ttcpal.c | 2 +- .../native/libfreetype/src/sfnt/ttcpal.h | 2 +- .../native/libfreetype/src/sfnt/ttkern.c | 5 +- .../native/libfreetype/src/sfnt/ttkern.h | 8 +- .../native/libfreetype/src/sfnt/ttload.c | 32 +- .../native/libfreetype/src/sfnt/ttload.h | 2 +- .../share/native/libfreetype/src/sfnt/ttmtx.c | 4 +- .../share/native/libfreetype/src/sfnt/ttmtx.h | 2 +- .../native/libfreetype/src/sfnt/ttpost.c | 2 +- .../native/libfreetype/src/sfnt/ttpost.h | 2 +- .../native/libfreetype/src/sfnt/ttsbit.c | 55 +- .../native/libfreetype/src/sfnt/ttsbit.h | 2 +- .../native/libfreetype/src/sfnt/woff2tags.c | 2 +- .../native/libfreetype/src/sfnt/woff2tags.h | 2 +- .../native/libfreetype/src/smooth/ftgrays.c | 84 +- .../native/libfreetype/src/smooth/ftgrays.h | 13 +- .../native/libfreetype/src/smooth/ftsmerrs.h | 2 +- .../native/libfreetype/src/smooth/ftsmooth.c | 2 +- .../native/libfreetype/src/smooth/ftsmooth.h | 2 +- .../libfreetype/src/truetype/ttdriver.c | 6 +- .../libfreetype/src/truetype/ttdriver.h | 2 +- .../libfreetype/src/truetype/tterrors.h | 2 +- .../native/libfreetype/src/truetype/ttgload.c | 423 ++--- .../native/libfreetype/src/truetype/ttgload.h | 2 +- .../native/libfreetype/src/truetype/ttgxvar.c | 702 +++++-- .../native/libfreetype/src/truetype/ttgxvar.h | 7 +- .../libfreetype/src/truetype/ttinterp.c | 1587 +++++++--------- .../libfreetype/src/truetype/ttinterp.h | 128 +- .../native/libfreetype/src/truetype/ttobjs.c | 450 ++--- .../native/libfreetype/src/truetype/ttobjs.h | 127 +- .../native/libfreetype/src/truetype/ttpload.c | 18 +- .../native/libfreetype/src/truetype/ttpload.h | 2 +- .../libfreetype/src/truetype/ttsubpix.c | 1013 ----------- .../libfreetype/src/truetype/ttsubpix.h | 110 -- .../native/libfreetype/src/type1/t1afm.c | 2 +- .../native/libfreetype/src/type1/t1afm.h | 2 +- .../native/libfreetype/src/type1/t1driver.c | 2 +- .../native/libfreetype/src/type1/t1driver.h | 2 +- .../native/libfreetype/src/type1/t1errors.h | 2 +- .../native/libfreetype/src/type1/t1gload.c | 44 +- .../native/libfreetype/src/type1/t1gload.h | 2 +- .../native/libfreetype/src/type1/t1load.c | 8 +- .../native/libfreetype/src/type1/t1load.h | 2 +- .../native/libfreetype/src/type1/t1objs.c | 2 +- .../native/libfreetype/src/type1/t1objs.h | 2 +- .../native/libfreetype/src/type1/t1parse.c | 2 +- .../native/libfreetype/src/type1/t1parse.h | 2 +- .../native/libfreetype/src/type1/t1tokens.h | 2 +- .../html/CSS/8231286/HtmlFontSizeTest.java | 18 +- 286 files changed, 7169 insertions(+), 4787 deletions(-) create mode 100644 src/java.desktop/share/native/libfreetype/src/autofit/afadjust.c create mode 100644 src/java.desktop/share/native/libfreetype/src/autofit/afadjust.h create mode 100644 src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.c create mode 100644 src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.h delete mode 100644 src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.c delete mode 100644 src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.h diff --git a/src/java.desktop/share/legal/freetype.md b/src/java.desktop/share/legal/freetype.md index 5df525e2f67..cf2518df594 100644 --- a/src/java.desktop/share/legal/freetype.md +++ b/src/java.desktop/share/legal/freetype.md @@ -1,4 +1,4 @@ -## The FreeType Project: Freetype v2.13.3 +## The FreeType Project: Freetype v2.14.1 ### FreeType Notice @@ -21,25 +21,24 @@ which fits your needs best. ### FreeType License ``` -Copyright (C) 1996-2024 by David Turner, Robert Wilhelm, and Werner Lemberg. -Copyright (C) 2007-2024 by Dereg Clegg and Michael Toftdal. -Copyright (C) 1996-2024 by Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. -Copyright (C) 2022-2024 by David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and -Copyright (C) 2004-2024 by Masatake YAMATO and Redhat K.K. -Copyright (C) 2007-2024 by Derek Clegg and Michael Toftdal. -Copyright (C) 2003-2024 by Masatake YAMATO, Red Hat K.K., -Copyright (C) 1996-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches. -Copyright (C) 2007-2024 by David Turner. -Copyright (C) 2022-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti. -Copyright (C) 2007-2024 by Rahul Bhalerao , . -Copyright (C) 2008-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya. -Copyright (C) 2013-2024 by Google, Inc. -Copyright (C) 2019-2024 by Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. -Copyright (C) 2009-2024 by Oran Agra and Mickey Gabel. -Copyright (C) 2018-2024 by David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg. -Copyright (C) 2004-2024 by David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. - - +Copyright (C) 1996-2025 by David Turner, Robert Wilhelm, and Werner Lemberg. +Copyright (C) 2007-2025 by Dereg Clegg and Michael Toftdal. +Copyright (C) 1996-2025 by Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. +Copyright (C) 2022-2025 by David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and +Copyright (C) 2004-2025 by Masatake YAMATO and Redhat K.K. +Copyright (C) 2007-2025 by Derek Clegg and Michael Toftdal. +Copyright (C) 2003-2025 by Masatake YAMATO, Red Hat K.K., +Copyright (C) 1996-2025 by David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches. +Copyright (C) 2007-2025 by David Turner. +Copyright (C) 2022-2025 by David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti. +Copyright (C) 2007-2025 by Rahul Bhalerao , . +Copyright (C) 2025 by Behdad Esfahbod. +Copyright (C) 2008-2025 by David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya. +Copyright (C) 2013-2025 by Google, Inc. +Copyright (C) 2019-2025 by Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. +Copyright (C) 2009-2025 by Oran Agra and Mickey Gabel. +Copyright (C) 2018-2025 by David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg. +Copyright (C) 2004-2025 by David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. The FreeType Project LICENSE ---------------------------- @@ -207,6 +206,7 @@ Legal Terms https://www.freetype.org + ``` ### GPL v2 diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h index 0667493fec6..d66c5df9976 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftconfig.h @@ -4,7 +4,7 @@ * * ANSI-specific configuration file (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h index f6ef2618ded..16eab9048fc 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftheader.h @@ -4,7 +4,7 @@ * * Build macros of the FreeType 2 library. * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h index d29a0a7cefb..be7a4c50dc3 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftoption.h @@ -4,7 +4,7 @@ * * User-selectable configuration macros (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -158,12 +158,12 @@ FT_BEGIN_HEADER /************************************************************************** * - * If this macro is defined, try to use an inlined assembler version of the - * @FT_MulFix function, which is a 'hotspot' when loading and hinting - * glyphs, and which should be executed as fast as possible. + * If this macro is defined, try to use an inlined 64-bit or assembler + * version of the @FT_MulFix function, which is a 'hotspot' when loading + * and hinting glyphs, and which should be executed as fast as possible. * - * Note that if your compiler or CPU is not supported, this will default to - * the standard and portable implementation found in `ftcalc.c`. + * If your compiler is not C99-compliant or CPU assembly is not supported, + * you can disable this option. */ #define FT_CONFIG_OPTION_INLINE_MULFIX @@ -293,6 +293,31 @@ FT_BEGIN_HEADER /* #define FT_CONFIG_OPTION_USE_HARFBUZZ */ + /************************************************************************** + * + * HarfBuzz dynamic support. + * + * Define this macro if you want the HarfBuzz library to be loaded at + * runtime instead of being linked to FreeType. + * + * This option has no effect if `FT_CONFIG_OPTION_USE_HARFBUZZ` is not + * defined. + * + * When this option is enabled, FreeType will try to load the HarfBuzz + * library at runtime, using `dlopen` or `LoadLibrary`, depending on the + * platform. On Microsoft platforms, the library name looked up is + * `libharfbuzz-0.dll`. On Apple platforms, the library name looked up + * is `libharfbuzz.0.dylib`. On all other platforms, the library name + * looked up is `libharfbuzz.so.0`. This name can be overridden by + * defining the macro `FT_LIBHARFBUZZ` at FreeType compilation time. + * + * If you use a build system like cmake or the `configure` script, + * options set by those programs have precedence, overwriting the value + * here with the configured one. + */ +/* #define FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */ + + /************************************************************************** * * Brotli support. @@ -679,7 +704,7 @@ FT_BEGIN_HEADER * defined. * * [1] - * https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx + * https://learn.microsoft.com/typography/cleartype/truetypecleartype */ #define TT_CONFIG_OPTION_SUBPIXEL_HINTING @@ -697,7 +722,7 @@ FT_BEGIN_HEADER * flags array which can be used to disambiguate, but old fonts will not * have them. * - * https://www.microsoft.com/typography/otspec/glyf.htm + * https://learn.microsoft.com/typography/opentype/spec/glyf * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */ #undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED @@ -760,10 +785,10 @@ FT_BEGIN_HEADER /************************************************************************** * * Option `TT_CONFIG_OPTION_GPOS_KERNING` enables a basic GPOS kerning - * implementation (for TrueType fonts only). With this defined, FreeType - * is able to get kerning pair data from the GPOS 'kern' feature as well as - * legacy 'kern' tables; without this defined, FreeType will only be able - * to use legacy 'kern' tables. + * implementation (for TrueType and OpenType fonts only). With this + * defined, FreeType is able to get kerning pair data from the GPOS 'kern' + * feature as well as legacy 'kern' tables; without this defined, FreeType + * will only be able to use legacy 'kern' tables. * * Note that FreeType does not support more advanced GPOS layout features; * even the 'kern' feature implemented here doesn't handle more diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h index e17aa7b89d5..f846b4456c1 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/ftstdlib.h @@ -5,7 +5,7 @@ * ANSI-specific library and header configuration file (specification * only). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h index c27505ffc4b..a0b892ece4b 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/integer-types.h @@ -4,7 +4,7 @@ * * FreeType integer types definitions. * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -17,6 +17,8 @@ #ifndef FREETYPE_CONFIG_INTEGER_TYPES_H_ #define FREETYPE_CONFIG_INTEGER_TYPES_H_ +FT_BEGIN_HEADER + /* There are systems (like the Texas Instruments 'C54x) where a `char` */ /* has 16~bits. ANSI~C says that `sizeof(char)` is always~1. Since an */ /* `int` has 16~bits also for this system, `sizeof(int)` gives~1 which */ @@ -242,9 +244,34 @@ #endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */ #ifdef FT_INT64 + typedef FT_INT64 FT_Int64; typedef FT_UINT64 FT_UInt64; -#endif +# define FT_INT64_ZERO 0 + +#else /* !FT_INT64 */ + + /* we need to emulate 64-bit data types if none are available */ + + typedef struct FT_Int64_ + { + FT_UInt32 lo; + FT_UInt32 hi; + + } FT_Int64; + + typedef struct FT_UInt64_ + { + FT_UInt32 lo; + FT_UInt32 hi; + + } FT_UInt64; + +# define FT_INT64_ZERO { 0, 0 } + +#endif /* !FT_INT64 */ + +FT_END_HEADER #endif /* FREETYPE_CONFIG_INTEGER_TYPES_H_ */ diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h index 07b6f915bd8..d1b6a9898fd 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/mac-support.h @@ -4,7 +4,7 @@ * * Mac/OS X support configuration header. * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h b/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h index f56581a6ee7..9f28b394737 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/config/public-macros.h @@ -4,7 +4,7 @@ * * Define a set of compiler macros used in public FreeType headers. * - * Copyright (C) 2020-2024 by + * Copyright (C) 2020-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -62,8 +62,8 @@ FT_BEGIN_HEADER * because it is needed by `FT_EXPORT`. */ - /* Visual C, mingw */ -#if defined( _WIN32 ) + /* Visual C, MinGW, Cygwin */ +#if defined( _WIN32 ) || defined( __CYGWIN__ ) #if defined( FT2_BUILD_LIBRARY ) && defined( DLL_EXPORT ) #define FT_PUBLIC_FUNCTION_ATTRIBUTE __declspec( dllexport ) diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h b/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h index 58fc33dfe60..1e249235882 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/freetype.h @@ -4,7 +4,7 @@ * * FreeType high-level API and common types (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -811,7 +811,7 @@ FT_BEGIN_HEADER * FT_ENCODING_MS_SYMBOL :: * Microsoft Symbol encoding, used to encode mathematical symbols and * wingdings. For more information, see - * 'https://www.microsoft.com/typography/otspec/recom.htm#non-standard-symbol-fonts', + * 'https://learn.microsoft.com/typography/opentype/spec/recom#non-standard-symbol-fonts', * 'http://www.kostis.net/charsets/symbol.htm', and * 'http://www.kostis.net/charsets/wingding.htm'. * @@ -1068,12 +1068,12 @@ FT_BEGIN_HEADER * the face in the font file (starting with value~0). They are set * to~0 if there is only one face in the font file. * - * [Since 2.6.1] Bits 16-30 are relevant to GX and OpenType variation - * fonts only, holding the named instance index for the current face - * index (starting with value~1; value~0 indicates font access without - * a named instance). For non-variation fonts, bits 16-30 are ignored. - * If we have the third named instance of face~4, say, `face_index` is - * set to 0x00030004. + * [Since 2.6.1] Bits 16-30 are relevant to TrueType GX and OpenType + * Font Variations only, holding the named instance index for the + * current face index (starting with value~1; value~0 indicates font + * access without a named instance). For non-variation fonts, bits + * 16-30 are ignored. If we have the third named instance of face~4, + * say, `face_index` is set to 0x00030004. * * Bit 31 is always zero (that is, `face_index` is always a positive * value). @@ -1092,10 +1092,10 @@ FT_BEGIN_HEADER * the face; see @FT_STYLE_FLAG_XXX for the details. * * [Since 2.6.1] Bits 16-30 hold the number of named instances - * available for the current face if we have a GX or OpenType variation - * (sub)font. Bit 31 is always zero (that is, `style_flags` is always - * a positive value). Note that a variation font has always at least - * one named instance, namely the default instance. + * available for the current face if we have a TrueType GX or OpenType + * Font Variation. Bit 31 is always zero (that is, `style_flags` is + * always a positive value). Note that a variation font has always at + * least one named instance, namely the default instance. * * num_glyphs :: * The number of glyphs in the face. If the face is scalable and has @@ -1159,7 +1159,7 @@ FT_BEGIN_HEADER * Note that the bounding box might be off by (at least) one pixel for * hinted fonts. See @FT_Size_Metrics for further discussion. * - * Note that the bounding box does not vary in OpenType variation fonts + * Note that the bounding box does not vary in OpenType Font Variations * and should only be used in relation to the default instance. * * units_per_EM :: @@ -1218,7 +1218,7 @@ FT_BEGIN_HEADER * Fields may be changed after a call to @FT_Attach_File or * @FT_Attach_Stream. * - * For an OpenType variation font, the values of the following fields can + * For OpenType Font Variations, the values of the following fields can * change after a call to @FT_Set_Var_Design_Coordinates (and friends) if * the font contains an 'MVAR' table: `ascender`, `descender`, `height`, * `underline_position`, and `underline_thickness`. @@ -1336,7 +1336,7 @@ FT_BEGIN_HEADER * FT_FACE_FLAG_MULTIPLE_MASTERS :: * The face contains multiple masters and is capable of interpolating * between them. Supported formats are Adobe MM, TrueType GX, and - * OpenType variation fonts. + * OpenType Font Variations. * * See section @multiple_masters for API details. * @@ -1609,7 +1609,7 @@ FT_BEGIN_HEADER * * @description: * A macro that returns true whenever a face object is a named instance - * of a GX or OpenType variation font. + * of a TrueType GX or OpenType Font Variations. * * [Since 2.9] Changing the design coordinates with * @FT_Set_Var_Design_Coordinates or @FT_Set_Var_Blend_Coordinates does @@ -2147,7 +2147,7 @@ FT_BEGIN_HEADER * freed. * * [Since 2.10.1] If @FT_LOAD_NO_SCALE is set, outline coordinates of - * OpenType variation fonts for a selected instance are internally + * OpenType Font Variations for a selected instance are internally * handled as 26.6 fractional font units but returned as (rounded) * integers, as expected. To get unrounded font units, don't use * @FT_LOAD_NO_SCALE but load the glyph with @FT_LOAD_NO_HINTING and @@ -2640,14 +2640,14 @@ FT_BEGIN_HEADER * the face in the font file (starting with value~0). Set it to~0 if * there is only one face in the font file. * - * [Since 2.6.1] Bits 16-30 are relevant to GX and OpenType variation - * fonts only, specifying the named instance index for the current face - * index (starting with value~1; value~0 makes FreeType ignore named - * instances). For non-variation fonts, bits 16-30 are ignored. - * Assuming that you want to access the third named instance in face~4, - * `face_index` should be set to 0x00030004. If you want to access - * face~4 without variation handling, simply set `face_index` to - * value~4. + * [Since 2.6.1] Bits 16-30 are relevant to TrueType GX and OpenType + * Font Variations only, specifying the named instance index for the + * current face index (starting with value~1; value~0 makes FreeType + * ignore named instances). For non-variation fonts, bits 16-30 are + * ignored. Assuming that you want to access the third named instance + * in face~4, `face_index` should be set to 0x00030004. If you want + * to access face~4 without variation handling, simply set + * `face_index` to value~4. * * `FT_Open_Face` and its siblings can be used to quickly check whether * the font format of a given font resource is supported by FreeType. @@ -2914,11 +2914,11 @@ FT_BEGIN_HEADER * of the available glyphs at a given ppem value is available. FreeType * silently uses outlines if there is no bitmap for a given glyph index. * - * For GX and OpenType variation fonts, a bitmap strike makes sense only - * if the default instance is active (that is, no glyph variation takes - * place); otherwise, FreeType simply ignores bitmap strikes. The same - * is true for all named instances that are different from the default - * instance. + * For TrueType GX and OpenType Font Variations, a bitmap strike makes + * sense only if the default instance is active (that is, no glyph + * variation takes place); otherwise, FreeType simply ignores bitmap + * strikes. The same is true for all named instances that are different + * from the default instance. * * Don't use this function if you are using the FreeType cache API. */ @@ -3078,7 +3078,7 @@ FT_BEGIN_HEADER * is dependent entirely on how the size is defined in the source face. * The font designer chooses the final size of each glyph relative to * this size. For more information refer to - * 'https://www.freetype.org/freetype2/docs/glyphs/glyphs-2.html'. + * 'https://freetype.org/freetype2/docs/glyphs/glyphs-2.html'. * * Contrary to @FT_Set_Char_Size, this function doesn't have special code * to normalize zero-valued widths, heights, or resolutions, which are @@ -3441,8 +3441,10 @@ FT_BEGIN_HEADER * blending of the color glyph layers associated with the glyph index, * using the same bitmap format as embedded color bitmap images. This * is mainly for convenience and works only for glyphs in 'COLR' v0 - * tables (or glyphs in 'COLR' v1 tables that exclusively use v0 - * features). For full control of color layers use + * tables. **There is no rendering support for 'COLR' v1** (with the + * exception of v1 tables that exclusively use v0 features)! You need + * a graphics library like Skia or Cairo to interpret the graphics + * commands stored in v1 tables. For full control of color layers use * @FT_Get_Color_Glyph_Layer and FreeType's color functions like * @FT_Palette_Select instead of setting @FT_LOAD_COLOR for rendering * so that the client application can handle blending by itself. @@ -3895,8 +3897,10 @@ FT_BEGIN_HEADER * * This process can cost performance. There is an approximation that * does not need to know about the background color; see - * https://bel.fi/alankila/lcd/ and - * https://bel.fi/alankila/lcd/alpcor.html for details. + * https://web.archive.org/web/20211019204945/https://bel.fi/alankila/lcd/ + * and + * https://web.archive.org/web/20210211002939/https://bel.fi/alankila/lcd/alpcor.html + * for details. * * **ATTENTION**: Linear blending is even more important when dealing * with subpixel-rendered glyphs to prevent color-fringing! A @@ -3993,13 +3997,13 @@ FT_BEGIN_HEADER * out of the scope of this API function -- they can be implemented * through format-specific interfaces. * - * Note that, for TrueType fonts only, this can extract data from both - * the 'kern' table and the basic, pair-wise kerning feature from the - * GPOS table (with `TT_CONFIG_OPTION_GPOS_KERNING` enabled), though - * FreeType does not support the more advanced GPOS layout features; use - * a library like HarfBuzz for those instead. If a font has both a - * 'kern' table and kern features of a GPOS table, the 'kern' table will - * be used. + * Note that, for TrueType and OpenType fonts only, this can extract data + * from both the 'kern' table and the basic, pair-wise kerning feature + * from the GPOS table (with `TT_CONFIG_OPTION_GPOS_KERNING` enabled), + * though FreeType does not support the more advanced GPOS layout + * features; use a library like HarfBuzz for those instead. If a font + * has both a 'kern' table and kern features of a GPOS table, the 'kern' + * table will be used. * * Also note for right-to-left scripts, the functionality may differ for * fonts with GPOS tables vs. 'kern' tables. For GPOS, right-to-left @@ -4530,7 +4534,7 @@ FT_BEGIN_HEADER * table description in the OpenType specification for the meaning of the * various flags (which get synthesized for non-OpenType subglyphs). * - * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description + * https://learn.microsoft.com/typography/opentype/spec/glyf#composite-glyph-description * * @values: * FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS :: @@ -4593,7 +4597,7 @@ FT_BEGIN_HEADER * interpreted depending on the flags returned in `*p_flags`. See the * OpenType specification for details. * - * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description + * https://learn.microsoft.com/typography/opentype/spec/glyf#composite-glyph-description * */ FT_EXPORT( FT_Error ) @@ -4619,7 +4623,7 @@ FT_BEGIN_HEADER * associated with a font. * * See - * https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/FontPolicies.pdf + * https://adobe-type-tools.github.io/font-tech-notes/pdfs/AcrobatDC_FontPolicies.pdf * for more details. * * @values: @@ -5173,8 +5177,8 @@ FT_BEGIN_HEADER * */ #define FREETYPE_MAJOR 2 -#define FREETYPE_MINOR 13 -#define FREETYPE_PATCH 3 +#define FREETYPE_MINOR 14 +#define FREETYPE_PATCH 1 /************************************************************************** diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h index 85b8ba2554b..62a856ccbd7 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftadvanc.h @@ -4,7 +4,7 @@ * * Quick computation of advance widths (specification only). * - * Copyright (C) 2008-2024 by + * Copyright (C) 2008-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h index 12bbfa63a62..348b4b3a268 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftbbox.h @@ -4,7 +4,7 @@ * * FreeType exact bbox computation (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h index 6f63b0b1e78..faad25689c9 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftbdf.h @@ -4,7 +4,7 @@ * * FreeType API for accessing BDF-specific strings (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h index df9d462652e..a22d43adf14 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftbitmap.h @@ -4,7 +4,7 @@ * * FreeType utility functions for bitmaps (specification). * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h index 96b2a90fc59..7cda8ff3f39 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftcid.h @@ -4,7 +4,7 @@ * * FreeType API for accessing CID font information (specification). * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * Dereg Clegg and Michael Toftdal. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h index 420720ddf22..129b1a23fb0 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftcolor.h @@ -4,7 +4,7 @@ * * FreeType's glyph color management (specification). * - * Copyright (C) 2018-2024 by + * Copyright (C) 2018-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -317,6 +317,15 @@ FT_BEGIN_HEADER * @description: * The functions described here allow access of colored glyph layer data * in OpenType's 'COLR' tables. + * + * Note that FreeType does *not* provide rendering in general of glyphs + * that use a 'COLR' table! While FreeType has very limited rendering + * support for 'COLR' v0 tables (without a possibility to change the + * color palette) via @FT_Render_Glyph, there is no such convenience + * code for 'COLR' v1 tables -- while it appears that v1 is simply an + * 'improved' version of v0, this is not the case: it is a completely + * different color font format, and you need a dedicated graphics + * library like Skia or Cairo to handle a v1 table's drawing commands. */ @@ -359,7 +368,7 @@ FT_BEGIN_HEADER * iteratively retrieve the colored glyph layers associated with the * current glyph slot. * - * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + * https://learn.microsoft.com/typography/opentype/spec/colr * * The glyph layer data for a given glyph index, if present, provides an * alternative, multi-color glyph representation: Instead of rendering @@ -1518,7 +1527,7 @@ FT_BEGIN_HEADER * * @return: * Value~1 if a clip box is found. If no clip box is found or an error - * occured, value~0 is returned. + * occurred, value~0 is returned. * * @note: * To retrieve the clip box in font units, reset scale to units-per-em @@ -1646,7 +1655,7 @@ FT_BEGIN_HEADER * * @return: * Value~1 if everything is OK. Value~0 if no details can be found for - * this paint or any other error occured. + * this paint or any other error occurred. * * @since: * 2.13 diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h index 1b7f539f5e2..b65a06ab69b 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftdriver.h @@ -4,7 +4,7 @@ * * FreeType API for controlling driver modules (specification only). * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -282,7 +282,7 @@ FT_BEGIN_HEADER * minimize hinting techniques that were problematic with the extra * resolution of ClearType; see * http://rastertragedy.com/RTRCh4.htm#Sec1 and - * https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx. + * https://learn.microsoft.com/typography/cleartype/truetypecleartype. * This technique is not to be confused with ClearType compatible widths. * ClearType backward compatibility has no direct impact on changing * advance widths, but there might be an indirect impact on disabling @@ -784,7 +784,7 @@ FT_BEGIN_HEADER * * Details on subpixel hinting and some of the necessary tweaks can be * found in Greg Hitchcock's whitepaper at - * 'https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx'. + * 'https://learn.microsoft.com/typography/cleartype/truetypecleartype'. * Note that FreeType currently doesn't really 'subpixel hint' (6x1, 6x2, * or 6x5 supersampling) like discussed in the paper. Depending on the * chosen interpreter, it simply ignores instructions on vertical stems diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h b/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h index 710ca91bbdd..3e591bede8d 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/fterrdef.h @@ -4,7 +4,7 @@ * * FreeType error codes (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h b/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h index 27c0ece5c1c..eca494f90c0 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/fterrors.h @@ -4,7 +4,7 @@ * * FreeType error code handling (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h index 7c8b0874a81..5df82447d0e 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftfntfmt.h @@ -4,7 +4,7 @@ * * Support functions for font formats. * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h index 30e5a9bf82b..77e5a7e7bfd 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftgasp.h @@ -4,7 +4,7 @@ * * Access of TrueType's 'gasp' table (specification). * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h index dc1eb8873ae..3691781cf52 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftglyph.h @@ -4,7 +4,7 @@ * * FreeType convenience functions to handle glyphs (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h index 9516dc030ac..e26c334c11a 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftgzip.h @@ -4,7 +4,7 @@ * * Gzip-compressed stream support. * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h index 2b4b4ac60ae..b0a0172ef4f 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftimage.h @@ -5,7 +5,7 @@ * FreeType glyph image formats and default raster interface * (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -267,6 +267,10 @@ FT_BEGIN_HEADER * *logical* one. For example, if @FT_Pixel_Mode is set to * `FT_PIXEL_MODE_LCD`, the logical width is a just a third of the * physical one. + * + * An empty bitmap with a NULL `buffer` is valid, with `rows` and/or + * `pitch` also set to 0. Such bitmaps might be produced while rendering + * empty or degenerate outlines. */ typedef struct FT_Bitmap_ { @@ -439,7 +443,7 @@ FT_BEGIN_HEADER * rasterizer; see the `tags` field in @FT_Outline. * * Please refer to the description of the 'SCANTYPE' instruction in the - * [OpenType specification](https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#scantype) + * [OpenType specification](https://learn.microsoft.com/typography/opentype/spec/tt_instructions#scantype) * how simple drop-outs, smart drop-outs, and stubs are defined. */ #define FT_OUTLINE_NONE 0x0 diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h index 816581b78eb..2233044754e 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftincrem.h @@ -4,7 +4,7 @@ * * FreeType incremental loading (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h index 25274dc4ac2..a0a8e9da929 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftlcdfil.h @@ -5,7 +5,7 @@ * FreeType API for color filtering of subpixel bitmap glyphs * (specification). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h index 972fbfa2fe4..14958b0ff37 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftlist.h @@ -4,7 +4,7 @@ * * Generic list support for FreeType (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h index 1813cfc2c27..d155171136c 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftlogging.h @@ -4,7 +4,7 @@ * * Additional debugging APIs. * - * Copyright (C) 2020-2024 by + * Copyright (C) 2020-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h index e4efde33dd8..c5ac49101a4 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmac.h @@ -4,7 +4,7 @@ * * Additional Mac-specific API. * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h index 35ed039c89b..ff0bbab59f9 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmm.h @@ -2,9 +2,9 @@ * * ftmm.h * - * FreeType Multiple Master font interface (specification). + * FreeType variation font interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -37,24 +37,79 @@ FT_BEGIN_HEADER * multiple_masters * * @title: - * Multiple Masters + * OpenType Font Variations, TrueType GX, and Adobe MM Fonts * * @abstract: - * How to manage Multiple Masters fonts. + * How to manage variable fonts with multiple design axes. * * @description: - * The following types and functions are used to manage Multiple Master - * fonts, i.e., the selection of specific design instances by setting - * design axis coordinates. + * The following types and functions manage OpenType Font Variations, + * Adobe Multiple Master (MM) fonts, and Apple TrueType GX fonts. These + * formats have in common that they allow the selection of specific + * design instances by setting design coordinates for one or more axes + * like font weight or width. * - * Besides Adobe MM fonts, the interface supports Apple's TrueType GX and - * OpenType variation fonts. Some of the routines only work with Adobe - * MM fonts, others will work with all three types. They are similar - * enough that a consistent interface makes sense. + * For historical reasons there are two interfaces. The first, older one + * can be used with Adobe MM fonts only, and the second, newer one is a + * unified interface that handles all three font formats. However, some + * differences remain and are documented accordingly; in particular, + * Adobe MM fonts don't have named instances (see below). * - * For Adobe MM fonts, macro @FT_IS_SFNT returns false. For GX and - * OpenType variation fonts, it returns true. + * For Adobe MM fonts, macro @FT_IS_SFNT returns false. For TrueType GX + * and OpenType Font Variations, it returns true. * + * We use mostly the terminology of the OpenType standard. Here are some + * important technical terms. + * + * * A 'named instance' is a tuple of design coordinates that has a + * string ID (i.e., an index into the font's 'name' table) associated + * with it. The font can tell the user that, for example, + * [Weight=700,Width=110] is 'Bold'. Another name for 'named instance' + * is 'named style'. + * + * Adobe MM fonts don't have named instances. + * + * * The 'default instance' of a variation font is that instance for + * which the nth axis coordinate is equal to the nth default axis + * coordinate (i.e., `axis[n].def` as specified in the @FT_MM_Var + * structure), with~n covering all axes. In TrueType GX and OpenType + * Font Variations, the default instance is explicitly given. In Adobe + * MM fonts, the `WeightVector` entry as found in the font file is + * taken as the default instance. + * + * For TrueType GX and OpenType Font Variations, FreeType synthesizes + * a named instance for the default instance if the font does not + * contain such an entry. + * + * * 'Design coordinates' are the axis values found in a variation font + * file. Their meaning is specified by the font designer and the + * values are rather arbitrary. + * + * For example, the 'weight' axis in design coordinates might vary + * between 100 (thin) and 900 (heavy) in font~A, while font~B + * contains values between 400 (normal) and 800 (extra bold). + * + * * 'Normalized coordinates' are design coordinates mapped to a standard + * range; they are also called 'blend coordinates'. + * + * For TrueType GX and OpenType Font Variations, the range is [-1;1], + * with the minimum mapped to value~-1, the default mapped to + * value~0, and the maximum mapped to value~1, and all other + * coordinates mapped to intervening points. Please look up the + * [OpenType + * specification](https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview) + * on how this mapping works in detail. + * + * For Adobe MM fonts, this standard range is [0;1], with the minimum + * mapped to value~0 and the maximum mapped to value~1, and all other + * coordinates mapped to intervening points. Please look up [Adobe + * TechNote + * #5015](https://adobe-type-tools.github.io/font-tech-notes/pdfs/5015.Type1_Supp.pdf) + * on how this mapping works in detail. + * + * Assuming that the two fonts in the previous example are OpenType + * Font Variations, both font~A's [100;900] and font~B's [400;800] + * coordinate ranges get mapped to [-1;1]. */ @@ -64,14 +119,14 @@ FT_BEGIN_HEADER * T1_MAX_MM_XXX * * @description: - * Multiple Masters limits as defined in their specifications. + * Adobe MM font limits as defined in their specifications. * * @values: * T1_MAX_MM_AXIS :: - * The maximum number of Multiple Masters axes. + * The maximum number of Adobe MM font axes. * * T1_MAX_MM_DESIGNS :: - * The maximum number of Multiple Masters designs. + * The maximum number of Adobe MM font designs. * * T1_MAX_MM_MAP_POINTS :: * The maximum number of elements in a design map. @@ -88,11 +143,10 @@ FT_BEGIN_HEADER * FT_MM_Axis * * @description: - * A structure to model a given axis in design space for Multiple Masters - * fonts. + * A structure to model a given axis in design space for Adobe MM fonts. * - * This structure can't be used for TrueType GX or OpenType variation - * fonts. + * This structure can't be used with TrueType GX or OpenType Font + * Variations. * * @fields: * name :: @@ -119,17 +173,17 @@ FT_BEGIN_HEADER * FT_Multi_Master * * @description: - * A structure to model the axes and space of a Multiple Masters font. + * A structure to model the axes and space of an Adobe MM font. * - * This structure can't be used for TrueType GX or OpenType variation - * fonts. + * This structure can't be used with TrueType GX or OpenType Font + * Variations. * * @fields: * num_axis :: * Number of axes. Cannot exceed~4. * * num_designs :: - * Number of designs; should be normally 2^num_axis even though the + * Number of designs; should be normally `2^num_axis` even though the * Type~1 specification strangely allows for intermediate designs to be * present. This number cannot exceed~16. * @@ -151,13 +205,13 @@ FT_BEGIN_HEADER * FT_Var_Axis * * @description: - * A structure to model a given axis in design space for Multiple - * Masters, TrueType GX, and OpenType variation fonts. + * A structure to model a given axis in design space for Adobe MM fonts, + * TrueType GX, and OpenType Font Variations. * * @fields: * name :: * The axis's name. Not always meaningful for TrueType GX or OpenType - * variation fonts. + * Font Variations. * * minimum :: * The axis's minimum design coordinate. @@ -171,17 +225,17 @@ FT_BEGIN_HEADER * * tag :: * The axis's tag (the equivalent to 'name' for TrueType GX and - * OpenType variation fonts). FreeType provides default values for + * OpenType Font Variations). FreeType provides default values for * Adobe MM fonts if possible. * * strid :: * The axis name entry in the font's 'name' table. This is another * (and often better) version of the 'name' field for TrueType GX or - * OpenType variation fonts. Not meaningful for Adobe MM fonts. + * OpenType Font Variations. Not meaningful for Adobe MM fonts. * * @note: * The fields `minimum`, `def`, and `maximum` are 16.16 fractional values - * for TrueType GX and OpenType variation fonts. For Adobe MM fonts, the + * for TrueType GX and OpenType Font Variations. For Adobe MM fonts, the * values are whole numbers (i.e., the fractional part is zero). */ typedef struct FT_Var_Axis_ @@ -205,7 +259,7 @@ FT_BEGIN_HEADER * * @description: * A structure to model a named instance in a TrueType GX or OpenType - * variation font. + * Font Variations. * * This structure can't be used for Adobe MM fonts. * @@ -215,11 +269,11 @@ FT_BEGIN_HEADER * entry for each axis. * * strid :: - * The entry in 'name' table identifying this instance. + * An index into the 'name' table identifying this instance. * * psid :: - * The entry in 'name' table identifying a PostScript name for this - * instance. Value 0xFFFF indicates a missing entry. + * An index into the 'name' table identifying a PostScript name for + * this instance. Value 0xFFFF indicates a missing entry. */ typedef struct FT_Var_Named_Style_ { @@ -236,39 +290,33 @@ FT_BEGIN_HEADER * FT_MM_Var * * @description: - * A structure to model the axes and space of an Adobe MM, TrueType GX, - * or OpenType variation font. + * A structure to model the axes and space of Adobe MM fonts, TrueType + * GX, or OpenType Font Variations. * * Some fields are specific to one format and not to the others. * * @fields: * num_axis :: * The number of axes. The maximum value is~4 for Adobe MM fonts; no - * limit in TrueType GX or OpenType variation fonts. + * limit in TrueType GX or OpenType Font Variations. * * num_designs :: - * The number of designs; should be normally 2^num_axis for Adobe MM - * fonts. Not meaningful for TrueType GX or OpenType variation fonts + * The number of designs; should be normally `2^num_axis` for Adobe MM + * fonts. Not meaningful for TrueType GX or OpenType Font Variations * (where every glyph could have a different number of designs). * * num_namedstyles :: - * The number of named styles; a 'named style' is a tuple of design - * coordinates that has a string ID (in the 'name' table) associated - * with it. The font can tell the user that, for example, - * [Weight=1.5,Width=1.1] is 'Bold'. Another name for 'named style' is - * 'named instance'. - * - * For Adobe Multiple Masters fonts, this value is always zero because - * the format does not support named styles. + * The number of named instances. For Adobe MM fonts, this value is + * always zero. * * axis :: - * An axis descriptor table. TrueType GX and OpenType variation fonts + * An axis descriptor table. TrueType GX and OpenType Font Variations * contain slightly more data than Adobe MM fonts. Memory management * of this pointer is done internally by FreeType. * * namedstyle :: - * A named style (instance) table. Only meaningful for TrueType GX and - * OpenType variation fonts. Memory management of this pointer is done + * An array of named instances. Only meaningful for TrueType GX and + * OpenType Font Variations. Memory management of this pointer is done * internally by FreeType. */ typedef struct FT_MM_Var_ @@ -290,8 +338,8 @@ FT_BEGIN_HEADER * @description: * Retrieve a variation descriptor of a given Adobe MM font. * - * This function can't be used with TrueType GX or OpenType variation - * fonts. + * This function can't be used with TrueType GX or OpenType Font + * Variations. * * @input: * face :: @@ -299,7 +347,7 @@ FT_BEGIN_HEADER * * @output: * amaster :: - * The Multiple Masters descriptor. + * The Adobe MM font's variation descriptor. * * @return: * FreeType error code. 0~means success. @@ -366,8 +414,8 @@ FT_BEGIN_HEADER * For Adobe MM fonts, choose an interpolated font design through design * coordinates. * - * This function can't be used with TrueType GX or OpenType variation - * fonts. + * This function can't be used with TrueType GX or OpenType Font + * Variations. * * @inout: * face :: @@ -391,8 +439,8 @@ FT_BEGIN_HEADER * * [Since 2.9] If `num_coords` is larger than zero, this function sets * the @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags` field - * (i.e., @FT_IS_VARIATION will return true). If `num_coords` is zero, - * this bit flag gets unset. + * (i.e., @FT_IS_VARIATION returns true). If `num_coords` is zero, this + * bit flag gets unset. */ FT_EXPORT( FT_Error ) FT_Set_MM_Design_Coordinates( FT_Face face, @@ -428,7 +476,7 @@ FT_BEGIN_HEADER * * @note: * The design coordinates are 16.16 fractional values for TrueType GX and - * OpenType variation fonts. For Adobe MM fonts, the values are supposed + * OpenType Font Variations. For Adobe MM fonts, the values are supposed * to be whole numbers (i.e., the fractional part is zero). * * [Since 2.8.1] To reset all axes to the default values, call the @@ -438,8 +486,14 @@ FT_BEGIN_HEADER * * [Since 2.9] If `num_coords` is larger than zero, this function sets * the @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags` field - * (i.e., @FT_IS_VARIATION will return true). If `num_coords` is zero, - * this bit flag gets unset. + * (i.e., @FT_IS_VARIATION returns true). If `num_coords` is zero, this + * bit flag gets unset. + * + * [Since 2.14] This function also sets the @FT_FACE_FLAG_VARIATION bit + * in @FT_Face's `face_flags` field (i.e., @FT_IS_VARIATION returns + * true) if any of the provided coordinates is different from the face's + * default value for the corresponding axis, that is, the set up face is + * not at its default position. */ FT_EXPORT( FT_Error ) FT_Set_Var_Design_Coordinates( FT_Face face, @@ -468,14 +522,14 @@ FT_BEGIN_HEADER * * @output: * coords :: - * The design coordinates array. + * The design coordinates array, which must be allocated by the user. * * @return: * FreeType error code. 0~means success. * * @note: * The design coordinates are 16.16 fractional values for TrueType GX and - * OpenType variation fonts. For Adobe MM fonts, the values are whole + * OpenType Font Variations. For Adobe MM fonts, the values are whole * numbers (i.e., the fractional part is zero). * * @since: @@ -493,8 +547,7 @@ FT_BEGIN_HEADER * FT_Set_MM_Blend_Coordinates * * @description: - * Choose an interpolated font design through normalized blend - * coordinates. + * Choose an interpolated font design through normalized coordinates. * * This function works with all supported variation formats. * @@ -509,9 +562,10 @@ FT_BEGIN_HEADER * the number of axes, use default values for the remaining axes. * * coords :: - * The design coordinates array. Each element is a 16.16 fractional - * value and must be between 0 and 1.0 for Adobe MM fonts, and between - * -1.0 and 1.0 for TrueType GX and OpenType variation fonts. + * The normalized coordinates array. Each element is a 16.16 + * fractional value and must be between 0 and 1.0 for Adobe MM fonts, + * and between -1.0 and 1.0 for TrueType GX and OpenType Font + * Variations. * * @return: * FreeType error code. 0~means success. @@ -524,8 +578,14 @@ FT_BEGIN_HEADER * * [Since 2.9] If `num_coords` is larger than zero, this function sets * the @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags` field - * (i.e., @FT_IS_VARIATION will return true). If `num_coords` is zero, - * this bit flag gets unset. + * (i.e., @FT_IS_VARIATION returns true). If `num_coords` is zero, this + * bit flag gets unset. + * + * [Since 2.14] This function also sets the @FT_FACE_FLAG_VARIATION bit + * in @FT_Face's `face_flags` field (i.e., @FT_IS_VARIATION returns + * true) if any of the provided coordinates is different from the face's + * default value for the corresponding axis, that is, the set up face is + * not at its default position. */ FT_EXPORT( FT_Error ) FT_Set_MM_Blend_Coordinates( FT_Face face, @@ -539,8 +599,8 @@ FT_BEGIN_HEADER * FT_Get_MM_Blend_Coordinates * * @description: - * Get the normalized blend coordinates of the currently selected - * interpolated font. + * Get the normalized coordinates of the currently selected interpolated + * font. * * This function works with all supported variation formats. * @@ -549,14 +609,14 @@ FT_BEGIN_HEADER * A handle to the source face. * * num_coords :: - * The number of normalized blend coordinates to retrieve. If it is - * larger than the number of axes, set the excess values to~0.5 for - * Adobe MM fonts, and to~0 for TrueType GX and OpenType variation - * fonts. + * The number of normalized coordinates to retrieve. If it is larger + * than the number of axes, set the excess values to~0.5 for Adobe MM + * fonts, and to~0 for TrueType GX and OpenType Font Variations. * * @output: * coords :: - * The normalized blend coordinates array (as 16.16 fractional values). + * The normalized coordinates array (as 16.16 fractional values), which + * must be allocated by the user. * * @return: * FreeType error code. 0~means success. @@ -610,8 +670,8 @@ FT_BEGIN_HEADER * For Adobe MM fonts, choose an interpolated font design by directly * setting the weight vector. * - * This function can't be used with TrueType GX or OpenType variation - * fonts. + * This function can't be used with TrueType GX or OpenType Font + * Variations. * * @inout: * face :: @@ -630,16 +690,16 @@ FT_BEGIN_HEADER * FreeType error code. 0~means success. * * @note: - * Adobe Multiple Master fonts limit the number of designs, and thus the - * length of the weight vector to 16~elements. + * Adobe MM fonts limit the number of designs, and thus the length of the + * weight vector, to 16~elements. * * If `len` is larger than zero, this function sets the * @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags` field (i.e., - * @FT_IS_VARIATION will return true). If `len` is zero, this bit flag - * is unset and the weight vector array is reset to the default values. + * @FT_IS_VARIATION returns true). If `len` is zero, this bit flag is + * unset and the weight vector array is reset to the default values. * * The Adobe documentation also states that the values in the - * WeightVector array must total 1.0 +/-~0.001. In practice this does + * `WeightVector` array must total 1.0 +/-~0.001. In practice this does * not seem to be enforced, so is not enforced here, either. * * @since: @@ -659,8 +719,8 @@ FT_BEGIN_HEADER * @description: * For Adobe MM fonts, retrieve the current weight vector of the font. * - * This function can't be used with TrueType GX or OpenType variation - * fonts. + * This function can't be used with TrueType GX or OpenType Font + * Variations. * * @inout: * face :: @@ -677,14 +737,14 @@ FT_BEGIN_HEADER * * @output: * weightvector :: - * An array to be filled. + * An array to be filled; it must be allocated by the user. * * @return: * FreeType error code. 0~means success. * * @note: - * Adobe Multiple Master fonts limit the number of designs, and thus the - * length of the WeightVector to~16. + * Adobe MM fonts limit the number of designs, and thus the length of the + * weight vector, to~16 elements. * * @since: * 2.10 @@ -760,8 +820,8 @@ FT_BEGIN_HEADER * A handle to the source face. * * instance_index :: - * The index of the requested instance, starting with value 1. If set - * to value 0, FreeType switches to font access without a named + * The index of the requested instance, starting with value~1. If set + * to value~0, FreeType switches to font access without a named * instance. * * @return: @@ -771,11 +831,11 @@ FT_BEGIN_HEADER * The function uses the value of `instance_index` to set bits 16-30 of * the face's `face_index` field. It also resets any variation applied * to the font, and the @FT_FACE_FLAG_VARIATION bit of the face's - * `face_flags` field gets reset to zero (i.e., @FT_IS_VARIATION will - * return false). + * `face_flags` field gets reset to zero (i.e., @FT_IS_VARIATION returns + * false). * - * For Adobe MM fonts (which don't have named instances) this function - * simply resets the current face to the default instance. + * For Adobe MM fonts, this function resets the current face to the + * default instance. * * @since: * 2.9 @@ -794,10 +854,6 @@ FT_BEGIN_HEADER * Retrieve the index of the default named instance, to be used with * @FT_Set_Named_Instance. * - * The default instance of a variation font is that instance for which - * the nth axis coordinate is equal to `axis[n].def` (as specified in the - * @FT_MM_Var structure), with~n covering all axes. - * * FreeType synthesizes a named instance for the default instance if the * font does not contain such an entry. * @@ -813,8 +869,8 @@ FT_BEGIN_HEADER * FreeType error code. 0~means success. * * @note: - * For Adobe MM fonts (which don't have named instances) this function - * always returns zero for `instance_index`. + * For Adobe MM fonts, this function always returns zero for + * `instance_index`. * * @since: * 2.13.1 diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h index 0ee715898f7..2669e4a03b3 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmodapi.h @@ -4,7 +4,7 @@ * * FreeType modules public interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h index 6722fbf8b70..8e2ef2f01f8 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftmoderr.h @@ -4,7 +4,7 @@ * * FreeType module error offsets (specification). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h index 44e94b4f5bb..2545ca8486b 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftoutln.h @@ -5,7 +5,7 @@ * Support for the FT_Outline type used to store glyph shapes of * most scalable font formats (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h index 43bf69c202f..2c09db1683e 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftparams.h @@ -4,7 +4,7 @@ * * FreeType API for possible FT_Parameter tags (specification only). * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h index dc5018a1b54..cc3102073b1 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftrender.h @@ -4,7 +4,7 @@ * * FreeType renderer modules public interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h index 4ef5c7955df..fdb89f24ccc 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsizes.h @@ -4,7 +4,7 @@ * * FreeType size objects management (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h index d5d5cd93103..99728574db6 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsnames.h @@ -7,7 +7,7 @@ * * This is _not_ used to retrieve glyph names! * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h index 41626dc9d7b..2c4761c768d 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftstroke.h @@ -4,7 +4,7 @@ * * FreeType path stroker (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h index 43081b6c330..93499a4b4f1 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsynth.h @@ -5,7 +5,7 @@ * FreeType synthesizing code for emboldening and slanting * (specification). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h b/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h index 1eacb3af398..1de9f8e603d 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ftsystem.h @@ -4,7 +4,7 @@ * * FreeType low-level system interface definition (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h b/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h index a5299e938d4..ed7bd06a78f 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/fttrigon.h @@ -4,7 +4,7 @@ * * FreeType trigonometric functions (specification). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h index 27815143a64..e207c5ebe09 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/fttypes.h @@ -4,7 +4,7 @@ * * FreeType simple types definitions (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h index 8865d53b389..987e704e9b0 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/autohint.h @@ -4,7 +4,7 @@ * * High-level 'autohint' module-specific interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h index 36b0390a5a5..26ee43bb9a9 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cffotypes.h @@ -4,7 +4,7 @@ * * Basic OpenType/CFF object type definitions (specification). * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h index ef2e8e7569c..62335db4834 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/cfftypes.h @@ -5,7 +5,7 @@ * Basic OpenType/CFF type definitions and interface (specification * only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h index 876f66e2561..e6d0166d888 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/compiler-macros.h @@ -4,7 +4,7 @@ * * Compiler-specific macro definitions used internally by FreeType. * - * Copyright (C) 2020-2024 by + * Copyright (C) 2020-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -128,8 +128,8 @@ FT_BEGIN_HEADER * before a function declaration. */ - /* Visual C, mingw */ -#if defined( _WIN32 ) + /* Visual C, MinGW, Cygwin */ +#if defined( _WIN32 ) || defined( __CYGWIN__ ) #define FT_INTERNAL_FUNCTION_ATTRIBUTE /* empty */ /* gcc, clang */ diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h index 71128a2df90..16a732224ef 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftcalc.h @@ -4,7 +4,7 @@ * * Arithmetic computations (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -27,17 +27,87 @@ FT_BEGIN_HEADER + /* + * The following macros have two purposes. + * + * - Tag places where overflow is expected and harmless. + * + * - Avoid run-time undefined behavior sanitizer errors. + * + * Use with care! + */ +#define ADD_INT( a, b ) \ + (FT_Int)( (FT_UInt)(a) + (FT_UInt)(b) ) +#define SUB_INT( a, b ) \ + (FT_Int)( (FT_UInt)(a) - (FT_UInt)(b) ) +#define MUL_INT( a, b ) \ + (FT_Int)( (FT_UInt)(a) * (FT_UInt)(b) ) +#define NEG_INT( a ) \ + (FT_Int)( (FT_UInt)0 - (FT_UInt)(a) ) + +#define ADD_LONG( a, b ) \ + (FT_Long)( (FT_ULong)(a) + (FT_ULong)(b) ) +#define SUB_LONG( a, b ) \ + (FT_Long)( (FT_ULong)(a) - (FT_ULong)(b) ) +#define MUL_LONG( a, b ) \ + (FT_Long)( (FT_ULong)(a) * (FT_ULong)(b) ) +#define NEG_LONG( a ) \ + (FT_Long)( (FT_ULong)0 - (FT_ULong)(a) ) + +#define ADD_INT32( a, b ) \ + (FT_Int32)( (FT_UInt32)(a) + (FT_UInt32)(b) ) +#define SUB_INT32( a, b ) \ + (FT_Int32)( (FT_UInt32)(a) - (FT_UInt32)(b) ) +#define MUL_INT32( a, b ) \ + (FT_Int32)( (FT_UInt32)(a) * (FT_UInt32)(b) ) +#define NEG_INT32( a ) \ + (FT_Int32)( (FT_UInt32)0 - (FT_UInt32)(a) ) + +#ifdef FT_INT64 + +#define ADD_INT64( a, b ) \ + (FT_Int64)( (FT_UInt64)(a) + (FT_UInt64)(b) ) +#define SUB_INT64( a, b ) \ + (FT_Int64)( (FT_UInt64)(a) - (FT_UInt64)(b) ) +#define MUL_INT64( a, b ) \ + (FT_Int64)( (FT_UInt64)(a) * (FT_UInt64)(b) ) +#define NEG_INT64( a ) \ + (FT_Int64)( (FT_UInt64)0 - (FT_UInt64)(a) ) + +#endif /* FT_INT64 */ + + /************************************************************************** * * FT_MulDiv() and FT_MulFix() are declared in freetype.h. * */ -#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER - /* Provide assembler fragments for performance-critical functions. */ - /* These must be defined `static __inline__' with GCC. */ +#ifdef FT_CONFIG_OPTION_INLINE_MULFIX -#if defined( __CC_ARM ) || defined( __ARMCC__ ) /* RVCT */ +#ifdef FT_INT64 + + static inline FT_Long + FT_MulFix_64( FT_Long a, + FT_Long b ) + { + FT_Int64 ab = MUL_INT64( a, b ); + + + ab = ADD_INT64( ab, 0x8000 + ( ab >> 63 ) ); /* rounding phase */ + + return (FT_Long)( ab >> 16 ); + } + + +#define FT_MulFix( a, b ) FT_MulFix_64( a, b ) + +#elif !defined( FT_CONFIG_OPTION_NO_ASSEMBLER ) + /* Provide 32-bit assembler fragments for optimized FT_MulFix. */ + /* These must be defined `static __inline__' or similar. */ + +#if defined( __arm__ ) && \ + ( defined( __thumb2__ ) || !defined( __thumb__ ) ) #define FT_MULFIX_ASSEMBLER FT_MulFix_arm @@ -49,6 +119,7 @@ FT_BEGIN_HEADER { FT_Int32 t, t2; +#if defined( __CC_ARM ) || defined( __ARMCC__ ) /* RVCT */ __asm { @@ -60,28 +131,8 @@ FT_BEGIN_HEADER mov a, t2, lsr #16 /* a = t2 >> 16 */ orr a, a, t, lsl #16 /* a |= t << 16 */ } - return a; - } - -#endif /* __CC_ARM || __ARMCC__ */ - - -#ifdef __GNUC__ - -#if defined( __arm__ ) && \ - ( !defined( __thumb__ ) || defined( __thumb2__ ) ) && \ - !( defined( __CC_ARM ) || defined( __ARMCC__ ) ) - -#define FT_MULFIX_ASSEMBLER FT_MulFix_arm - - /* documentation is in freetype.h */ - - static __inline__ FT_Int32 - FT_MulFix_arm( FT_Int32 a, - FT_Int32 b ) - { - FT_Int32 t, t2; +#elif defined( __GNUC__ ) __asm__ __volatile__ ( "smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */ @@ -98,26 +149,25 @@ FT_BEGIN_HEADER : "=r"(a), "=&r"(t2), "=&r"(t) : "r"(a), "r"(b) : "cc" ); + +#endif + return a; } -#endif /* __arm__ && */ - /* ( __thumb2__ || !__thumb__ ) && */ - /* !( __CC_ARM || __ARMCC__ ) */ - - -#if defined( __i386__ ) +#elif defined( __i386__ ) || defined( _M_IX86 ) #define FT_MULFIX_ASSEMBLER FT_MulFix_i386 /* documentation is in freetype.h */ - static __inline__ FT_Int32 + static __inline FT_Int32 FT_MulFix_i386( FT_Int32 a, FT_Int32 b ) { FT_Int32 result; +#if defined( __GNUC__ ) __asm__ __volatile__ ( "imul %%edx\n" @@ -132,27 +182,8 @@ FT_BEGIN_HEADER : "=a"(result), "=d"(b) : "a"(a), "d"(b) : "%ecx", "cc" ); - return result; - } -#endif /* i386 */ - -#endif /* __GNUC__ */ - - -#ifdef _MSC_VER /* Visual C++ */ - -#ifdef _M_IX86 - -#define FT_MULFIX_ASSEMBLER FT_MulFix_i386 - - /* documentation is in freetype.h */ - - static __inline FT_Int32 - FT_MulFix_i386( FT_Int32 a, - FT_Int32 b ) - { - FT_Int32 result; +#elif defined( _MSC_VER ) __asm { @@ -169,81 +200,21 @@ FT_BEGIN_HEADER add eax, edx mov result, eax } + +#endif + return result; } -#endif /* _M_IX86 */ +#endif /* __i386__ || _M_IX86 */ -#endif /* _MSC_VER */ - - -#if defined( __GNUC__ ) && defined( __x86_64__ ) - -#define FT_MULFIX_ASSEMBLER FT_MulFix_x86_64 - - static __inline__ FT_Int32 - FT_MulFix_x86_64( FT_Int32 a, - FT_Int32 b ) - { - /* Temporarily disable the warning that C90 doesn't support */ - /* `long long'. */ -#if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 ) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wlong-long" -#endif - -#if 1 - /* Technically not an assembly fragment, but GCC does a really good */ - /* job at inlining it and generating good machine code for it. */ - long long ret, tmp; - - - ret = (long long)a * b; - tmp = ret >> 63; - ret += 0x8000 + tmp; - - return (FT_Int32)( ret >> 16 ); -#else - - /* For some reason, GCC 4.6 on Ubuntu 12.04 generates invalid machine */ - /* code from the lines below. The main issue is that `wide_a' is not */ - /* properly initialized by sign-extending `a'. Instead, the generated */ - /* machine code assumes that the register that contains `a' on input */ - /* can be used directly as a 64-bit value, which is wrong most of the */ - /* time. */ - long long wide_a = (long long)a; - long long wide_b = (long long)b; - long long result; - - - __asm__ __volatile__ ( - "imul %2, %1\n" - "mov %1, %0\n" - "sar $63, %0\n" - "lea 0x8000(%1, %0), %0\n" - "sar $16, %0\n" - : "=&r"(result), "=&r"(wide_a) - : "r"(wide_b) - : "cc" ); - - return (FT_Int32)result; -#endif - -#if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 ) -#pragma GCC diagnostic pop -#endif - } - -#endif /* __GNUC__ && __x86_64__ */ - -#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ - - -#ifdef FT_CONFIG_OPTION_INLINE_MULFIX #ifdef FT_MULFIX_ASSEMBLER #define FT_MulFix( a, b ) FT_MULFIX_ASSEMBLER( (FT_Int32)(a), (FT_Int32)(b) ) #endif -#endif + +#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ + +#endif /* FT_CONFIG_OPTION_INLINE_MULFIX */ /************************************************************************** @@ -278,40 +249,6 @@ FT_BEGIN_HEADER FT_Long c ); - /************************************************************************** - * - * @function: - * FT_MulAddFix - * - * @description: - * Compute `(s[0] * f[0] + s[1] * f[1] + ...) / 0x10000`, where `s[n]` is - * usually a 16.16 scalar. - * - * @input: - * s :: - * The array of scalars. - * f :: - * The array of factors. - * count :: - * The number of entries in the array. - * - * @return: - * The result of `(s[0] * f[0] + s[1] * f[1] + ...) / 0x10000`. - * - * @note: - * This function is currently used for the scaled delta computation of - * variation stores. It internally uses 64-bit data types when - * available, otherwise it emulates 64-bit math by using 32-bit - * operations, which produce a correct result but most likely at a slower - * performance in comparison to the implementation base on `int64_t`. - * - */ - FT_BASE( FT_Int32 ) - FT_MulAddFix( FT_Fixed* s, - FT_Int32* f, - FT_UInt count ); - - /* * A variant of FT_Matrix_Multiply which scales its result afterwards. The * idea is that both `a' and `b' are scaled by factors of 10 so that the @@ -455,6 +392,10 @@ FT_BEGIN_HEADER #define FT_MSB( x ) FT_MSB_i386( x ) +#elif defined( __CC_ARM ) + +#define FT_MSB( x ) ( 31 - __clz( x ) ) + #elif defined( __SunOS_5_11 ) #include @@ -526,55 +467,6 @@ FT_BEGIN_HEADER #define ROUND_F26DOT6( x ) ( ( (x) + 32 - ( x < 0 ) ) & -64 ) - /* - * The following macros have two purposes. - * - * - Tag places where overflow is expected and harmless. - * - * - Avoid run-time sanitizer errors. - * - * Use with care! - */ -#define ADD_INT( a, b ) \ - (FT_Int)( (FT_UInt)(a) + (FT_UInt)(b) ) -#define SUB_INT( a, b ) \ - (FT_Int)( (FT_UInt)(a) - (FT_UInt)(b) ) -#define MUL_INT( a, b ) \ - (FT_Int)( (FT_UInt)(a) * (FT_UInt)(b) ) -#define NEG_INT( a ) \ - (FT_Int)( (FT_UInt)0 - (FT_UInt)(a) ) - -#define ADD_LONG( a, b ) \ - (FT_Long)( (FT_ULong)(a) + (FT_ULong)(b) ) -#define SUB_LONG( a, b ) \ - (FT_Long)( (FT_ULong)(a) - (FT_ULong)(b) ) -#define MUL_LONG( a, b ) \ - (FT_Long)( (FT_ULong)(a) * (FT_ULong)(b) ) -#define NEG_LONG( a ) \ - (FT_Long)( (FT_ULong)0 - (FT_ULong)(a) ) - -#define ADD_INT32( a, b ) \ - (FT_Int32)( (FT_UInt32)(a) + (FT_UInt32)(b) ) -#define SUB_INT32( a, b ) \ - (FT_Int32)( (FT_UInt32)(a) - (FT_UInt32)(b) ) -#define MUL_INT32( a, b ) \ - (FT_Int32)( (FT_UInt32)(a) * (FT_UInt32)(b) ) -#define NEG_INT32( a ) \ - (FT_Int32)( (FT_UInt32)0 - (FT_UInt32)(a) ) - -#ifdef FT_INT64 - -#define ADD_INT64( a, b ) \ - (FT_Int64)( (FT_UInt64)(a) + (FT_UInt64)(b) ) -#define SUB_INT64( a, b ) \ - (FT_Int64)( (FT_UInt64)(a) - (FT_UInt64)(b) ) -#define MUL_INT64( a, b ) \ - (FT_Int64)( (FT_UInt64)(a) * (FT_UInt64)(b) ) -#define NEG_INT64( a ) \ - (FT_Int64)( (FT_UInt64)0 - (FT_UInt64)(a) ) - -#endif /* FT_INT64 */ - FT_END_HEADER diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h index d7fa8dc93cf..d7facf40d12 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdebug.h @@ -4,7 +4,7 @@ * * Debugging and logging component (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h index 5609b3ef12b..24be4dad36b 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftdrv.h @@ -4,7 +4,7 @@ * * FreeType internal font driver interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h index f1c155b162c..8f2a54c015b 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftgloadr.h @@ -4,7 +4,7 @@ * * The FreeType glyph loader (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/fthash.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/fthash.h index 622ec76bb9a..642d21e21c6 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/fthash.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/fthash.h @@ -117,6 +117,18 @@ FT_BEGIN_HEADER FT_Hash hash, FT_Memory memory ); + FT_Error + ft_hash_str_insert_no_overwrite( const char* key, + size_t data, + FT_Hash hash, + FT_Memory memory ); + + FT_Error + ft_hash_num_insert_no_overwrite( FT_Int num, + size_t data, + FT_Hash hash, + FT_Memory memory ); + size_t* ft_hash_str_lookup( const char* key, FT_Hash hash ); @@ -125,6 +137,17 @@ FT_BEGIN_HEADER ft_hash_num_lookup( FT_Int num, FT_Hash hash ); + FT_Bool + ft_hash_num_iterator( FT_UInt *idx, + FT_Int *key, + size_t *value, + FT_Hash hash ); + + FT_Bool + ft_hash_str_iterator( FT_UInt *idx, + const char* *key, + size_t *value, + FT_Hash hash ); FT_END_HEADER diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h index 4e05a29f13a..c75c33f2895 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmemory.h @@ -4,7 +4,7 @@ * * The FreeType memory management macros (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h index 8449e7a010d..be3747bbf94 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftmmtypes.h @@ -5,7 +5,7 @@ * OpenType Variations type definitions for internal use * with the multi-masters service (specification). * - * Copyright (C) 2022-2024 by + * Copyright (C) 2022-2025 by * David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and * Dominik Röttsches. * diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h index a1e93298fdb..3db2fe28ffd 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftobjs.h @@ -4,7 +4,7 @@ * * The FreeType private base classes (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -275,6 +275,28 @@ FT_BEGIN_HEADER FT_GlyphSlot slot, FT_Render_Mode mode ); + + /************************************************************************** + * + * @Function: + * find_unicode_charmap + * + * @Description: + * This function finds a Unicode charmap, if there is one. And if there + * is more than one, it tries to favour the more extensive one, i.e., one + * that supports UCS-4 against those which are limited to the BMP (UCS-2 + * encoding.) + * + * If a unicode charmap is found, `face->charmap` is set to it. + * + * This function is called from `open_face`, from `FT_Select_Charmap(..., + * FT_ENCODING_UNICODE)`, and also from `afadjust.c` in the 'autofit' + * module. + */ + FT_BASE( FT_Error ) + find_unicode_charmap( FT_Face face ); + + #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap, @@ -498,9 +520,9 @@ FT_BEGIN_HEADER */ typedef struct FT_ModuleRec_ { - FT_Module_Class* clazz; - FT_Library library; - FT_Memory memory; + const FT_Module_Class* clazz; + FT_Library library; + FT_Memory memory; } FT_ModuleRec; diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h index 4f11aa16ba1..18a954d22f5 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftpsprop.h @@ -4,7 +4,7 @@ * * Get and set properties of PostScript drivers (specification). * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h index 05c1d6c48b5..e077f98bfb9 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftrfork.h @@ -4,7 +4,7 @@ * * Embedded resource forks accessor (specification). * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * Masatake YAMATO and Redhat K.K. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h index 8c35dbd7139..ce11bba19b2 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftserv.h @@ -4,7 +4,7 @@ * * The FreeType services (specification only). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h index fd52f767ef7..20c1dd7c4b0 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftstream.h @@ -4,7 +4,7 @@ * * Stream handling (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h index 42595a29ff3..3fd592800e2 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/fttrace.h @@ -4,7 +4,7 @@ * * Tracing handling (specification only). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -19,7 +19,7 @@ /* definitions of trace levels for FreeType 2 */ /* the maximum string length (if the argument to `FT_TRACE_DEF` */ - /* gets used as a string) plus one charachter for ':' plus */ + /* gets used as a string) plus one character for ':' plus */ /* another one for the trace level */ #define FT_MAX_TRACE_LEVEL_LENGTH (9 + 1 + 1) @@ -159,6 +159,7 @@ FT_TRACE_DEF( gxvprop ) FT_TRACE_DEF( gxvtrak ) /* autofit components */ +FT_TRACE_DEF( afadjust ) FT_TRACE_DEF( afcjk ) FT_TRACE_DEF( afglobal ) FT_TRACE_DEF( afhints ) diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h index a1312f2aba6..03a726c82cb 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/ftvalid.h @@ -4,7 +4,7 @@ * * FreeType validation support (specification). * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h index 745d2cb56b7..344be0f19a7 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/psaux.h @@ -5,7 +5,7 @@ * Auxiliary functions and data structures related to PostScript fonts * (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h index dba6c7303fd..96c5d84f058 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/pshints.h @@ -6,7 +6,7 @@ * recorders (specification only). These are used to support native * T1/T2 hints in the 'type1', 'cid', and 'cff' font drivers. * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h index 89e9c2e5de8..5bd51da23f4 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svbdf.h @@ -4,7 +4,7 @@ * * The FreeType BDF services (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h index 3cb483c344f..c97bf84fb2e 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcfftl.h @@ -4,7 +4,7 @@ * * The FreeType CFF tables loader service (specification). * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h index 8362cb8724d..748a8caf887 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svcid.h @@ -4,7 +4,7 @@ * * The FreeType CID font services (specification). * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * Derek Clegg and Michael Toftdal. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h index 6b837e79fcd..690fdc2a24f 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svfntfmt.h @@ -4,7 +4,7 @@ * * The FreeType font format service (specification only). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h index 6126ec9ada4..7128d6f3d7a 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgldict.h @@ -4,7 +4,7 @@ * * The FreeType glyph dictionary services (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h index 29cf5528189..1ca3e0a031b 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svgxval.h @@ -4,7 +4,7 @@ * * FreeType API for validating TrueTypeGX/AAT tables (specification). * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * Masatake YAMATO, Red Hat K.K., * David Turner, Robert Wilhelm, and Werner Lemberg. * diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h index ac1bc30c412..8a3d59bec6d 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svkern.h @@ -4,7 +4,7 @@ * * The FreeType Kerning service (specification). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h index 8b3563b25ca..4dde3a8151a 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmetric.h @@ -4,7 +4,7 @@ * * The FreeType services for metrics variations (specification). * - * Copyright (C) 2016-2024 by + * Copyright (C) 2016-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -77,7 +77,7 @@ FT_BEGIN_HEADER typedef void (*FT_Metrics_Adjust_Func)( FT_Face face ); - typedef FT_Error + typedef void (*FT_Size_Reset_Func)( FT_Size size ); diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h index 5288fadf375..9be133e2db0 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svmm.h @@ -4,7 +4,7 @@ * * The FreeType Multiple Masters and GX var services (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h index 7aea7ec11f0..933e5de98da 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svotval.h @@ -4,7 +4,7 @@ * * The FreeType OpenType validation service (specification). * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h index b2fac6d086b..c81b6a68a8b 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpfr.h @@ -4,7 +4,7 @@ * * Internal PFR service functions (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h index d19f3adc6d5..33864ebc344 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpostnm.h @@ -4,7 +4,7 @@ * * The FreeType PostScript name services (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h index ba39c0dd4da..0eb79c885d8 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svprop.h @@ -4,7 +4,7 @@ * * The FreeType property service (specification). * - * Copyright (C) 2012-2024 by + * Copyright (C) 2012-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h index d4908ee41aa..8f85d12157c 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpscmap.h @@ -4,7 +4,7 @@ * * The FreeType PostScript charmap service (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h index 2aadcdd02a1..83de04478df 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svpsinfo.h @@ -4,7 +4,7 @@ * * The FreeType PostScript info service (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h index 9e0f4ff202e..9bf5e3473c4 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svsfnt.h @@ -4,7 +4,7 @@ * * The FreeType SFNT table loading service (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h index 250886bcc5d..fc9b0aeb8e3 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttcmap.h @@ -4,7 +4,7 @@ * * The FreeType TrueType/sfnt cmap extra information service. * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * Masatake YAMATO, Redhat K.K., * David Turner, Robert Wilhelm, and Werner Lemberg. * diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h index 14967529a9a..979e9ea102e 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svtteng.h @@ -4,7 +4,7 @@ * * The FreeType TrueType engine query service (specification). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h index f190b3985d0..e4f54c10037 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svttglyf.h @@ -4,7 +4,7 @@ * * The FreeType TrueType glyph service. * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * David Turner. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h index 49f3fb7f775..ff887ffdc03 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/services/svwinfnt.h @@ -4,7 +4,7 @@ * * The FreeType Windows FNT/FONT service (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h index 35e4e73af02..adba2178877 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/sfnt.h @@ -4,7 +4,7 @@ * * High-level 'sfnt' driver interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -612,7 +612,7 @@ FT_BEGIN_HEADER * * @return: * Value~1 if a ClipBox is found. If no clip box is found or an - * error occured, value~0 is returned. + * error occurred, value~0 is returned. */ typedef FT_Bool ( *TT_Get_Color_Glyph_ClipBox_Func )( TT_Face face, @@ -707,7 +707,7 @@ FT_BEGIN_HEADER * * @return: * Value~1 if everything is OK. Value~0 if no details can be found for - * this paint or any other error occured. + * this paint or any other error occurred. */ typedef FT_Bool ( *TT_Get_Paint_Func )( TT_Face face, @@ -808,7 +808,7 @@ FT_BEGIN_HEADER * corresponding (1,0) Apple entry. * * @return: - * 1 if there is either a win or apple entry (or both), 0 otheriwse. + * 1 if there is either a win or apple entry (or both), 0 otherwise. */ typedef FT_Bool (*TT_Get_Name_ID_Func)( TT_Face face, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h index 68c99efb10a..20c73b2fbd2 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/svginterface.h @@ -4,7 +4,7 @@ * * Interface of ot-svg module (specification only). * - * Copyright (C) 2022-2024 by + * Copyright (C) 2022-2025 by * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h index 1821ae5cc83..5b26e4620d0 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/t1types.h @@ -5,7 +5,7 @@ * Basic Type1/Type2 type definitions and interface (specification * only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h index 7053e656a7e..d0e5eee89bc 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/tttypes.h @@ -5,7 +5,7 @@ * Basic SFNT/TrueType type definitions and interface (specification * only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -930,8 +930,8 @@ FT_BEGIN_HEADER * resolution and scaling independent parts of a TrueType font resource. * * @note: - * The TT_Face structure is also used as a 'parent class' for the - * OpenType-CFF class (T2_Face). + * The TT_Face structure is also used for CFF support; see file + * `cffotypes.h`. */ typedef struct TT_FaceRec_* TT_Face; @@ -1276,10 +1276,6 @@ FT_BEGIN_HEADER * * If varied by the `CVAR' table, non-integer values are possible. * - * interpreter :: - * A pointer to the TrueType bytecode interpreters field is also used - * to hook the debugger in 'ttdebug'. - * * extra :: * Reserved for third-party font drivers. * @@ -1521,10 +1517,6 @@ FT_BEGIN_HEADER FT_ULong cvt_size; FT_Int32* cvt; - /* A pointer to the bytecode interpreter to use. This is also */ - /* used to hook the debugger for the `ttdebug' utility. */ - TT_Interpreter interpreter; - /************************************************************************ * @@ -1582,11 +1574,6 @@ FT_BEGIN_HEADER FT_UInt32 kern_avail_bits; FT_UInt32 kern_order_bits; -#ifdef TT_CONFIG_OPTION_GPOS_KERNING - FT_Byte* gpos_table; - FT_Bool gpos_kerning_available; -#endif - #ifdef TT_CONFIG_OPTION_BDF TT_BDFRec bdf; #endif /* TT_CONFIG_OPTION_BDF */ @@ -1608,6 +1595,15 @@ FT_BEGIN_HEADER /* since 2.12 */ void* svg; +#ifdef TT_CONFIG_OPTION_GPOS_KERNING + /* since 2.13.3 */ + FT_Byte* gpos_table; + /* since 2.14 */ + /* This is actually an array of GPOS lookup subtables. */ + FT_UInt32* gpos_lookups_kerning; + FT_UInt num_gpos_lookups_kerning; +#endif + } TT_FaceRec; @@ -1621,15 +1617,6 @@ FT_BEGIN_HEADER * coordinates. * * @fields: - * memory :: - * A handle to the memory manager. - * - * max_points :: - * The maximum size in points of the zone. - * - * max_contours :: - * Max size in links contours of the zone. - * * n_points :: * The current number of points in the zone. * @@ -1653,9 +1640,6 @@ FT_BEGIN_HEADER */ typedef struct TT_GlyphZoneRec_ { - FT_Memory memory; - FT_UShort max_points; - FT_UShort max_contours; FT_UShort n_points; /* number of points in zone */ FT_UShort n_contours; /* number of contours */ @@ -1714,7 +1698,6 @@ FT_BEGIN_HEADER TT_GlyphZoneRec zone; TT_ExecContext exec; - FT_Byte* instructions; FT_ULong ins_pos; /* for possible extensibility in other formats */ diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h b/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h index 4a169d12f57..7d5b7df0fa1 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/internal/wofftypes.h @@ -5,7 +5,7 @@ * Basic WOFF/WOFF2 type definitions and interface (specification * only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h b/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h index 9d356938cc7..326bbcd0153 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/otsvg.h @@ -4,7 +4,7 @@ * * Interface for OT-SVG support related things (specification). * - * Copyright (C) 2022-2024 by + * Copyright (C) 2022-2025 by * David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h b/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h index fbd558aa34d..fc3c1706de5 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/t1tables.h @@ -5,7 +5,7 @@ * Basic Type 1/Type 2 tables definitions and interface (specification * only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h b/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h index d5d470e380f..3ef61091cc9 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/ttnameid.h @@ -4,7 +4,7 @@ * * TrueType name ID definitions (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -436,7 +436,7 @@ FT_BEGIN_HEADER * * The canonical source for Microsoft's IDs is * - * https://docs.microsoft.com/en-us/windows/desktop/Intl/language-identifier-constants-and-strings , + * https://learn.microsoft.com/windows/win32/intl/language-identifier-constants-and-strings , * * however, we only provide macros for language identifiers present in * the OpenType specification: Microsoft has abandoned the concept of @@ -847,113 +847,113 @@ FT_BEGIN_HEADER /* --------------- */ /* Bit 0 Basic Latin */ -#define TT_UCR_BASIC_LATIN (1L << 0) /* U+0020-U+007E */ +#define TT_UCR_BASIC_LATIN (1UL << 0) /* U+0020-U+007E */ /* Bit 1 C1 Controls and Latin-1 Supplement */ -#define TT_UCR_LATIN1_SUPPLEMENT (1L << 1) /* U+0080-U+00FF */ +#define TT_UCR_LATIN1_SUPPLEMENT (1UL << 1) /* U+0080-U+00FF */ /* Bit 2 Latin Extended-A */ -#define TT_UCR_LATIN_EXTENDED_A (1L << 2) /* U+0100-U+017F */ +#define TT_UCR_LATIN_EXTENDED_A (1UL << 2) /* U+0100-U+017F */ /* Bit 3 Latin Extended-B */ -#define TT_UCR_LATIN_EXTENDED_B (1L << 3) /* U+0180-U+024F */ +#define TT_UCR_LATIN_EXTENDED_B (1UL << 3) /* U+0180-U+024F */ /* Bit 4 IPA Extensions */ /* Phonetic Extensions */ /* Phonetic Extensions Supplement */ -#define TT_UCR_IPA_EXTENSIONS (1L << 4) /* U+0250-U+02AF */ +#define TT_UCR_IPA_EXTENSIONS (1UL << 4) /* U+0250-U+02AF */ /* U+1D00-U+1D7F */ /* U+1D80-U+1DBF */ /* Bit 5 Spacing Modifier Letters */ /* Modifier Tone Letters */ -#define TT_UCR_SPACING_MODIFIER (1L << 5) /* U+02B0-U+02FF */ +#define TT_UCR_SPACING_MODIFIER (1UL << 5) /* U+02B0-U+02FF */ /* U+A700-U+A71F */ /* Bit 6 Combining Diacritical Marks */ /* Combining Diacritical Marks Supplement */ -#define TT_UCR_COMBINING_DIACRITICAL_MARKS (1L << 6) /* U+0300-U+036F */ +#define TT_UCR_COMBINING_DIACRITICAL_MARKS (1UL << 6) /* U+0300-U+036F */ /* U+1DC0-U+1DFF */ /* Bit 7 Greek and Coptic */ -#define TT_UCR_GREEK (1L << 7) /* U+0370-U+03FF */ +#define TT_UCR_GREEK (1UL << 7) /* U+0370-U+03FF */ /* Bit 8 Coptic */ -#define TT_UCR_COPTIC (1L << 8) /* U+2C80-U+2CFF */ +#define TT_UCR_COPTIC (1UL << 8) /* U+2C80-U+2CFF */ /* Bit 9 Cyrillic */ /* Cyrillic Supplement */ /* Cyrillic Extended-A */ /* Cyrillic Extended-B */ -#define TT_UCR_CYRILLIC (1L << 9) /* U+0400-U+04FF */ +#define TT_UCR_CYRILLIC (1UL << 9) /* U+0400-U+04FF */ /* U+0500-U+052F */ /* U+2DE0-U+2DFF */ /* U+A640-U+A69F */ /* Bit 10 Armenian */ -#define TT_UCR_ARMENIAN (1L << 10) /* U+0530-U+058F */ +#define TT_UCR_ARMENIAN (1UL << 10) /* U+0530-U+058F */ /* Bit 11 Hebrew */ -#define TT_UCR_HEBREW (1L << 11) /* U+0590-U+05FF */ +#define TT_UCR_HEBREW (1UL << 11) /* U+0590-U+05FF */ /* Bit 12 Vai */ -#define TT_UCR_VAI (1L << 12) /* U+A500-U+A63F */ +#define TT_UCR_VAI (1UL << 12) /* U+A500-U+A63F */ /* Bit 13 Arabic */ /* Arabic Supplement */ -#define TT_UCR_ARABIC (1L << 13) /* U+0600-U+06FF */ +#define TT_UCR_ARABIC (1UL << 13) /* U+0600-U+06FF */ /* U+0750-U+077F */ /* Bit 14 NKo */ -#define TT_UCR_NKO (1L << 14) /* U+07C0-U+07FF */ +#define TT_UCR_NKO (1UL << 14) /* U+07C0-U+07FF */ /* Bit 15 Devanagari */ -#define TT_UCR_DEVANAGARI (1L << 15) /* U+0900-U+097F */ - /* Bit 16 Bengali */ -#define TT_UCR_BENGALI (1L << 16) /* U+0980-U+09FF */ +#define TT_UCR_DEVANAGARI (1UL << 15) /* U+0900-U+097F */ + /* Bit 16 Bangla (Bengali) */ +#define TT_UCR_BENGALI (1UL << 16) /* U+0980-U+09FF */ /* Bit 17 Gurmukhi */ -#define TT_UCR_GURMUKHI (1L << 17) /* U+0A00-U+0A7F */ +#define TT_UCR_GURMUKHI (1UL << 17) /* U+0A00-U+0A7F */ /* Bit 18 Gujarati */ -#define TT_UCR_GUJARATI (1L << 18) /* U+0A80-U+0AFF */ - /* Bit 19 Oriya */ -#define TT_UCR_ORIYA (1L << 19) /* U+0B00-U+0B7F */ +#define TT_UCR_GUJARATI (1UL << 18) /* U+0A80-U+0AFF */ + /* Bit 19 Oriya (Odia) */ +#define TT_UCR_ORIYA (1UL << 19) /* U+0B00-U+0B7F */ /* Bit 20 Tamil */ -#define TT_UCR_TAMIL (1L << 20) /* U+0B80-U+0BFF */ +#define TT_UCR_TAMIL (1UL << 20) /* U+0B80-U+0BFF */ /* Bit 21 Telugu */ -#define TT_UCR_TELUGU (1L << 21) /* U+0C00-U+0C7F */ +#define TT_UCR_TELUGU (1UL << 21) /* U+0C00-U+0C7F */ /* Bit 22 Kannada */ -#define TT_UCR_KANNADA (1L << 22) /* U+0C80-U+0CFF */ +#define TT_UCR_KANNADA (1UL << 22) /* U+0C80-U+0CFF */ /* Bit 23 Malayalam */ -#define TT_UCR_MALAYALAM (1L << 23) /* U+0D00-U+0D7F */ +#define TT_UCR_MALAYALAM (1UL << 23) /* U+0D00-U+0D7F */ /* Bit 24 Thai */ -#define TT_UCR_THAI (1L << 24) /* U+0E00-U+0E7F */ +#define TT_UCR_THAI (1UL << 24) /* U+0E00-U+0E7F */ /* Bit 25 Lao */ -#define TT_UCR_LAO (1L << 25) /* U+0E80-U+0EFF */ +#define TT_UCR_LAO (1UL << 25) /* U+0E80-U+0EFF */ /* Bit 26 Georgian */ /* Georgian Supplement */ -#define TT_UCR_GEORGIAN (1L << 26) /* U+10A0-U+10FF */ +#define TT_UCR_GEORGIAN (1UL << 26) /* U+10A0-U+10FF */ /* U+2D00-U+2D2F */ /* Bit 27 Balinese */ -#define TT_UCR_BALINESE (1L << 27) /* U+1B00-U+1B7F */ +#define TT_UCR_BALINESE (1UL << 27) /* U+1B00-U+1B7F */ /* Bit 28 Hangul Jamo */ -#define TT_UCR_HANGUL_JAMO (1L << 28) /* U+1100-U+11FF */ +#define TT_UCR_HANGUL_JAMO (1UL << 28) /* U+1100-U+11FF */ /* Bit 29 Latin Extended Additional */ /* Latin Extended-C */ /* Latin Extended-D */ -#define TT_UCR_LATIN_EXTENDED_ADDITIONAL (1L << 29) /* U+1E00-U+1EFF */ +#define TT_UCR_LATIN_EXTENDED_ADDITIONAL (1UL << 29) /* U+1E00-U+1EFF */ /* U+2C60-U+2C7F */ /* U+A720-U+A7FF */ /* Bit 30 Greek Extended */ -#define TT_UCR_GREEK_EXTENDED (1L << 30) /* U+1F00-U+1FFF */ +#define TT_UCR_GREEK_EXTENDED (1UL << 30) /* U+1F00-U+1FFF */ /* Bit 31 General Punctuation */ /* Supplemental Punctuation */ -#define TT_UCR_GENERAL_PUNCTUATION (1L << 31) /* U+2000-U+206F */ +#define TT_UCR_GENERAL_PUNCTUATION (1UL << 31) /* U+2000-U+206F */ /* U+2E00-U+2E7F */ /* ulUnicodeRange2 */ /* --------------- */ /* Bit 32 Superscripts And Subscripts */ -#define TT_UCR_SUPERSCRIPTS_SUBSCRIPTS (1L << 0) /* U+2070-U+209F */ +#define TT_UCR_SUPERSCRIPTS_SUBSCRIPTS (1UL << 0) /* U+2070-U+209F */ /* Bit 33 Currency Symbols */ -#define TT_UCR_CURRENCY_SYMBOLS (1L << 1) /* U+20A0-U+20CF */ +#define TT_UCR_CURRENCY_SYMBOLS (1UL << 1) /* U+20A0-U+20CF */ /* Bit 34 Combining Diacritical Marks For Symbols */ #define TT_UCR_COMBINING_DIACRITICAL_MARKS_SYMB \ - (1L << 2) /* U+20D0-U+20FF */ + (1UL << 2) /* U+20D0-U+20FF */ /* Bit 35 Letterlike Symbols */ -#define TT_UCR_LETTERLIKE_SYMBOLS (1L << 3) /* U+2100-U+214F */ +#define TT_UCR_LETTERLIKE_SYMBOLS (1UL << 3) /* U+2100-U+214F */ /* Bit 36 Number Forms */ -#define TT_UCR_NUMBER_FORMS (1L << 4) /* U+2150-U+218F */ +#define TT_UCR_NUMBER_FORMS (1UL << 4) /* U+2150-U+218F */ /* Bit 37 Arrows */ /* Supplemental Arrows-A */ /* Supplemental Arrows-B */ /* Miscellaneous Symbols and Arrows */ -#define TT_UCR_ARROWS (1L << 5) /* U+2190-U+21FF */ +#define TT_UCR_ARROWS (1UL << 5) /* U+2190-U+21FF */ /* U+27F0-U+27FF */ /* U+2900-U+297F */ /* U+2B00-U+2BFF */ @@ -961,52 +961,52 @@ FT_BEGIN_HEADER /* Supplemental Mathematical Operators */ /* Miscellaneous Mathematical Symbols-A */ /* Miscellaneous Mathematical Symbols-B */ -#define TT_UCR_MATHEMATICAL_OPERATORS (1L << 6) /* U+2200-U+22FF */ +#define TT_UCR_MATHEMATICAL_OPERATORS (1UL << 6) /* U+2200-U+22FF */ /* U+2A00-U+2AFF */ /* U+27C0-U+27EF */ /* U+2980-U+29FF */ /* Bit 39 Miscellaneous Technical */ -#define TT_UCR_MISCELLANEOUS_TECHNICAL (1L << 7) /* U+2300-U+23FF */ +#define TT_UCR_MISCELLANEOUS_TECHNICAL (1UL << 7) /* U+2300-U+23FF */ /* Bit 40 Control Pictures */ -#define TT_UCR_CONTROL_PICTURES (1L << 8) /* U+2400-U+243F */ +#define TT_UCR_CONTROL_PICTURES (1UL << 8) /* U+2400-U+243F */ /* Bit 41 Optical Character Recognition */ -#define TT_UCR_OCR (1L << 9) /* U+2440-U+245F */ +#define TT_UCR_OCR (1UL << 9) /* U+2440-U+245F */ /* Bit 42 Enclosed Alphanumerics */ -#define TT_UCR_ENCLOSED_ALPHANUMERICS (1L << 10) /* U+2460-U+24FF */ +#define TT_UCR_ENCLOSED_ALPHANUMERICS (1UL << 10) /* U+2460-U+24FF */ /* Bit 43 Box Drawing */ -#define TT_UCR_BOX_DRAWING (1L << 11) /* U+2500-U+257F */ +#define TT_UCR_BOX_DRAWING (1UL << 11) /* U+2500-U+257F */ /* Bit 44 Block Elements */ -#define TT_UCR_BLOCK_ELEMENTS (1L << 12) /* U+2580-U+259F */ +#define TT_UCR_BLOCK_ELEMENTS (1UL << 12) /* U+2580-U+259F */ /* Bit 45 Geometric Shapes */ -#define TT_UCR_GEOMETRIC_SHAPES (1L << 13) /* U+25A0-U+25FF */ +#define TT_UCR_GEOMETRIC_SHAPES (1UL << 13) /* U+25A0-U+25FF */ /* Bit 46 Miscellaneous Symbols */ -#define TT_UCR_MISCELLANEOUS_SYMBOLS (1L << 14) /* U+2600-U+26FF */ +#define TT_UCR_MISCELLANEOUS_SYMBOLS (1UL << 14) /* U+2600-U+26FF */ /* Bit 47 Dingbats */ -#define TT_UCR_DINGBATS (1L << 15) /* U+2700-U+27BF */ +#define TT_UCR_DINGBATS (1UL << 15) /* U+2700-U+27BF */ /* Bit 48 CJK Symbols and Punctuation */ -#define TT_UCR_CJK_SYMBOLS (1L << 16) /* U+3000-U+303F */ +#define TT_UCR_CJK_SYMBOLS (1UL << 16) /* U+3000-U+303F */ /* Bit 49 Hiragana */ -#define TT_UCR_HIRAGANA (1L << 17) /* U+3040-U+309F */ +#define TT_UCR_HIRAGANA (1UL << 17) /* U+3040-U+309F */ /* Bit 50 Katakana */ /* Katakana Phonetic Extensions */ -#define TT_UCR_KATAKANA (1L << 18) /* U+30A0-U+30FF */ +#define TT_UCR_KATAKANA (1UL << 18) /* U+30A0-U+30FF */ /* U+31F0-U+31FF */ /* Bit 51 Bopomofo */ /* Bopomofo Extended */ -#define TT_UCR_BOPOMOFO (1L << 19) /* U+3100-U+312F */ +#define TT_UCR_BOPOMOFO (1UL << 19) /* U+3100-U+312F */ /* U+31A0-U+31BF */ /* Bit 52 Hangul Compatibility Jamo */ -#define TT_UCR_HANGUL_COMPATIBILITY_JAMO (1L << 20) /* U+3130-U+318F */ +#define TT_UCR_HANGUL_COMPATIBILITY_JAMO (1UL << 20) /* U+3130-U+318F */ /* Bit 53 Phags-Pa */ -#define TT_UCR_CJK_MISC (1L << 21) /* U+A840-U+A87F */ -#define TT_UCR_KANBUN TT_UCR_CJK_MISC /* deprecated */ -#define TT_UCR_PHAGSPA +#define TT_UCR_PHAGSPA (1UL << 21) /* U+A840-U+A87F */ +#define TT_UCR_KANBUN TT_UCR_PHAGSPA /* deprecated */ +#define TT_UCR_CJK_MISC TT_UCR_PHAGSPA /* deprecated */ /* Bit 54 Enclosed CJK Letters and Months */ -#define TT_UCR_ENCLOSED_CJK_LETTERS_MONTHS (1L << 22) /* U+3200-U+32FF */ +#define TT_UCR_ENCLOSED_CJK_LETTERS_MONTHS (1UL << 22) /* U+3200-U+32FF */ /* Bit 55 CJK Compatibility */ -#define TT_UCR_CJK_COMPATIBILITY (1L << 23) /* U+3300-U+33FF */ +#define TT_UCR_CJK_COMPATIBILITY (1UL << 23) /* U+3300-U+33FF */ /* Bit 56 Hangul Syllables */ -#define TT_UCR_HANGUL (1L << 24) /* U+AC00-U+D7A3 */ +#define TT_UCR_HANGUL (1UL << 24) /* U+AC00-U+D7A3 */ /* Bit 57 High Surrogates */ /* High Private Use Surrogates */ /* Low Surrogates */ @@ -1017,12 +1017,12 @@ FT_BEGIN_HEADER /* Basic Multilingual Plane that is */ /* supported by this font. So it really */ /* means >= U+10000. */ -#define TT_UCR_SURROGATES (1L << 25) /* U+D800-U+DB7F */ +#define TT_UCR_SURROGATES (1UL << 25) /* U+D800-U+DB7F */ /* U+DB80-U+DBFF */ /* U+DC00-U+DFFF */ #define TT_UCR_NON_PLANE_0 TT_UCR_SURROGATES /* Bit 58 Phoenician */ -#define TT_UCR_PHOENICIAN (1L << 26) /*U+10900-U+1091F*/ +#define TT_UCR_PHOENICIAN (1UL << 26) /*U+10900-U+1091F*/ /* Bit 59 CJK Unified Ideographs */ /* CJK Radicals Supplement */ /* Kangxi Radicals */ @@ -1030,7 +1030,7 @@ FT_BEGIN_HEADER /* CJK Unified Ideographs Extension A */ /* CJK Unified Ideographs Extension B */ /* Kanbun */ -#define TT_UCR_CJK_UNIFIED_IDEOGRAPHS (1L << 27) /* U+4E00-U+9FFF */ +#define TT_UCR_CJK_UNIFIED_IDEOGRAPHS (1UL << 27) /* U+4E00-U+9FFF */ /* U+2E80-U+2EFF */ /* U+2F00-U+2FDF */ /* U+2FF0-U+2FFF */ @@ -1038,178 +1038,178 @@ FT_BEGIN_HEADER /*U+20000-U+2A6DF*/ /* U+3190-U+319F */ /* Bit 60 Private Use */ -#define TT_UCR_PRIVATE_USE (1L << 28) /* U+E000-U+F8FF */ +#define TT_UCR_PRIVATE_USE (1UL << 28) /* U+E000-U+F8FF */ /* Bit 61 CJK Strokes */ /* CJK Compatibility Ideographs */ /* CJK Compatibility Ideographs Supplement */ -#define TT_UCR_CJK_COMPATIBILITY_IDEOGRAPHS (1L << 29) /* U+31C0-U+31EF */ +#define TT_UCR_CJK_COMPATIBILITY_IDEOGRAPHS (1UL << 29) /* U+31C0-U+31EF */ /* U+F900-U+FAFF */ /*U+2F800-U+2FA1F*/ /* Bit 62 Alphabetic Presentation Forms */ -#define TT_UCR_ALPHABETIC_PRESENTATION_FORMS (1L << 30) /* U+FB00-U+FB4F */ +#define TT_UCR_ALPHABETIC_PRESENTATION_FORMS (1UL << 30) /* U+FB00-U+FB4F */ /* Bit 63 Arabic Presentation Forms-A */ -#define TT_UCR_ARABIC_PRESENTATION_FORMS_A (1L << 31) /* U+FB50-U+FDFF */ +#define TT_UCR_ARABIC_PRESENTATION_FORMS_A (1UL << 31) /* U+FB50-U+FDFF */ /* ulUnicodeRange3 */ /* --------------- */ /* Bit 64 Combining Half Marks */ -#define TT_UCR_COMBINING_HALF_MARKS (1L << 0) /* U+FE20-U+FE2F */ +#define TT_UCR_COMBINING_HALF_MARKS (1UL << 0) /* U+FE20-U+FE2F */ /* Bit 65 Vertical forms */ /* CJK Compatibility Forms */ -#define TT_UCR_CJK_COMPATIBILITY_FORMS (1L << 1) /* U+FE10-U+FE1F */ +#define TT_UCR_CJK_COMPATIBILITY_FORMS (1UL << 1) /* U+FE10-U+FE1F */ /* U+FE30-U+FE4F */ /* Bit 66 Small Form Variants */ -#define TT_UCR_SMALL_FORM_VARIANTS (1L << 2) /* U+FE50-U+FE6F */ +#define TT_UCR_SMALL_FORM_VARIANTS (1UL << 2) /* U+FE50-U+FE6F */ /* Bit 67 Arabic Presentation Forms-B */ -#define TT_UCR_ARABIC_PRESENTATION_FORMS_B (1L << 3) /* U+FE70-U+FEFE */ +#define TT_UCR_ARABIC_PRESENTATION_FORMS_B (1UL << 3) /* U+FE70-U+FEFF */ /* Bit 68 Halfwidth and Fullwidth Forms */ -#define TT_UCR_HALFWIDTH_FULLWIDTH_FORMS (1L << 4) /* U+FF00-U+FFEF */ +#define TT_UCR_HALFWIDTH_FULLWIDTH_FORMS (1UL << 4) /* U+FF00-U+FFEF */ /* Bit 69 Specials */ -#define TT_UCR_SPECIALS (1L << 5) /* U+FFF0-U+FFFD */ +#define TT_UCR_SPECIALS (1UL << 5) /* U+FFF0-U+FFFF */ /* Bit 70 Tibetan */ -#define TT_UCR_TIBETAN (1L << 6) /* U+0F00-U+0FFF */ +#define TT_UCR_TIBETAN (1UL << 6) /* U+0F00-U+0FFF */ /* Bit 71 Syriac */ -#define TT_UCR_SYRIAC (1L << 7) /* U+0700-U+074F */ +#define TT_UCR_SYRIAC (1UL << 7) /* U+0700-U+074F */ /* Bit 72 Thaana */ -#define TT_UCR_THAANA (1L << 8) /* U+0780-U+07BF */ +#define TT_UCR_THAANA (1UL << 8) /* U+0780-U+07BF */ /* Bit 73 Sinhala */ -#define TT_UCR_SINHALA (1L << 9) /* U+0D80-U+0DFF */ +#define TT_UCR_SINHALA (1UL << 9) /* U+0D80-U+0DFF */ /* Bit 74 Myanmar */ -#define TT_UCR_MYANMAR (1L << 10) /* U+1000-U+109F */ +#define TT_UCR_MYANMAR (1UL << 10) /* U+1000-U+109F */ /* Bit 75 Ethiopic */ /* Ethiopic Supplement */ /* Ethiopic Extended */ -#define TT_UCR_ETHIOPIC (1L << 11) /* U+1200-U+137F */ +#define TT_UCR_ETHIOPIC (1UL << 11) /* U+1200-U+137F */ /* U+1380-U+139F */ /* U+2D80-U+2DDF */ /* Bit 76 Cherokee */ -#define TT_UCR_CHEROKEE (1L << 12) /* U+13A0-U+13FF */ +#define TT_UCR_CHEROKEE (1UL << 12) /* U+13A0-U+13FF */ /* Bit 77 Unified Canadian Aboriginal Syllabics */ -#define TT_UCR_CANADIAN_ABORIGINAL_SYLLABICS (1L << 13) /* U+1400-U+167F */ +#define TT_UCR_CANADIAN_ABORIGINAL_SYLLABICS (1UL << 13) /* U+1400-U+167F */ /* Bit 78 Ogham */ -#define TT_UCR_OGHAM (1L << 14) /* U+1680-U+169F */ +#define TT_UCR_OGHAM (1UL << 14) /* U+1680-U+169F */ /* Bit 79 Runic */ -#define TT_UCR_RUNIC (1L << 15) /* U+16A0-U+16FF */ +#define TT_UCR_RUNIC (1UL << 15) /* U+16A0-U+16FF */ /* Bit 80 Khmer */ /* Khmer Symbols */ -#define TT_UCR_KHMER (1L << 16) /* U+1780-U+17FF */ +#define TT_UCR_KHMER (1UL << 16) /* U+1780-U+17FF */ /* U+19E0-U+19FF */ /* Bit 81 Mongolian */ -#define TT_UCR_MONGOLIAN (1L << 17) /* U+1800-U+18AF */ +#define TT_UCR_MONGOLIAN (1UL << 17) /* U+1800-U+18AF */ /* Bit 82 Braille Patterns */ -#define TT_UCR_BRAILLE (1L << 18) /* U+2800-U+28FF */ +#define TT_UCR_BRAILLE (1UL << 18) /* U+2800-U+28FF */ /* Bit 83 Yi Syllables */ /* Yi Radicals */ -#define TT_UCR_YI (1L << 19) /* U+A000-U+A48F */ +#define TT_UCR_YI (1UL << 19) /* U+A000-U+A48F */ /* U+A490-U+A4CF */ /* Bit 84 Tagalog */ /* Hanunoo */ /* Buhid */ /* Tagbanwa */ -#define TT_UCR_PHILIPPINE (1L << 20) /* U+1700-U+171F */ +#define TT_UCR_PHILIPPINE (1UL << 20) /* U+1700-U+171F */ /* U+1720-U+173F */ /* U+1740-U+175F */ /* U+1760-U+177F */ /* Bit 85 Old Italic */ -#define TT_UCR_OLD_ITALIC (1L << 21) /*U+10300-U+1032F*/ +#define TT_UCR_OLD_ITALIC (1UL << 21) /*U+10300-U+1032F*/ /* Bit 86 Gothic */ -#define TT_UCR_GOTHIC (1L << 22) /*U+10330-U+1034F*/ +#define TT_UCR_GOTHIC (1UL << 22) /*U+10330-U+1034F*/ /* Bit 87 Deseret */ -#define TT_UCR_DESERET (1L << 23) /*U+10400-U+1044F*/ +#define TT_UCR_DESERET (1UL << 23) /*U+10400-U+1044F*/ /* Bit 88 Byzantine Musical Symbols */ /* Musical Symbols */ /* Ancient Greek Musical Notation */ -#define TT_UCR_MUSICAL_SYMBOLS (1L << 24) /*U+1D000-U+1D0FF*/ +#define TT_UCR_MUSICAL_SYMBOLS (1UL << 24) /*U+1D000-U+1D0FF*/ /*U+1D100-U+1D1FF*/ /*U+1D200-U+1D24F*/ /* Bit 89 Mathematical Alphanumeric Symbols */ -#define TT_UCR_MATH_ALPHANUMERIC_SYMBOLS (1L << 25) /*U+1D400-U+1D7FF*/ +#define TT_UCR_MATH_ALPHANUMERIC_SYMBOLS (1UL << 25) /*U+1D400-U+1D7FF*/ /* Bit 90 Private Use (plane 15) */ /* Private Use (plane 16) */ -#define TT_UCR_PRIVATE_USE_SUPPLEMENTARY (1L << 26) /*U+F0000-U+FFFFD*/ +#define TT_UCR_PRIVATE_USE_SUPPLEMENTARY (1UL << 26) /*U+F0000-U+FFFFD*/ /*U+100000-U+10FFFD*/ /* Bit 91 Variation Selectors */ /* Variation Selectors Supplement */ -#define TT_UCR_VARIATION_SELECTORS (1L << 27) /* U+FE00-U+FE0F */ +#define TT_UCR_VARIATION_SELECTORS (1UL << 27) /* U+FE00-U+FE0F */ /*U+E0100-U+E01EF*/ /* Bit 92 Tags */ -#define TT_UCR_TAGS (1L << 28) /*U+E0000-U+E007F*/ +#define TT_UCR_TAGS (1UL << 28) /*U+E0000-U+E007F*/ /* Bit 93 Limbu */ -#define TT_UCR_LIMBU (1L << 29) /* U+1900-U+194F */ +#define TT_UCR_LIMBU (1UL << 29) /* U+1900-U+194F */ /* Bit 94 Tai Le */ -#define TT_UCR_TAI_LE (1L << 30) /* U+1950-U+197F */ +#define TT_UCR_TAI_LE (1UL << 30) /* U+1950-U+197F */ /* Bit 95 New Tai Lue */ -#define TT_UCR_NEW_TAI_LUE (1L << 31) /* U+1980-U+19DF */ +#define TT_UCR_NEW_TAI_LUE (1UL << 31) /* U+1980-U+19DF */ /* ulUnicodeRange4 */ /* --------------- */ /* Bit 96 Buginese */ -#define TT_UCR_BUGINESE (1L << 0) /* U+1A00-U+1A1F */ +#define TT_UCR_BUGINESE (1UL << 0) /* U+1A00-U+1A1F */ /* Bit 97 Glagolitic */ -#define TT_UCR_GLAGOLITIC (1L << 1) /* U+2C00-U+2C5F */ +#define TT_UCR_GLAGOLITIC (1UL << 1) /* U+2C00-U+2C5F */ /* Bit 98 Tifinagh */ -#define TT_UCR_TIFINAGH (1L << 2) /* U+2D30-U+2D7F */ +#define TT_UCR_TIFINAGH (1UL << 2) /* U+2D30-U+2D7F */ /* Bit 99 Yijing Hexagram Symbols */ -#define TT_UCR_YIJING (1L << 3) /* U+4DC0-U+4DFF */ +#define TT_UCR_YIJING (1UL << 3) /* U+4DC0-U+4DFF */ /* Bit 100 Syloti Nagri */ -#define TT_UCR_SYLOTI_NAGRI (1L << 4) /* U+A800-U+A82F */ +#define TT_UCR_SYLOTI_NAGRI (1UL << 4) /* U+A800-U+A82F */ /* Bit 101 Linear B Syllabary */ /* Linear B Ideograms */ /* Aegean Numbers */ -#define TT_UCR_LINEAR_B (1L << 5) /*U+10000-U+1007F*/ +#define TT_UCR_LINEAR_B (1UL << 5) /*U+10000-U+1007F*/ /*U+10080-U+100FF*/ /*U+10100-U+1013F*/ /* Bit 102 Ancient Greek Numbers */ -#define TT_UCR_ANCIENT_GREEK_NUMBERS (1L << 6) /*U+10140-U+1018F*/ +#define TT_UCR_ANCIENT_GREEK_NUMBERS (1UL << 6) /*U+10140-U+1018F*/ /* Bit 103 Ugaritic */ -#define TT_UCR_UGARITIC (1L << 7) /*U+10380-U+1039F*/ +#define TT_UCR_UGARITIC (1UL << 7) /*U+10380-U+1039F*/ /* Bit 104 Old Persian */ -#define TT_UCR_OLD_PERSIAN (1L << 8) /*U+103A0-U+103DF*/ +#define TT_UCR_OLD_PERSIAN (1UL << 8) /*U+103A0-U+103DF*/ /* Bit 105 Shavian */ -#define TT_UCR_SHAVIAN (1L << 9) /*U+10450-U+1047F*/ +#define TT_UCR_SHAVIAN (1UL << 9) /*U+10450-U+1047F*/ /* Bit 106 Osmanya */ -#define TT_UCR_OSMANYA (1L << 10) /*U+10480-U+104AF*/ +#define TT_UCR_OSMANYA (1UL << 10) /*U+10480-U+104AF*/ /* Bit 107 Cypriot Syllabary */ -#define TT_UCR_CYPRIOT_SYLLABARY (1L << 11) /*U+10800-U+1083F*/ +#define TT_UCR_CYPRIOT_SYLLABARY (1UL << 11) /*U+10800-U+1083F*/ /* Bit 108 Kharoshthi */ -#define TT_UCR_KHAROSHTHI (1L << 12) /*U+10A00-U+10A5F*/ +#define TT_UCR_KHAROSHTHI (1UL << 12) /*U+10A00-U+10A5F*/ /* Bit 109 Tai Xuan Jing Symbols */ -#define TT_UCR_TAI_XUAN_JING (1L << 13) /*U+1D300-U+1D35F*/ +#define TT_UCR_TAI_XUAN_JING (1UL << 13) /*U+1D300-U+1D35F*/ /* Bit 110 Cuneiform */ /* Cuneiform Numbers and Punctuation */ -#define TT_UCR_CUNEIFORM (1L << 14) /*U+12000-U+123FF*/ +#define TT_UCR_CUNEIFORM (1UL << 14) /*U+12000-U+123FF*/ /*U+12400-U+1247F*/ /* Bit 111 Counting Rod Numerals */ -#define TT_UCR_COUNTING_ROD_NUMERALS (1L << 15) /*U+1D360-U+1D37F*/ +#define TT_UCR_COUNTING_ROD_NUMERALS (1UL << 15) /*U+1D360-U+1D37F*/ /* Bit 112 Sundanese */ -#define TT_UCR_SUNDANESE (1L << 16) /* U+1B80-U+1BBF */ +#define TT_UCR_SUNDANESE (1UL << 16) /* U+1B80-U+1BBF */ /* Bit 113 Lepcha */ -#define TT_UCR_LEPCHA (1L << 17) /* U+1C00-U+1C4F */ +#define TT_UCR_LEPCHA (1UL << 17) /* U+1C00-U+1C4F */ /* Bit 114 Ol Chiki */ -#define TT_UCR_OL_CHIKI (1L << 18) /* U+1C50-U+1C7F */ +#define TT_UCR_OL_CHIKI (1UL << 18) /* U+1C50-U+1C7F */ /* Bit 115 Saurashtra */ -#define TT_UCR_SAURASHTRA (1L << 19) /* U+A880-U+A8DF */ +#define TT_UCR_SAURASHTRA (1UL << 19) /* U+A880-U+A8DF */ /* Bit 116 Kayah Li */ -#define TT_UCR_KAYAH_LI (1L << 20) /* U+A900-U+A92F */ +#define TT_UCR_KAYAH_LI (1UL << 20) /* U+A900-U+A92F */ /* Bit 117 Rejang */ -#define TT_UCR_REJANG (1L << 21) /* U+A930-U+A95F */ +#define TT_UCR_REJANG (1UL << 21) /* U+A930-U+A95F */ /* Bit 118 Cham */ -#define TT_UCR_CHAM (1L << 22) /* U+AA00-U+AA5F */ +#define TT_UCR_CHAM (1UL << 22) /* U+AA00-U+AA5F */ /* Bit 119 Ancient Symbols */ -#define TT_UCR_ANCIENT_SYMBOLS (1L << 23) /*U+10190-U+101CF*/ +#define TT_UCR_ANCIENT_SYMBOLS (1UL << 23) /*U+10190-U+101CF*/ /* Bit 120 Phaistos Disc */ -#define TT_UCR_PHAISTOS_DISC (1L << 24) /*U+101D0-U+101FF*/ +#define TT_UCR_PHAISTOS_DISC (1UL << 24) /*U+101D0-U+101FF*/ /* Bit 121 Carian */ /* Lycian */ /* Lydian */ -#define TT_UCR_OLD_ANATOLIAN (1L << 25) /*U+102A0-U+102DF*/ +#define TT_UCR_OLD_ANATOLIAN (1UL << 25) /*U+102A0-U+102DF*/ /*U+10280-U+1029F*/ /*U+10920-U+1093F*/ /* Bit 122 Domino Tiles */ /* Mahjong Tiles */ -#define TT_UCR_GAME_TILES (1L << 26) /*U+1F030-U+1F09F*/ +#define TT_UCR_GAME_TILES (1UL << 26) /*U+1F030-U+1F09F*/ /*U+1F000-U+1F02F*/ /* Bit 123-127 Reserved for process-internal usage */ diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h b/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h index 2cf0ff1bc61..aa4336435d9 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/tttables.h @@ -5,7 +5,7 @@ * Basic SFNT/TrueType tables definitions and interface * (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -192,7 +192,7 @@ FT_BEGIN_HEADER * A pointer into the 'hmtx' table. * * @note: - * For an OpenType variation font, the values of the following fields can + * For OpenType Font Variations, the values of the following fields can * change after a call to @FT_Set_Var_Design_Coordinates (and friends) if * the font contains an 'MVAR' table: `caret_Slope_Rise`, * `caret_Slope_Run`, and `caret_Offset`. @@ -310,7 +310,7 @@ FT_BEGIN_HEADER * A pointer into the 'vmtx' table. * * @note: - * For an OpenType variation font, the values of the following fields can + * For OpenType Font Variations, the values of the following fields can * change after a call to @FT_Set_Var_Design_Coordinates (and friends) if * the font contains an 'MVAR' table: `Ascender`, `Descender`, * `Line_Gap`, `caret_Slope_Rise`, `caret_Slope_Run`, and `caret_Offset`. @@ -359,7 +359,7 @@ FT_BEGIN_HEADER * table. In this case, the `version` field is always set to 0xFFFF. * * @note: - * For an OpenType variation font, the values of the following fields can + * For OpenType Font Variations, the values of the following fields can * change after a call to @FT_Set_Var_Design_Coordinates (and friends) if * the font contains an 'MVAR' table: `sCapHeight`, `sTypoAscender`, * `sTypoDescender`, `sTypoLineGap`, `sxHeight`, `usWinAscent`, @@ -442,7 +442,7 @@ FT_BEGIN_HEADER * them. * * @note: - * For an OpenType variation font, the values of the following fields can + * For OpenType Font Variations, the values of the following fields can * change after a call to @FT_Set_Var_Design_Coordinates (and friends) if * the font contains an 'MVAR' table: `underlinePosition` and * `underlineThickness`. @@ -705,6 +705,9 @@ FT_BEGIN_HEADER * definitions found in the @FT_TRUETYPE_TAGS_H file, or forge a new * one with @FT_MAKE_TAG. * + * [Since 2.14] Use value~1 if you want to access the table directory + * of the (currently selected) font. + * * offset :: * The starting offset in the table (or file if tag~==~0). * diff --git a/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h b/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h index da0af5d3f23..56bb0a3ee5e 100644 --- a/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h +++ b/src/java.desktop/share/native/libfreetype/include/freetype/tttags.h @@ -4,7 +4,7 @@ * * Tags for TrueType and OpenType tables (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/include/ft2build.h b/src/java.desktop/share/native/libfreetype/include/ft2build.h index d3d7685039c..3008aea7cf5 100644 --- a/src/java.desktop/share/native/libfreetype/include/ft2build.h +++ b/src/java.desktop/share/native/libfreetype/include/ft2build.h @@ -4,7 +4,7 @@ * * FreeType 2 build and setup macros. * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afadjust.c b/src/java.desktop/share/native/libfreetype/src/autofit/afadjust.c new file mode 100644 index 00000000000..6637cacfccf --- /dev/null +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afadjust.c @@ -0,0 +1,1612 @@ +/**************************************************************************** + * + * afadjust.c + * + * Auto-fitter routines to adjust components based on charcode (body). + * + * Copyright (C) 2023-2025 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * Written by Craig White . + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + +#include "afadjust.h" +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ +# include "afgsub.h" +#endif + +#include +#include +#include +#include + +#define AF_ADJUSTMENT_DATABASE_LENGTH \ + ( sizeof ( adjustment_database ) / \ + sizeof ( adjustment_database[0] ) ) + +#undef FT_COMPONENT +#define FT_COMPONENT afadjust + + + typedef struct AF_AdjustmentDatabaseEntry_ + { + FT_UInt32 codepoint; + FT_UInt32 flags; + + } AF_AdjustmentDatabaseEntry; + + + /* + All entries in this list must be sorted by ascending Unicode code + points. The table entries are 3 numbers consisting of: + + - Unicode code point. + - The vertical adjustment type. This should be a combination of the + AF_ADJUST_XXX and AF_IGNORE_XXX macros. + */ + static AF_AdjustmentDatabaseEntry adjustment_database[] = + { + /* C0 Controls and Basic Latin */ + { 0x21, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ! */ + { 0x51, AF_IGNORE_CAPITAL_BOTTOM } , /* Q */ + { 0x3F, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ? */ + { 0x69, AF_ADJUST_UP }, /* i */ + { 0x6A, AF_ADJUST_UP }, /* j */ +#if 0 + /* XXX TODO */ + { 0x7E, AF_ADJUST_TILDE_TOP }, /* ~ */ +#endif + + /* C1 Controls and Latin-1 Supplement */ + { 0xA1, AF_ADJUST_UP }, /* ¡ */ + { 0xA6, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ¦ */ + { 0xAA, AF_ADJUST_UP }, /* ª */ + { 0xBA, AF_ADJUST_UP }, /* º */ + { 0xBF, AF_ADJUST_UP }, /* ¿ */ + + { 0xC0, AF_ADJUST_UP }, /* À */ + { 0xC1, AF_ADJUST_UP }, /* Á */ + { 0xC2, AF_ADJUST_UP }, /* Â */ + { 0xC3, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Ã */ + { 0xC4, AF_ADJUST_UP }, /* Ä */ + { 0xC5, AF_ADJUST_UP }, /* Å */ + { 0xC7, AF_IGNORE_CAPITAL_BOTTOM }, /* Ç */ + { 0xC8, AF_ADJUST_UP }, /* È */ + { 0xC9, AF_ADJUST_UP }, /* É */ + { 0xCA, AF_ADJUST_UP }, /* Ê */ + { 0xCB, AF_ADJUST_UP }, /* Ë */ + { 0xCC, AF_ADJUST_UP }, /* Ì */ + { 0xCD, AF_ADJUST_UP }, /* Í */ + { 0xCE, AF_ADJUST_UP }, /* Î */ + { 0xCF, AF_ADJUST_UP }, /* Ï */ + + { 0xD1, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Ñ */ + { 0xD2, AF_ADJUST_UP }, /* Ò */ + { 0xD3, AF_ADJUST_UP }, /* Ó */ + { 0xD4, AF_ADJUST_UP }, /* Ô */ + { 0xD5, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Õ */ + { 0xD6, AF_ADJUST_UP }, /* Ö */ + { 0xD8, AF_IGNORE_CAPITAL_TOP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ø */ + { 0xD9, AF_ADJUST_UP }, /* Ù */ + { 0xDA, AF_ADJUST_UP }, /* Ú */ + { 0xDB, AF_ADJUST_UP }, /* Û */ + { 0xDC, AF_ADJUST_UP }, /* Ü */ + { 0xDD, AF_ADJUST_UP }, /* Ý */ + + { 0xE0, AF_ADJUST_UP }, /* à */ + { 0xE1, AF_ADJUST_UP }, /* á */ + { 0xE2, AF_ADJUST_UP }, /* â */ + { 0xE3, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ã */ + { 0xE4, AF_ADJUST_UP }, /* ä */ + { 0xE5, AF_ADJUST_UP }, /* å */ + { 0xE7, AF_IGNORE_SMALL_BOTTOM }, /* ç */ + { 0xE8, AF_ADJUST_UP }, /* è */ + { 0xE9, AF_ADJUST_UP }, /* é */ + { 0xEA, AF_ADJUST_UP }, /* ê */ + { 0xEB, AF_ADJUST_UP }, /* ë */ + { 0xEC, AF_ADJUST_UP }, /* ì */ + { 0xED, AF_ADJUST_UP }, /* í */ + { 0xEE, AF_ADJUST_UP }, /* î */ + { 0xEF, AF_ADJUST_UP }, /* ï */ + + { 0xF1, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ñ */ + { 0xF2, AF_ADJUST_UP }, /* ò */ + { 0xF3, AF_ADJUST_UP }, /* ó */ + { 0xF4, AF_ADJUST_UP }, /* ô */ + { 0xF5, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* õ */ + { 0xF6, AF_ADJUST_UP }, /* ö */ + { 0xF8, AF_IGNORE_SMALL_TOP | AF_IGNORE_SMALL_BOTTOM }, /* ø */ + { 0xF9, AF_ADJUST_UP }, /* ù */ + { 0xFA, AF_ADJUST_UP }, /* ú */ + { 0xFB, AF_ADJUST_UP }, /* û */ + { 0xFC, AF_ADJUST_UP }, /* ü */ + { 0xFD, AF_ADJUST_UP }, /* ý */ + { 0xFF, AF_ADJUST_UP }, /* ÿ */ + + /* Latin Extended-A */ + { 0x100, AF_ADJUST_UP }, /* Ā */ + { 0x101, AF_ADJUST_UP }, /* ā */ + { 0x102, AF_ADJUST_UP }, /* Ă */ + { 0x103, AF_ADJUST_UP }, /* ă */ + { 0x104, AF_IGNORE_CAPITAL_BOTTOM }, /* Ą */ + { 0x105, AF_IGNORE_SMALL_BOTTOM }, /* ą */ + { 0x106, AF_ADJUST_UP }, /* Ć */ + { 0x107, AF_ADJUST_UP }, /* ć */ + { 0x108, AF_ADJUST_UP }, /* Ĉ */ + { 0x109, AF_ADJUST_UP }, /* ĉ */ + { 0x10A, AF_ADJUST_UP }, /* Ċ */ + { 0x10B, AF_ADJUST_UP }, /* ċ */ + { 0x10C, AF_ADJUST_UP }, /* Č */ + { 0x10D, AF_ADJUST_UP }, /* č */ + { 0x10E, AF_ADJUST_UP }, /* Ď */ + + { 0x112, AF_ADJUST_UP }, /* Ē */ + { 0x113, AF_ADJUST_UP }, /* ē */ + { 0x114, AF_ADJUST_UP }, /* Ĕ */ + { 0x115, AF_ADJUST_UP }, /* ĕ */ + { 0x116, AF_ADJUST_UP }, /* Ė */ + { 0x117, AF_ADJUST_UP }, /* ė */ + { 0x118, AF_IGNORE_CAPITAL_BOTTOM }, /* Ę */ + { 0x119, AF_IGNORE_SMALL_BOTTOM }, /* ę */ + { 0x11A, AF_ADJUST_UP }, /* Ě */ + { 0x11B, AF_ADJUST_UP }, /* ě */ + { 0x11C, AF_ADJUST_UP }, /* Ĝ */ + { 0x11D, AF_ADJUST_UP }, /* ĝ */ + { 0x11E, AF_ADJUST_UP }, /* Ğ */ + { 0x11F, AF_ADJUST_UP }, /* ğ */ + + { 0x120, AF_ADJUST_UP }, /* Ġ */ + { 0x121, AF_ADJUST_UP }, /* ġ */ + { 0x122, AF_ADJUST_DOWN }, /* Ģ */ + { 0x123, AF_ADJUST_UP }, /* ģ */ + { 0x124, AF_ADJUST_UP }, /* Ĥ */ + { 0x125, AF_ADJUST_UP }, /* ĥ */ + { 0x128, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Ĩ */ + { 0x129, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ĩ */ + { 0x12A, AF_ADJUST_UP }, /* Ī */ + { 0x12B, AF_ADJUST_UP }, /* ī */ + { 0x12C, AF_ADJUST_UP }, /* Ĭ */ + { 0x12D, AF_ADJUST_UP }, /* ĭ */ + { 0x12E, AF_IGNORE_CAPITAL_BOTTOM }, /* Į */ + { 0x12F, AF_ADJUST_UP | AF_IGNORE_SMALL_BOTTOM }, /* į */ + + { 0x130, AF_ADJUST_UP }, /* İ */ + { 0x133, AF_ADJUST_UP }, /* ij */ + { 0x134, AF_ADJUST_UP }, /* Ĵ */ + { 0x135, AF_ADJUST_UP }, /* ĵ */ + { 0x136, AF_ADJUST_DOWN }, /* Ķ */ + { 0x137, AF_ADJUST_DOWN }, /* ķ */ + { 0x139, AF_ADJUST_UP }, /* Ĺ */ + { 0x13A, AF_ADJUST_UP }, /* ĺ */ + { 0x13B, AF_ADJUST_DOWN }, /* Ļ */ + { 0x13C, AF_ADJUST_DOWN }, /* ļ */ + + { 0x143, AF_ADJUST_UP }, /* Ń */ + { 0x144, AF_ADJUST_UP }, /* ń */ + { 0x145, AF_ADJUST_DOWN }, /* Ņ */ + { 0x146, AF_ADJUST_DOWN }, /* ņ */ + { 0x147, AF_ADJUST_UP }, /* Ň */ + { 0x148, AF_ADJUST_UP }, /* ň */ + { 0x14C, AF_ADJUST_UP }, /* Ō */ + { 0x14D, AF_ADJUST_UP }, /* ō */ + { 0x14E, AF_ADJUST_UP }, /* Ŏ */ + { 0x14F, AF_ADJUST_UP }, /* ŏ */ + + { 0x150, AF_ADJUST_UP }, /* Ő */ + { 0x151, AF_ADJUST_UP }, /* ő */ + { 0x154, AF_ADJUST_UP }, /* Ŕ */ + { 0x155, AF_ADJUST_UP }, /* ŕ */ + { 0x156, AF_ADJUST_DOWN }, /* Ŗ */ + { 0x157, AF_ADJUST_DOWN }, /* ŗ */ + { 0x158, AF_ADJUST_UP }, /* Ř */ + { 0x159, AF_ADJUST_UP }, /* ř */ + { 0x15A, AF_ADJUST_UP }, /* Ś */ + { 0x15B, AF_ADJUST_UP }, /* ś */ + { 0x15C, AF_ADJUST_UP }, /* Ŝ */ + { 0x15D, AF_ADJUST_UP }, /* ŝ */ + { 0x15E, AF_IGNORE_CAPITAL_BOTTOM }, /* Ş */ + { 0x15F, AF_IGNORE_SMALL_BOTTOM }, /* ş */ + + { 0x160, AF_ADJUST_UP }, /* Š */ + { 0x161, AF_ADJUST_UP }, /* š */ + { 0x162, AF_IGNORE_CAPITAL_BOTTOM }, /* Ţ */ + { 0x163, AF_IGNORE_SMALL_BOTTOM }, /* ţ */ + { 0x164, AF_ADJUST_UP }, /* Ť */ + { 0x168, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Ũ */ + { 0x169, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ũ */ + { 0x16A, AF_ADJUST_UP }, /* Ū */ + { 0x16B, AF_ADJUST_UP }, /* ū */ + { 0x16C, AF_ADJUST_UP }, /* Ŭ */ + { 0x16D, AF_ADJUST_UP }, /* ŭ */ + { 0x16E, AF_ADJUST_UP }, /* Ů */ + { 0x16F, AF_ADJUST_UP }, /* ů */ + + { 0x170, AF_ADJUST_UP }, /* Ű */ + { 0x171, AF_ADJUST_UP }, /* ű */ + { 0x172, AF_IGNORE_CAPITAL_BOTTOM }, /* Ų */ + { 0x173, AF_IGNORE_SMALL_BOTTOM }, /* ų */ + { 0x174, AF_ADJUST_UP }, /* Ŵ */ + { 0x175, AF_ADJUST_UP }, /* ŵ */ + { 0x176, AF_ADJUST_UP }, /* Ŷ */ + { 0x177, AF_ADJUST_UP }, /* ŷ */ + { 0x178, AF_ADJUST_UP }, /* Ÿ */ + { 0x179, AF_ADJUST_UP }, /* Ź */ + { 0x17A, AF_ADJUST_UP }, /* ź */ + { 0x17B, AF_ADJUST_UP }, /* Ż */ + { 0x17C, AF_ADJUST_UP }, /* ż */ + { 0x17D, AF_ADJUST_UP }, /* Ž */ + { 0x17E, AF_ADJUST_UP }, /* ž */ + + /* Latin Extended-B */ + { 0x187, AF_IGNORE_CAPITAL_TOP }, /* Ƈ */ + { 0x188, AF_IGNORE_SMALL_TOP }, /* ƈ */ + + { 0x1A0, AF_IGNORE_CAPITAL_TOP }, /* Ơ */ + { 0x1A1, AF_IGNORE_SMALL_TOP }, /* ơ */ + { 0x1A5, AF_IGNORE_SMALL_TOP }, /* ƥ */ + { 0x1AB, AF_IGNORE_SMALL_BOTTOM }, /* ƫ */ + { 0x1AE, AF_IGNORE_CAPITAL_BOTTOM }, /* Ʈ */ + { 0x1AF, AF_IGNORE_CAPITAL_TOP }, /* Ư */ + + { 0x1B0, AF_IGNORE_SMALL_TOP }, /* ư */ + { 0x1B4, AF_IGNORE_SMALL_TOP }, /* ƴ */ + + { 0x1C3, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ǃ */ + { 0x1C4, AF_ADJUST_UP }, /* DŽ */ +#if 0 + { 0x1C5, AF_ADJUST_UP }, /* Dž */ + { 0x1C6, AF_ADJUST_UP }, /* dž */ + { 0x1C8, AF_ADJUST_UP }, /* Lj */ + { 0x1C9, AF_ADJUST_UP }, /* lj */ + { 0x1CB, AF_ADJUST_UP }, /* Nj */ +#endif + { 0x1CC, AF_ADJUST_UP }, /* nj */ + { 0x1CD, AF_ADJUST_UP }, /* Ǎ */ + { 0x1CE, AF_ADJUST_UP }, /* ǎ */ + { 0x1CF, AF_ADJUST_UP }, /* Ǐ */ + + { 0x1D0, AF_ADJUST_UP }, /* ǐ */ + { 0x1D1, AF_ADJUST_UP }, /* Ǒ */ + { 0x1D2, AF_ADJUST_UP }, /* ǒ */ + { 0x1D3, AF_ADJUST_UP }, /* Ǔ */ + { 0x1D4, AF_ADJUST_UP }, /* ǔ */ + { 0x1D5, AF_ADJUST_UP2 }, /* Ǖ */ + { 0x1D6, AF_ADJUST_UP2 }, /* ǖ */ + { 0x1D7, AF_ADJUST_UP2 }, /* Ǘ */ + { 0x1D8, AF_ADJUST_UP2 }, /* ǘ */ + { 0x1D9, AF_ADJUST_UP2 }, /* Ǚ */ + { 0x1DA, AF_ADJUST_UP2 }, /* ǚ */ + { 0x1DB, AF_ADJUST_UP2 }, /* Ǜ */ + { 0x1DC, AF_ADJUST_UP2 }, /* ǜ */ + { 0x1DE, AF_ADJUST_UP2 }, /* Ǟ */ + { 0x1DF, AF_ADJUST_UP2 }, /* ǟ */ + + { 0x1E0, AF_ADJUST_UP2 }, /* Ǡ */ + { 0x1E1, AF_ADJUST_UP2 }, /* ǡ */ + { 0x1E2, AF_ADJUST_UP }, /* Ǣ */ + { 0x1E3, AF_ADJUST_UP }, /* ǣ */ + { 0x1E6, AF_ADJUST_UP }, /* Ǧ */ + { 0x1E7, AF_ADJUST_UP }, /* ǧ */ + { 0x1E8, AF_ADJUST_UP }, /* Ǩ */ + { 0x1E9, AF_ADJUST_UP }, /* ǩ */ + { 0x1EA, AF_IGNORE_CAPITAL_BOTTOM }, /* Ǫ */ + { 0x1EB, AF_IGNORE_SMALL_BOTTOM }, /* ǫ */ + { 0x1EC, AF_ADJUST_UP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ǭ */ + { 0x1ED, AF_ADJUST_UP | AF_IGNORE_SMALL_BOTTOM }, /* ǭ */ + { 0x1EE, AF_ADJUST_UP }, /* Ǯ */ + { 0x1EF, AF_ADJUST_UP }, /* ǯ */ + + { 0x1F0, AF_ADJUST_UP }, /* ǰ */ + { 0x1F4, AF_ADJUST_UP }, /* Ǵ */ + { 0x1F5, AF_ADJUST_UP }, /* ǵ */ + { 0x1F8, AF_ADJUST_UP }, /* Ǹ */ + { 0x1F9, AF_ADJUST_UP }, /* ǹ */ + { 0x1FA, AF_ADJUST_UP2 }, /* Ǻ */ + { 0x1FB, AF_ADJUST_UP2 }, /* ǻ */ + { 0x1FC, AF_ADJUST_UP }, /* Ǽ */ + { 0x1FD, AF_ADJUST_UP }, /* ǽ */ + { 0x1FE, AF_ADJUST_UP }, /* Ǿ */ + { 0x1FF, AF_ADJUST_UP }, /* ǿ */ + + { 0x200, AF_ADJUST_UP }, /* Ȁ */ + { 0x201, AF_ADJUST_UP }, /* ȁ */ + { 0x202, AF_ADJUST_UP }, /* Ȃ */ + { 0x203, AF_ADJUST_UP }, /* ȃ */ + { 0x204, AF_ADJUST_UP }, /* Ȅ */ + { 0x205, AF_ADJUST_UP }, /* ȅ */ + { 0x206, AF_ADJUST_UP }, /* Ȇ */ + { 0x207, AF_ADJUST_UP }, /* ȇ */ + { 0x208, AF_ADJUST_UP }, /* Ȉ */ + { 0x209, AF_ADJUST_UP }, /* ȉ */ + { 0x20A, AF_ADJUST_UP }, /* Ȋ */ + { 0x20B, AF_ADJUST_UP }, /* ȋ */ + { 0x20C, AF_ADJUST_UP }, /* Ȍ */ + { 0x20D, AF_ADJUST_UP }, /* ȍ */ + { 0x20E, AF_ADJUST_UP }, /* Ȏ */ + { 0x20F, AF_ADJUST_UP }, /* ȏ */ + + { 0x210, AF_ADJUST_UP }, /* Ȑ */ + { 0x211, AF_ADJUST_UP }, /* ȑ */ + { 0x212, AF_ADJUST_UP }, /* Ȓ */ + { 0x213, AF_ADJUST_UP }, /* ȓ */ + { 0x214, AF_ADJUST_UP }, /* Ȕ */ + { 0x215, AF_ADJUST_UP }, /* ȕ */ + { 0x216, AF_ADJUST_UP }, /* Ȗ */ + { 0x217, AF_ADJUST_UP }, /* ȗ */ + { 0x218, AF_ADJUST_DOWN }, /* Ș */ + { 0x219, AF_ADJUST_DOWN }, /* ș */ + { 0x21A, AF_ADJUST_DOWN }, /* Ț */ + { 0x21B, AF_ADJUST_DOWN }, /* ț */ + { 0x21E, AF_ADJUST_UP }, /* Ȟ */ + { 0x21F, AF_ADJUST_UP }, /* ȟ */ + + { 0x224, AF_IGNORE_CAPITAL_BOTTOM }, /* Ȥ */ + { 0x225, AF_IGNORE_SMALL_BOTTOM }, /* ȥ */ + { 0x226, AF_ADJUST_UP }, /* Ȧ */ + { 0x227, AF_ADJUST_UP }, /* ȧ */ + { 0x228, AF_IGNORE_CAPITAL_BOTTOM }, /* Ȩ */ + { 0x229, AF_IGNORE_SMALL_BOTTOM }, /* ȩ */ + { 0x22A, AF_ADJUST_UP2 }, /* Ȫ */ + { 0x22B, AF_ADJUST_UP2 }, /* ȫ */ + { 0x22C, AF_ADJUST_UP2 }, /* Ȭ */ + { 0x22D, AF_ADJUST_UP2 }, /* ȭ */ + { 0x22E, AF_ADJUST_UP }, /* Ȯ */ + { 0x22F, AF_ADJUST_UP }, /* ȯ */ + + { 0x230, AF_ADJUST_UP2 }, /* Ȱ */ + { 0x231, AF_ADJUST_UP2 }, /* ȱ */ + { 0x232, AF_ADJUST_UP }, /* Ȳ */ + { 0x233, AF_ADJUST_UP }, /* ȳ */ + { 0x23A, AF_IGNORE_CAPITAL_TOP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ⱥ */ + { 0x23B, AF_IGNORE_CAPITAL_TOP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ȼ */ + { 0x23F, AF_IGNORE_SMALL_BOTTOM }, /* ȿ */ + + { 0x240, AF_IGNORE_SMALL_BOTTOM }, /* ɀ */ + { 0x249, AF_ADJUST_UP }, /* ɉ */ + + /* IPA Extensions */ + { 0x256, AF_IGNORE_SMALL_BOTTOM }, /* ɖ */ + + { 0x260, AF_IGNORE_SMALL_TOP }, /* ɠ */ + { 0x267, AF_IGNORE_SMALL_BOTTOM }, /* ɧ */ + { 0x268, AF_ADJUST_UP }, /* ɨ */ + + { 0x272, AF_IGNORE_SMALL_BOTTOM }, /* ɲ */ + { 0x273, AF_IGNORE_SMALL_BOTTOM }, /* ɳ */ + { 0x27B, AF_IGNORE_SMALL_BOTTOM }, /* ɻ */ + { 0x27D, AF_IGNORE_SMALL_BOTTOM }, /* ɽ */ + + { 0x282, AF_IGNORE_SMALL_BOTTOM }, /* ʂ */ + { 0x288, AF_IGNORE_SMALL_BOTTOM }, /* ʈ */ + + { 0x290, AF_IGNORE_SMALL_BOTTOM }, /* ʐ */ + { 0x29B, AF_IGNORE_SMALL_TOP }, /* ʛ */ + + { 0x2A0, AF_IGNORE_SMALL_TOP }, /* ʠ */ + + /* Spacing Modifier Letters */ + { 0x2B2, AF_ADJUST_UP }, /* ʲ */ + { 0x2B5, AF_IGNORE_SMALL_BOTTOM }, /* ʵ */ + + /* Greek and Coptic */ + { 0x390, AF_ADJUST_UP2 }, /* ΐ */ + + { 0x3AA, AF_ADJUST_UP }, /* Ϊ */ + { 0x3AB, AF_ADJUST_UP }, /* Ϋ */ + { 0x3AC, AF_ADJUST_UP }, /* ά */ + { 0x3AD, AF_ADJUST_UP }, /* έ */ + { 0x3AE, AF_ADJUST_UP }, /* ή */ + { 0x3AF, AF_ADJUST_UP }, /* ί */ + + { 0x3B0, AF_ADJUST_UP2 }, /* ΰ */ + + { 0x3CA, AF_ADJUST_UP }, /* ϊ */ + { 0x3CB, AF_ADJUST_UP }, /* ϋ */ + { 0x3CC, AF_ADJUST_UP }, /* ό */ + { 0x3CD, AF_ADJUST_UP }, /* ύ */ + { 0x3CE, AF_ADJUST_UP }, /* ώ */ + { 0x3CF, AF_IGNORE_CAPITAL_BOTTOM }, /* Ϗ */ + + { 0x3D4, AF_ADJUST_UP }, /* ϔ */ + { 0x3D7, AF_IGNORE_SMALL_BOTTOM }, /* ϗ */ + { 0x3D9, AF_IGNORE_SMALL_BOTTOM }, /* ϙ */ + + { 0x3E2, AF_IGNORE_CAPITAL_BOTTOM }, /* Ϣ */ + { 0x3E3, AF_IGNORE_SMALL_BOTTOM }, /* ϣ */ + + { 0x3F3, AF_ADJUST_UP }, /* ϳ */ + + /* Cyrillic */ + { 0x400, AF_ADJUST_UP }, /* Ѐ */ + { 0x401, AF_ADJUST_UP }, /* Ё */ + { 0x403, AF_ADJUST_UP }, /* Ѓ */ + { 0x407, AF_ADJUST_UP }, /* Ї */ + { 0x40C, AF_ADJUST_UP }, /* Ќ */ + { 0x40D, AF_ADJUST_UP }, /* Ѝ */ + { 0x40E, AF_ADJUST_UP }, /* Ў */ + { 0x40F, AF_IGNORE_CAPITAL_BOTTOM }, /* Џ */ + + { 0x419, AF_ADJUST_UP }, /* Й */ + + { 0x426, AF_IGNORE_CAPITAL_BOTTOM }, /* Ц */ + { 0x429, AF_IGNORE_CAPITAL_BOTTOM }, /* Щ */ + + { 0x439, AF_ADJUST_UP }, /* й */ + + { 0x446, AF_IGNORE_SMALL_BOTTOM }, /* ц */ + { 0x449, AF_IGNORE_SMALL_BOTTOM }, /* щ */ + + { 0x450, AF_ADJUST_UP }, /* ѐ */ + { 0x451, AF_ADJUST_UP }, /* ё */ + { 0x453, AF_ADJUST_UP }, /* ѓ */ + { 0x456, AF_ADJUST_UP }, /* і */ + { 0x457, AF_ADJUST_UP }, /* ї */ + { 0x458, AF_ADJUST_UP }, /* ј */ + { 0x45C, AF_ADJUST_UP }, /* ќ */ + { 0x45D, AF_ADJUST_UP }, /* ѝ */ + { 0x45E, AF_ADJUST_UP }, /* ў */ + { 0x45F, AF_IGNORE_SMALL_BOTTOM }, /* џ */ + + { 0x476, AF_ADJUST_UP }, /* Ѷ */ + { 0x477, AF_ADJUST_UP }, /* ѷ */ + { 0x47C, AF_ADJUST_UP2 }, /* Ѽ */ + { 0x47D, AF_ADJUST_UP2 }, /* ѽ */ + { 0x47E, AF_ADJUST_UP }, /* Ѿ */ + { 0x47F, AF_ADJUST_UP }, /* ѿ */ + + { 0x480, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҁ */ + { 0x481, AF_IGNORE_SMALL_BOTTOM }, /* ҁ */ + { 0x48A, AF_ADJUST_UP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ҋ */ + { 0x48B, AF_ADJUST_UP | AF_IGNORE_SMALL_BOTTOM }, /* ҋ */ + + { 0x490, AF_IGNORE_CAPITAL_TOP }, /* Ґ */ + { 0x491, AF_IGNORE_SMALL_TOP }, /* ґ */ + { 0x496, AF_IGNORE_CAPITAL_BOTTOM }, /* Җ */ + { 0x497, AF_IGNORE_SMALL_BOTTOM }, /* җ */ + { 0x498, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҙ */ + { 0x499, AF_IGNORE_SMALL_BOTTOM }, /* ҙ */ + { 0x49A, AF_IGNORE_CAPITAL_BOTTOM }, /* Қ */ + { 0x49B, AF_IGNORE_SMALL_BOTTOM }, /* қ */ + + { 0x4A2, AF_IGNORE_CAPITAL_BOTTOM }, /* Ң */ + { 0x4A3, AF_IGNORE_SMALL_BOTTOM }, /* ң */ + { 0x4AA, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҫ */ + { 0x4AB, AF_IGNORE_SMALL_BOTTOM }, /* ҫ */ + { 0x4AC, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҭ */ + { 0x4AD, AF_IGNORE_SMALL_BOTTOM }, /* ҭ */ + + { 0x4B2, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҳ */ + { 0x4B3, AF_IGNORE_SMALL_BOTTOM }, /* ҳ */ + { 0x4B4, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҵ */ + { 0x4B5, AF_IGNORE_SMALL_BOTTOM }, /* ҵ */ + { 0x4B6, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҷ */ + { 0x4B7, AF_IGNORE_SMALL_BOTTOM }, /* ҷ */ + { 0x4BE, AF_IGNORE_CAPITAL_BOTTOM }, /* Ҿ */ + { 0x4BF, AF_IGNORE_SMALL_BOTTOM }, /* ҿ */ + + { 0x4C1, AF_ADJUST_UP }, /* Ӂ */ + { 0x4C2, AF_ADJUST_UP }, /* ӂ */ + { 0x4C5, AF_IGNORE_CAPITAL_BOTTOM }, /* Ӆ */ + { 0x4C6, AF_IGNORE_SMALL_BOTTOM }, /* ӆ */ + { 0x4C9, AF_IGNORE_CAPITAL_BOTTOM }, /* Ӊ */ + { 0x4CA, AF_IGNORE_SMALL_BOTTOM }, /* ӊ */ + { 0x4CB, AF_IGNORE_CAPITAL_BOTTOM }, /* Ӌ */ + { 0x4CC, AF_IGNORE_SMALL_BOTTOM }, /* ӌ */ + { 0x4CD, AF_IGNORE_CAPITAL_BOTTOM }, /* Ӎ */ + { 0x4CE, AF_IGNORE_SMALL_BOTTOM }, /* ӎ */ + + { 0x4D0, AF_ADJUST_UP }, /* Ӑ */ + { 0x4D1, AF_ADJUST_UP }, /* ӑ */ + { 0x4D2, AF_ADJUST_UP }, /* Ӓ */ + { 0x4D3, AF_ADJUST_UP }, /* ӓ */ + { 0x4D6, AF_ADJUST_UP }, /* Ӗ */ + { 0x4D7, AF_ADJUST_UP }, /* ӗ */ + { 0x4DA, AF_ADJUST_UP }, /* Ӛ */ + { 0x4DB, AF_ADJUST_UP }, /* ӛ */ + { 0x4DC, AF_ADJUST_UP }, /* Ӝ */ + { 0x4DD, AF_ADJUST_UP }, /* ӝ */ + { 0x4DE, AF_ADJUST_UP }, /* Ӟ */ + { 0x4DF, AF_ADJUST_UP }, /* ӟ */ + + { 0x4E2, AF_ADJUST_UP }, /* Ӣ */ + { 0x4E3, AF_ADJUST_UP }, /* ӣ */ + { 0x4E4, AF_ADJUST_UP }, /* Ӥ */ + { 0x4E5, AF_ADJUST_UP }, /* ӥ */ + { 0x4E6, AF_ADJUST_UP }, /* Ӧ */ + { 0x4E7, AF_ADJUST_UP }, /* ӧ */ + { 0x4EA, AF_ADJUST_UP }, /* Ӫ */ + { 0x4EB, AF_ADJUST_UP }, /* ӫ */ + { 0x4EC, AF_ADJUST_UP }, /* Ӭ */ + { 0x4ED, AF_ADJUST_UP }, /* ӭ */ + { 0x4EE, AF_ADJUST_UP }, /* Ӯ */ + { 0x4EF, AF_ADJUST_UP }, /* ӯ */ + + { 0x4F0, AF_ADJUST_UP }, /* Ӱ */ + { 0x4F1, AF_ADJUST_UP }, /* ӱ */ + { 0x4F2, AF_ADJUST_UP }, /* Ӳ */ + { 0x4F3, AF_ADJUST_UP }, /* ӳ */ + { 0x4F4, AF_ADJUST_UP }, /* Ӵ */ + { 0x4F5, AF_ADJUST_UP }, /* ӵ */ + { 0x4F6, AF_IGNORE_CAPITAL_BOTTOM }, /* Ӷ */ + { 0x4F7, AF_IGNORE_SMALL_BOTTOM }, /* ӷ */ + { 0x4F8, AF_ADJUST_UP }, /* Ӹ */ + { 0x4F9, AF_ADJUST_UP }, /* ӹ */ + { 0x4FA, AF_IGNORE_CAPITAL_BOTTOM }, /* Ӻ */ + { 0x4FB, AF_IGNORE_SMALL_BOTTOM }, /* ӻ */ + + /* Cyrillic Supplement */ + { 0x506, AF_IGNORE_CAPITAL_BOTTOM }, /* Ԇ */ + { 0x507, AF_IGNORE_SMALL_BOTTOM }, /* ԇ */ + + { 0x524, AF_IGNORE_CAPITAL_BOTTOM }, /* Ԥ */ + { 0x525, AF_IGNORE_SMALL_BOTTOM }, /* ԥ */ + { 0x526, AF_IGNORE_CAPITAL_BOTTOM }, /* Ԧ */ + { 0x527, AF_IGNORE_SMALL_BOTTOM }, /* ԧ */ + { 0x52E, AF_IGNORE_CAPITAL_BOTTOM }, /* Ԯ */ + { 0x52F, AF_IGNORE_SMALL_BOTTOM }, /* ԯ */ + + /* Cherokee */ + { 0x13A5, AF_ADJUST_UP }, /* Ꭵ */ + + /* Phonetic Extensions */ + { 0x1D09, AF_ADJUST_DOWN }, /* ᴉ */ + + { 0x1D4E, AF_ADJUST_DOWN }, /* ᵎ */ + + { 0x1D51, AF_IGNORE_SMALL_BOTTOM }, /* ᵑ */ + + { 0x1D62, AF_ADJUST_UP }, /* ᵢ */ + + /* Phonetic Extensions Supplement */ + { 0x1D80, AF_IGNORE_SMALL_BOTTOM }, /* ᶀ */ + { 0x1D81, AF_IGNORE_SMALL_BOTTOM }, /* ᶁ */ + { 0x1D82, AF_IGNORE_SMALL_BOTTOM }, /* ᶂ */ + { 0x1D84, AF_IGNORE_SMALL_BOTTOM }, /* ᶄ */ + { 0x1D85, AF_IGNORE_SMALL_BOTTOM }, /* ᶅ */ + { 0x1D86, AF_IGNORE_SMALL_BOTTOM }, /* ᶆ */ + { 0x1D87, AF_IGNORE_SMALL_BOTTOM }, /* ᶇ */ + { 0x1D89, AF_IGNORE_SMALL_BOTTOM }, /* ᶉ */ + { 0x1D8A, AF_IGNORE_SMALL_BOTTOM }, /* ᶊ */ + { 0x1D8C, AF_IGNORE_SMALL_BOTTOM }, /* ᶌ */ + { 0x1D8D, AF_IGNORE_SMALL_BOTTOM }, /* ᶍ */ + { 0x1D8E, AF_IGNORE_SMALL_BOTTOM }, /* ᶎ */ + { 0x1D8F, AF_IGNORE_SMALL_BOTTOM }, /* ᶏ */ + + { 0x1D90, AF_IGNORE_SMALL_BOTTOM }, /* ᶐ */ + { 0x1D91, AF_IGNORE_SMALL_BOTTOM }, /* ᶑ */ + { 0x1D92, AF_IGNORE_SMALL_BOTTOM }, /* ᶒ */ + { 0x1D93, AF_IGNORE_SMALL_BOTTOM }, /* ᶓ */ + { 0x1D94, AF_IGNORE_SMALL_BOTTOM }, /* ᶔ */ + { 0x1D95, AF_IGNORE_SMALL_BOTTOM }, /* ᶕ */ + { 0x1D96, AF_ADJUST_UP | AF_IGNORE_SMALL_BOTTOM }, /* ᶖ */ + { 0x1D97, AF_IGNORE_SMALL_BOTTOM }, /* ᶗ */ + { 0x1D98, AF_IGNORE_SMALL_BOTTOM }, /* ᶘ */ + { 0x1D99, AF_IGNORE_SMALL_BOTTOM }, /* ᶙ */ + { 0x1D9A, AF_IGNORE_SMALL_BOTTOM }, /* ᶚ */ + + { 0x1DA4, AF_ADJUST_UP }, /* ᶤ */ + { 0x1DA8, AF_ADJUST_UP }, /* ᶨ */ + { 0x1DA9, AF_IGNORE_SMALL_BOTTOM }, /* ᶩ */ + { 0x1DAA, AF_IGNORE_SMALL_BOTTOM }, /* ᶪ */ + { 0x1DAC, AF_IGNORE_SMALL_BOTTOM }, /* ᶬ */ + { 0x1DAE, AF_IGNORE_SMALL_BOTTOM }, /* ᶮ */ + { 0x1DAF, AF_IGNORE_SMALL_BOTTOM }, /* ᶯ */ + + { 0x1DB3, AF_IGNORE_SMALL_BOTTOM }, /* ᶳ */ + { 0x1DB5, AF_IGNORE_SMALL_BOTTOM }, /* ᶵ */ + { 0x1DBC, AF_IGNORE_SMALL_BOTTOM }, /* ᶼ */ + + /* Latin Extended Additional */ + { 0x1E00, AF_ADJUST_DOWN }, /* Ḁ */ + { 0x1E01, AF_ADJUST_DOWN }, /* ḁ */ + { 0x1E02, AF_ADJUST_UP }, /* Ḃ */ + { 0x1E03, AF_ADJUST_UP }, /* ḃ */ + { 0x1E04, AF_ADJUST_DOWN }, /* Ḅ */ + { 0x1E05, AF_ADJUST_DOWN }, /* ḅ */ + { 0x1E06, AF_ADJUST_DOWN }, /* Ḇ */ + { 0x1E07, AF_ADJUST_DOWN }, /* ḇ */ + { 0x1E08, AF_ADJUST_UP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ḉ */ + { 0x1E09, AF_ADJUST_UP | AF_IGNORE_SMALL_BOTTOM }, /* ḉ */ + { 0x1E0A, AF_ADJUST_UP }, /* Ḋ */ + { 0x1E0B, AF_ADJUST_UP }, /* ḋ */ + { 0x1E0C, AF_ADJUST_DOWN }, /* Ḍ */ + { 0x1E0D, AF_ADJUST_DOWN }, /* ḍ */ + { 0x1E0E, AF_ADJUST_DOWN }, /* Ḏ */ + { 0x1E0F, AF_ADJUST_DOWN }, /* ḏ */ + + { 0x1E10, AF_ADJUST_DOWN }, /* Ḑ */ + { 0x1E11, AF_ADJUST_DOWN }, /* ḑ */ + { 0x1E12, AF_ADJUST_DOWN }, /* Ḓ */ + { 0x1E13, AF_ADJUST_DOWN }, /* ḓ */ + { 0x1E14, AF_ADJUST_UP2 }, /* Ḕ */ + { 0x1E15, AF_ADJUST_UP2 }, /* ḕ */ + { 0x1E16, AF_ADJUST_UP2 }, /* Ḗ */ + { 0x1E17, AF_ADJUST_UP2 }, /* ḗ */ + { 0x1E18, AF_ADJUST_DOWN }, /* Ḙ */ + { 0x1E19, AF_ADJUST_DOWN }, /* ḙ */ + { 0x1E1A, AF_ADJUST_DOWN | AF_ADJUST_TILDE_BOTTOM }, /* Ḛ */ + { 0x1E1B, AF_ADJUST_DOWN | AF_ADJUST_TILDE_BOTTOM }, /* ḛ */ + { 0x1E1C, AF_ADJUST_UP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ḝ */ + { 0x1E1D, AF_ADJUST_UP | AF_IGNORE_SMALL_BOTTOM }, /* ḝ */ + { 0x1E1E, AF_ADJUST_UP }, /* Ḟ */ + { 0x1E1F, AF_ADJUST_UP }, /* ḟ */ + + { 0x1E20, AF_ADJUST_UP }, /* Ḡ */ + { 0x1E21, AF_ADJUST_UP }, /* ḡ */ + { 0x1E22, AF_ADJUST_UP }, /* Ḣ */ + { 0x1E23, AF_ADJUST_UP }, /* ḣ */ + { 0x1E24, AF_ADJUST_DOWN }, /* Ḥ */ + { 0x1E25, AF_ADJUST_DOWN }, /* ḥ */ + { 0x1E26, AF_ADJUST_UP }, /* Ḧ */ + { 0x1E27, AF_ADJUST_UP }, /* ḧ */ + { 0x1E28, AF_IGNORE_CAPITAL_BOTTOM }, /* Ḩ */ + { 0x1E29, AF_IGNORE_SMALL_BOTTOM }, /* ḩ */ + { 0x1E2A, AF_ADJUST_DOWN }, /* Ḫ */ + { 0x1E2B, AF_ADJUST_DOWN }, /* ḫ */ + { 0x1E2C, AF_ADJUST_DOWN | AF_ADJUST_TILDE_BOTTOM }, /* Ḭ */ + { 0x1E2D, AF_ADJUST_UP | AF_ADJUST_DOWN | AF_ADJUST_TILDE_BOTTOM }, /* ḭ */ + { 0x1E2E, AF_ADJUST_UP2 }, /* Ḯ */ + { 0x1E2F, AF_ADJUST_UP2 }, /* ḯ */ + + { 0x1E30, AF_ADJUST_UP }, /* Ḱ */ + { 0x1E31, AF_ADJUST_UP }, /* ḱ */ + { 0x1E32, AF_ADJUST_DOWN }, /* Ḳ */ + { 0x1E33, AF_ADJUST_DOWN }, /* ḳ */ + { 0x1E34, AF_ADJUST_DOWN }, /* Ḵ */ + { 0x1E35, AF_ADJUST_DOWN }, /* ḵ */ + { 0x1E36, AF_ADJUST_DOWN }, /* Ḷ */ + { 0x1E37, AF_ADJUST_DOWN }, /* ḷ */ + { 0x1E38, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* Ḹ */ + { 0x1E39, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ḹ */ + { 0x1E3A, AF_ADJUST_DOWN }, /* Ḻ */ + { 0x1E3B, AF_ADJUST_DOWN }, /* ḻ */ + { 0x1E3C, AF_ADJUST_DOWN }, /* Ḽ */ + { 0x1E3D, AF_ADJUST_DOWN }, /* ḽ */ + { 0x1E3E, AF_ADJUST_UP }, /* Ḿ */ + { 0x1E3F, AF_ADJUST_UP }, /* ḿ */ + + { 0x1E40, AF_ADJUST_UP }, /* Ṁ */ + { 0x1E41, AF_ADJUST_UP }, /* ṁ */ + { 0x1E42, AF_ADJUST_DOWN }, /* Ṃ */ + { 0x1E43, AF_ADJUST_DOWN }, /* ṃ */ + { 0x1E44, AF_ADJUST_UP }, /* Ṅ */ + { 0x1E45, AF_ADJUST_UP }, /* ṅ */ + { 0x1E46, AF_ADJUST_DOWN }, /* Ṇ */ + { 0x1E47, AF_ADJUST_DOWN }, /* ṇ */ + { 0x1E48, AF_ADJUST_DOWN }, /* Ṉ */ + { 0x1E49, AF_ADJUST_DOWN }, /* ṉ */ + { 0x1E4A, AF_ADJUST_DOWN }, /* Ṋ */ + { 0x1E4B, AF_ADJUST_DOWN }, /* ṋ */ + { 0x1E4C, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP2 }, /* Ṍ */ + { 0x1E4D, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP2 }, /* ṍ */ + { 0x1E4E, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP2 }, /* Ṏ */ + { 0x1E4F, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP2 }, /* ṏ */ + + { 0x1E50, AF_ADJUST_UP2 }, /* Ṑ */ + { 0x1E51, AF_ADJUST_UP2 }, /* ṑ */ + { 0x1E52, AF_ADJUST_UP2 }, /* Ṓ */ + { 0x1E53, AF_ADJUST_UP2 }, /* ṓ */ + { 0x1E54, AF_ADJUST_UP }, /* Ṕ */ + { 0x1E55, AF_ADJUST_UP }, /* ṕ */ + { 0x1E56, AF_ADJUST_UP }, /* Ṗ */ + { 0x1E57, AF_ADJUST_UP }, /* ṗ */ + { 0x1E58, AF_ADJUST_UP }, /* Ṙ */ + { 0x1E59, AF_ADJUST_UP }, /* ṙ */ + { 0x1E5A, AF_ADJUST_DOWN }, /* Ṛ */ + { 0x1E5B, AF_ADJUST_DOWN }, /* ṛ */ + { 0x1E5C, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* Ṝ */ + { 0x1E5D, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ṝ */ + { 0x1E5E, AF_ADJUST_DOWN }, /* Ṟ */ + { 0x1E5F, AF_ADJUST_DOWN }, /* ṟ */ + + { 0x1E60, AF_ADJUST_UP }, /* Ṡ */ + { 0x1E61, AF_ADJUST_UP }, /* ṡ */ + { 0x1E62, AF_ADJUST_DOWN }, /* Ṣ */ + { 0x1E63, AF_ADJUST_DOWN }, /* ṣ */ + { 0x1E64, AF_ADJUST_UP }, /* Ṥ */ + { 0x1E65, AF_ADJUST_UP }, /* ṥ */ + { 0x1E66, AF_ADJUST_UP }, /* Ṧ */ + { 0x1E67, AF_ADJUST_UP }, /* ṧ */ + { 0x1E68, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* Ṩ */ + { 0x1E69, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ṩ */ + { 0x1E6A, AF_ADJUST_UP }, /* Ṫ */ + { 0x1E6B, AF_ADJUST_UP }, /* ṫ */ + { 0x1E6C, AF_ADJUST_DOWN }, /* Ṭ */ + { 0x1E6D, AF_ADJUST_DOWN }, /* ṭ */ + { 0x1E6E, AF_ADJUST_DOWN }, /* Ṯ */ + { 0x1E6F, AF_ADJUST_DOWN }, /* ṯ */ + + { 0x1E70, AF_ADJUST_DOWN }, /* Ṱ */ + { 0x1E71, AF_ADJUST_DOWN }, /* ṱ */ + { 0x1E72, AF_ADJUST_DOWN }, /* Ṳ */ + { 0x1E73, AF_ADJUST_DOWN }, /* ṳ */ + { 0x1E74, AF_ADJUST_DOWN | AF_ADJUST_TILDE_BOTTOM }, /* Ṵ */ + { 0x1E75, AF_ADJUST_DOWN | AF_ADJUST_TILDE_BOTTOM }, /* ṵ */ + { 0x1E76, AF_ADJUST_DOWN }, /* Ṷ */ + { 0x1E77, AF_ADJUST_DOWN }, /* ṷ */ + { 0x1E78, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP2 }, /* Ṹ */ + { 0x1E79, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP2 }, /* ṹ */ + { 0x1E7A, AF_ADJUST_UP2 }, /* Ṻ */ + { 0x1E7B, AF_ADJUST_UP2 }, /* ṻ */ + { 0x1E7C, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Ṽ */ + { 0x1E7D, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ṽ */ + { 0x1E7E, AF_ADJUST_DOWN }, /* Ṿ */ + { 0x1E7F, AF_ADJUST_DOWN }, /* ṿ */ + + { 0x1E80, AF_ADJUST_UP }, /* Ẁ */ + { 0x1E81, AF_ADJUST_UP }, /* ẁ */ + { 0x1E82, AF_ADJUST_UP }, /* Ẃ */ + { 0x1E83, AF_ADJUST_UP }, /* ẃ */ + { 0x1E84, AF_ADJUST_UP }, /* Ẅ */ + { 0x1E85, AF_ADJUST_UP }, /* ẅ */ + { 0x1E86, AF_ADJUST_UP }, /* Ẇ */ + { 0x1E87, AF_ADJUST_UP }, /* ẇ */ + { 0x1E88, AF_ADJUST_DOWN }, /* Ẉ */ + { 0x1E89, AF_ADJUST_DOWN }, /* ẉ */ + { 0x1E8A, AF_ADJUST_UP }, /* Ẋ */ + { 0x1E8B, AF_ADJUST_UP }, /* ẋ */ + { 0x1E8C, AF_ADJUST_UP }, /* Ẍ */ + { 0x1E8D, AF_ADJUST_UP }, /* ẍ */ + { 0x1E8E, AF_ADJUST_UP }, /* Ẏ */ + { 0x1E8F, AF_ADJUST_UP }, /* ẏ */ + + { 0x1E90, AF_ADJUST_UP }, /* Ẑ */ + { 0x1E91, AF_ADJUST_UP }, /* ẑ */ + { 0x1E92, AF_ADJUST_DOWN }, /* Ẓ */ + { 0x1E93, AF_ADJUST_DOWN }, /* ẓ */ + { 0x1E94, AF_ADJUST_DOWN }, /* Ẕ */ + { 0x1E95, AF_ADJUST_DOWN }, /* ẕ */ + { 0x1E96, AF_ADJUST_DOWN }, /* ẖ */ + { 0x1E97, AF_ADJUST_UP }, /* ẗ */ + { 0x1E98, AF_ADJUST_UP }, /* ẘ */ + { 0x1E99, AF_ADJUST_UP }, /* ẙ */ + { 0x1E9A, AF_ADJUST_UP }, /* ẚ */ + { 0x1E9B, AF_ADJUST_UP }, /* ẛ */ + + { 0x1EA0, AF_ADJUST_DOWN }, /* Ạ */ + { 0x1EA1, AF_ADJUST_DOWN }, /* ạ */ + { 0x1EA2, AF_ADJUST_UP }, /* Ả */ + { 0x1EA3, AF_ADJUST_UP }, /* ả */ + { 0x1EA4, AF_ADJUST_UP2 }, /* Ấ */ + { 0x1EA5, AF_ADJUST_UP2 }, /* ấ */ + { 0x1EA6, AF_ADJUST_UP2 }, /* Ầ */ + { 0x1EA7, AF_ADJUST_UP2 }, /* ầ */ + { 0x1EA8, AF_ADJUST_UP2 }, /* Ẩ */ + { 0x1EA9, AF_ADJUST_UP2 }, /* ẩ */ + { 0x1EAA, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* Ẫ */ + { 0x1EAB, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ẫ */ + { 0x1EAC, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* Ậ */ + { 0x1EAD, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ậ */ + { 0x1EAE, AF_ADJUST_UP2 }, /* Ắ */ + { 0x1EAF, AF_ADJUST_UP2 }, /* ắ */ + + { 0x1EB0, AF_ADJUST_UP2 }, /* Ằ */ + { 0x1EB1, AF_ADJUST_UP2 }, /* ằ */ + { 0x1EB2, AF_ADJUST_UP2 }, /* Ẳ */ + { 0x1EB3, AF_ADJUST_UP2 }, /* ẳ */ + { 0x1EB4, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* Ẵ */ + { 0x1EB5, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ẵ */ + { 0x1EB6, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* Ặ */ + { 0x1EB7, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ặ */ + { 0x1EB8, AF_ADJUST_DOWN }, /* Ẹ */ + { 0x1EB9, AF_ADJUST_DOWN }, /* ẹ */ + { 0x1EBA, AF_ADJUST_UP }, /* Ẻ */ + { 0x1EBB, AF_ADJUST_UP }, /* ẻ */ + { 0x1EBC, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Ẽ */ + { 0x1EBD, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ẽ */ + { 0x1EBE, AF_ADJUST_UP2 }, /* Ế */ + { 0x1EBF, AF_ADJUST_UP2 }, /* ế */ + + { 0x1EC0, AF_ADJUST_UP2 }, /* Ề */ + { 0x1EC1, AF_ADJUST_UP2 }, /* ề */ + { 0x1EC2, AF_ADJUST_UP2 }, /* Ể */ + { 0x1EC3, AF_ADJUST_UP2 }, /* ể */ + { 0x1EC4, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* Ễ */ + { 0x1EC5, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ễ */ + { 0x1EC6, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* Ệ */ + { 0x1EC7, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ệ */ + { 0x1EC8, AF_ADJUST_UP }, /* Ỉ */ + { 0x1EC9, AF_ADJUST_UP }, /* ỉ */ + { 0x1ECA, AF_ADJUST_DOWN }, /* Ị */ + { 0x1ECB, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ị */ + { 0x1ECC, AF_ADJUST_DOWN }, /* Ọ */ + { 0x1ECD, AF_ADJUST_DOWN }, /* ọ */ + { 0x1ECE, AF_ADJUST_UP }, /* Ỏ */ + { 0x1ECF, AF_ADJUST_UP }, /* ỏ */ + + { 0x1ED0, AF_ADJUST_UP2 }, /* Ố */ + { 0x1ED1, AF_ADJUST_UP2 }, /* ố */ + { 0x1ED2, AF_ADJUST_UP2 }, /* Ồ */ + { 0x1ED3, AF_ADJUST_UP2 }, /* ồ */ + { 0x1ED4, AF_ADJUST_UP2 }, /* Ổ */ + { 0x1ED5, AF_ADJUST_UP2 }, /* ổ */ + { 0x1ED6, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* Ỗ */ + { 0x1ED7, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ỗ */ + { 0x1ED8, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* Ộ */ + { 0x1ED9, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ộ */ + { 0x1EDA, AF_ADJUST_UP | AF_IGNORE_CAPITAL_TOP }, /* Ớ */ + { 0x1EDB, AF_ADJUST_UP | AF_IGNORE_SMALL_TOP }, /* ớ */ + { 0x1EDC, AF_ADJUST_UP | AF_IGNORE_CAPITAL_TOP }, /* Ờ */ + { 0x1EDD, AF_ADJUST_UP | AF_IGNORE_SMALL_TOP }, /* ờ */ + { 0x1EDE, AF_ADJUST_UP | AF_IGNORE_CAPITAL_TOP }, /* Ở */ + { 0x1EDF, AF_ADJUST_UP | AF_IGNORE_SMALL_TOP }, /* ở */ + + { 0x1EE0, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP | AF_IGNORE_CAPITAL_TOP }, /* Ỡ */ + { 0x1EE1, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP | AF_IGNORE_SMALL_TOP }, /* ỡ */ + { 0x1EE2, AF_ADJUST_DOWN | AF_IGNORE_CAPITAL_TOP }, /* Ợ */ + { 0x1EE3, AF_ADJUST_DOWN | AF_IGNORE_SMALL_TOP }, /* ợ */ + { 0x1EE4, AF_ADJUST_DOWN }, /* Ụ */ + { 0x1EE5, AF_ADJUST_DOWN }, /* ụ */ + { 0x1EE6, AF_ADJUST_UP }, /* Ủ */ + { 0x1EE7, AF_ADJUST_UP }, /* ủ */ + { 0x1EE8, AF_ADJUST_UP | AF_IGNORE_CAPITAL_TOP }, /* Ứ */ + { 0x1EE9, AF_ADJUST_UP | AF_IGNORE_SMALL_TOP }, /* ứ */ + { 0x1EEA, AF_ADJUST_UP | AF_IGNORE_CAPITAL_TOP }, /* Ừ */ + { 0x1EEB, AF_ADJUST_UP | AF_IGNORE_SMALL_TOP }, /* ừ */ + { 0x1EEC, AF_ADJUST_UP | AF_IGNORE_CAPITAL_TOP }, /* Ử */ + { 0x1EED, AF_ADJUST_UP | AF_IGNORE_SMALL_TOP }, /* ử */ + { 0x1EEE, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP | AF_IGNORE_CAPITAL_TOP }, /* Ữ */ + { 0x1EEF, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP | AF_IGNORE_SMALL_TOP }, /* ữ */ + + { 0x1EF0, AF_ADJUST_DOWN | AF_IGNORE_CAPITAL_TOP }, /* Ự */ + { 0x1EF1, AF_ADJUST_DOWN | AF_IGNORE_SMALL_TOP }, /* ự */ + { 0x1EF2, AF_ADJUST_UP }, /* Ỳ */ + { 0x1EF3, AF_ADJUST_UP }, /* ỳ */ + { 0x1EF4, AF_ADJUST_DOWN }, /* Ỵ */ + { 0x1EF5, AF_ADJUST_DOWN }, /* ỵ */ + { 0x1EF6, AF_ADJUST_UP }, /* Ỷ */ + { 0x1EF7, AF_ADJUST_UP }, /* ỷ */ + { 0x1EF8, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* Ỹ */ + { 0x1EF9, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ỹ */ + + /* Greek Extended */ + { 0x1F00, AF_ADJUST_UP }, /* ἀ */ + { 0x1F01, AF_ADJUST_UP }, /* ἁ */ + { 0x1F02, AF_ADJUST_UP }, /* ἂ */ + { 0x1F03, AF_ADJUST_UP }, /* ἃ */ + { 0x1F04, AF_ADJUST_UP }, /* ἄ */ + { 0x1F05, AF_ADJUST_UP }, /* ἅ */ + { 0x1F06, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ἆ */ + { 0x1F07, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ἇ */ + + { 0x1F10, AF_ADJUST_UP }, /* ἐ */ + { 0x1F11, AF_ADJUST_UP }, /* ἑ */ + { 0x1F12, AF_ADJUST_UP }, /* ἒ */ + { 0x1F13, AF_ADJUST_UP }, /* ἓ */ + { 0x1F14, AF_ADJUST_UP }, /* ἔ */ + { 0x1F15, AF_ADJUST_UP }, /* ἕ */ + + { 0x1F20, AF_ADJUST_UP }, /* ἠ */ + { 0x1F21, AF_ADJUST_UP }, /* ἡ */ + { 0x1F22, AF_ADJUST_UP }, /* ἢ */ + { 0x1F23, AF_ADJUST_UP }, /* ἣ */ + { 0x1F24, AF_ADJUST_UP }, /* ἤ */ + { 0x1F25, AF_ADJUST_UP }, /* ἥ */ + { 0x1F26, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ἦ */ + { 0x1F27, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ἧ */ + + { 0x1F30, AF_ADJUST_UP }, /* ἰ */ + { 0x1F31, AF_ADJUST_UP }, /* ἱ */ + { 0x1F32, AF_ADJUST_UP }, /* ἲ */ + { 0x1F33, AF_ADJUST_UP }, /* ἳ */ + { 0x1F34, AF_ADJUST_UP }, /* ἴ */ + { 0x1F35, AF_ADJUST_UP }, /* ἵ */ + { 0x1F36, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ἶ */ + { 0x1F37, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ἷ */ + + { 0x1F40, AF_ADJUST_UP }, /* ὀ */ + { 0x1F41, AF_ADJUST_UP }, /* ὁ */ + { 0x1F42, AF_ADJUST_UP }, /* ὂ */ + { 0x1F43, AF_ADJUST_UP }, /* ὃ */ + { 0x1F44, AF_ADJUST_UP }, /* ὄ */ + { 0x1F45, AF_ADJUST_UP }, /* ὅ */ + + { 0x1F50, AF_ADJUST_UP }, /* ὐ */ + { 0x1F51, AF_ADJUST_UP }, /* ὑ */ + { 0x1F52, AF_ADJUST_UP }, /* ὒ */ + { 0x1F53, AF_ADJUST_UP }, /* ὓ */ + { 0x1F54, AF_ADJUST_UP }, /* ὔ */ + { 0x1F55, AF_ADJUST_UP }, /* ὕ */ + { 0x1F56, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ὖ */ + { 0x1F57, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ὗ */ + + { 0x1F60, AF_ADJUST_UP }, /* ὠ */ + { 0x1F61, AF_ADJUST_UP }, /* ὡ */ + { 0x1F62, AF_ADJUST_UP }, /* ὢ */ + { 0x1F63, AF_ADJUST_UP }, /* ὣ */ + { 0x1F64, AF_ADJUST_UP }, /* ὤ */ + { 0x1F65, AF_ADJUST_UP }, /* ὥ */ + { 0x1F66, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ὦ */ + { 0x1F67, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ὧ */ + + { 0x1F70, AF_ADJUST_UP }, /* ὰ */ + { 0x1F71, AF_ADJUST_UP }, /* ά */ + { 0x1F72, AF_ADJUST_UP }, /* ὲ */ + { 0x1F73, AF_ADJUST_UP }, /* έ */ + { 0x1F74, AF_ADJUST_UP }, /* ὴ */ + { 0x1F75, AF_ADJUST_UP }, /* ή */ + { 0x1F76, AF_ADJUST_UP }, /* ὶ */ + { 0x1F77, AF_ADJUST_UP }, /* ί */ + { 0x1F78, AF_ADJUST_UP }, /* ὸ */ + { 0x1F79, AF_ADJUST_UP }, /* ό */ + { 0x1F7A, AF_ADJUST_UP }, /* ὺ */ + { 0x1F7B, AF_ADJUST_UP }, /* ύ */ + { 0x1F7C, AF_ADJUST_UP }, /* ὼ */ + { 0x1F7D, AF_ADJUST_UP }, /* ώ */ + + { 0x1F80, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾀ */ + { 0x1F81, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾁ */ + { 0x1F82, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾂ */ + { 0x1F83, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾃ */ + { 0x1F84, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾄ */ + { 0x1F85, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾅ */ + { 0x1F86, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ᾆ */ + { 0x1F87, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ᾇ */ + { 0x1F88, AF_ADJUST_DOWN }, /* ᾈ */ + { 0x1F89, AF_ADJUST_DOWN }, /* ᾉ */ + { 0x1F8A, AF_ADJUST_DOWN }, /* ᾊ */ + { 0x1F8B, AF_ADJUST_DOWN }, /* ᾋ */ + { 0x1F8C, AF_ADJUST_DOWN }, /* ᾌ */ + { 0x1F8D, AF_ADJUST_DOWN }, /* ᾍ */ + { 0x1F8E, AF_ADJUST_DOWN }, /* ᾎ */ + { 0x1F8F, AF_ADJUST_DOWN }, /* ᾏ */ + + { 0x1F90, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾐ */ + { 0x1F91, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾑ */ + { 0x1F92, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾒ */ + { 0x1F93, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾓ */ + { 0x1F94, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾔ */ + { 0x1F95, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾕ */ + { 0x1F96, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ᾖ */ + { 0x1F97, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ᾗ */ + { 0x1F98, AF_ADJUST_DOWN }, /* ᾘ */ + { 0x1F99, AF_ADJUST_DOWN }, /* ᾙ */ + { 0x1F9A, AF_ADJUST_DOWN }, /* ᾚ */ + { 0x1F9B, AF_ADJUST_DOWN }, /* ᾛ */ + { 0x1F9C, AF_ADJUST_DOWN }, /* ᾜ */ + { 0x1F9D, AF_ADJUST_DOWN }, /* ᾝ */ + { 0x1F9E, AF_ADJUST_DOWN }, /* ᾞ */ + { 0x1F9F, AF_ADJUST_DOWN }, /* ᾟ */ + + { 0x1FA0, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾠ */ + { 0x1FA1, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾡ */ + { 0x1FA2, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾢ */ + { 0x1FA3, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾣ */ + { 0x1FA4, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾤ */ + { 0x1FA5, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾥ */ + { 0x1FA6, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ᾦ */ + { 0x1FA7, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ᾧ */ + { 0x1FA8, AF_ADJUST_DOWN }, /* ᾨ */ + { 0x1FA9, AF_ADJUST_DOWN }, /* ᾩ */ + { 0x1FAA, AF_ADJUST_DOWN }, /* ᾪ */ + { 0x1FAB, AF_ADJUST_DOWN }, /* ᾫ */ + { 0x1FAC, AF_ADJUST_DOWN }, /* ᾬ */ + { 0x1FAD, AF_ADJUST_DOWN }, /* ᾭ */ + { 0x1FAE, AF_ADJUST_DOWN }, /* ᾮ */ + { 0x1FAF, AF_ADJUST_DOWN }, /* ᾯ */ + + { 0x1FB0, AF_ADJUST_UP }, /* ᾰ */ + { 0x1FB1, AF_ADJUST_UP }, /* ᾱ */ + { 0x1FB2, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾲ */ + { 0x1FB3, AF_ADJUST_DOWN }, /* ᾳ */ + { 0x1FB4, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ᾴ */ + { 0x1FB6, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ᾶ */ + { 0x1FB7, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ᾷ */ + { 0x1FB8, AF_ADJUST_UP }, /* Ᾰ */ + { 0x1FB9, AF_ADJUST_UP }, /* Ᾱ */ + { 0x1FBC, AF_ADJUST_DOWN }, /* ᾼ */ + + { 0x1FC2, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ῂ */ + { 0x1FC3, AF_ADJUST_DOWN }, /* ῃ */ + { 0x1FC4, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ῄ */ + { 0x1FC6, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ῆ */ + { 0x1FC7, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ῇ */ + { 0x1FCC, AF_ADJUST_DOWN }, /* ῌ */ + + { 0x1FD0, AF_ADJUST_UP }, /* ῐ */ + { 0x1FD1, AF_ADJUST_UP }, /* ῑ */ + { 0x1FD2, AF_ADJUST_UP2 }, /* ῒ */ + { 0x1FD3, AF_ADJUST_UP2 }, /* ΐ */ + { 0x1FD6, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ῖ */ + { 0x1FD7, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ῗ */ + { 0x1FD8, AF_ADJUST_UP }, /* Ῐ */ + { 0x1FD9, AF_ADJUST_UP }, /* Ῑ */ + + { 0x1FE0, AF_ADJUST_UP }, /* ῠ */ + { 0x1FE1, AF_ADJUST_UP }, /* ῡ */ + { 0x1FE2, AF_ADJUST_UP2 }, /* ῢ */ + { 0x1FE3, AF_ADJUST_UP2 }, /* ΰ */ + { 0x1FE4, AF_ADJUST_UP }, /* ῤ */ + { 0x1FE5, AF_ADJUST_UP }, /* ῥ */ + { 0x1FE6, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ῦ */ + { 0x1FE7, AF_ADJUST_UP2 | AF_ADJUST_TILDE_TOP }, /* ῧ */ + { 0x1FE8, AF_ADJUST_UP }, /* Ῠ */ + { 0x1FE9, AF_ADJUST_UP }, /* Ῡ */ + { 0x1FF2, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ῲ */ + { 0x1FF3, AF_ADJUST_DOWN }, /* ῳ */ + { 0x1FF4, AF_ADJUST_UP | AF_ADJUST_DOWN }, /* ῴ */ + { 0x1FF6, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP }, /* ῶ */ + { 0x1FF7, AF_ADJUST_UP | AF_ADJUST_TILDE_TOP | AF_ADJUST_DOWN }, /* ῷ */ + { 0x1FFC, AF_ADJUST_DOWN }, /* ῼ */ + + /* General Punctuation */ + { 0x203C, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ‼ */ + { 0x203D, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ‽ */ + + { 0x2047, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ⁇ */ + { 0x2048, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ⁈ */ + { 0x2049, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ⁉ */ + + /* Superscripts and Subscripts */ + { 0x2071, AF_ADJUST_UP }, /* ⁱ */ + + /* Currency Symbols */ + { 0x20AB, AF_ADJUST_DOWN }, /* ₫ */ + + { 0x20C0, AF_ADJUST_DOWN }, /* ⃀ */ + + /* Number Forms */ + { 0x2170, AF_ADJUST_UP }, /* ⅰ */ + { 0x2171, AF_ADJUST_UP }, /* ⅱ */ + { 0x2172, AF_ADJUST_UP }, /* ⅲ */ + { 0x2173, AF_ADJUST_UP }, /* ⅳ */ + { 0x2175, AF_ADJUST_UP }, /* ⅵ */ + { 0x2176, AF_ADJUST_UP }, /* ⅶ */ + { 0x2177, AF_ADJUST_UP }, /* ⅷ */ + { 0x2178, AF_ADJUST_UP }, /* ⅸ */ + { 0x217A, AF_ADJUST_UP }, /* ⅺ */ + { 0x217B, AF_ADJUST_UP }, /* ⅻ */ + + /* Latin Extended-C */ + { 0x2C64, AF_IGNORE_CAPITAL_BOTTOM } , /* Ɽ */ + { 0x2C67, AF_IGNORE_CAPITAL_BOTTOM } , /* Ⱨ */ + { 0x2C68, AF_IGNORE_SMALL_BOTTOM } , /* ⱨ */ + { 0x2C69, AF_IGNORE_CAPITAL_BOTTOM } , /* Ⱪ */ + { 0x2C6A, AF_IGNORE_SMALL_BOTTOM } , /* ⱪ */ + { 0x2C6B, AF_IGNORE_CAPITAL_BOTTOM } , /* Ⱬ */ + { 0x2C6C, AF_IGNORE_SMALL_BOTTOM } , /* ⱬ */ + { 0x2C6E, AF_IGNORE_CAPITAL_BOTTOM } , /* Ɱ */ + + { 0x2C7C, AF_ADJUST_UP }, /* ⱼ */ + { 0x2C7E, AF_IGNORE_CAPITAL_BOTTOM } , /* Ȿ */ + { 0x2C7F, AF_IGNORE_CAPITAL_BOTTOM } , /* Ɀ */ + + /* Coptic */ + { 0x2CC2, AF_ADJUST_UP }, /* Ⳃ */ + { 0x2CC3, AF_ADJUST_UP }, /* ⳃ */ + + /* Supplemental Punctuation */ + { 0x2E18, AF_ADJUST_UP }, /* ⸘ */ + + { 0x2E2E, AF_ADJUST_UP | AF_ADJUST_NO_HEIGHT_CHECK }, /* ⸮ */ + + /* Cyrillic Extended-B */ + { 0xA640, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꙁ */ + { 0xA641, AF_IGNORE_SMALL_BOTTOM } , /* ꙁ */ + { 0xA642, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꙃ */ + { 0xA643, AF_IGNORE_SMALL_BOTTOM } , /* ꙃ */ + + { 0xA680, AF_IGNORE_CAPITAL_TOP } , /* Ꚁ */ + { 0xA681, AF_IGNORE_SMALL_TOP } , /* ꚁ */ + { 0xA688, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꚉ */ + { 0xA689, AF_IGNORE_SMALL_BOTTOM } , /* ꚉ */ + { 0xA68A, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꚋ */ + { 0xA68B, AF_IGNORE_SMALL_BOTTOM } , /* ꚋ */ + { 0xA68E, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꚏ */ + { 0xA68F, AF_IGNORE_SMALL_BOTTOM } , /* ꚏ */ + + { 0xA690, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꚑ */ + { 0xA691, AF_IGNORE_SMALL_BOTTOM } , /* ꚑ */ + { 0xA696, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꚗ */ + { 0xA697, AF_IGNORE_SMALL_BOTTOM } , /* ꚗ */ + + /* Latin Extended-D */ + { 0xA726, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꜧ */ + { 0xA727, AF_IGNORE_SMALL_BOTTOM } , /* ꜧ */ + + { 0xA756, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꝗ */ + { 0xA758, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꝙ */ + + { 0xA771, AF_IGNORE_SMALL_BOTTOM } , /* ꝱ */ + { 0xA772, AF_IGNORE_SMALL_BOTTOM } , /* ꝲ */ + { 0xA773, AF_IGNORE_SMALL_BOTTOM } , /* ꝳ */ + { 0xA774, AF_IGNORE_SMALL_BOTTOM } , /* ꝴ */ + { 0xA776, AF_IGNORE_SMALL_BOTTOM } , /* ꝶ */ + + { 0xA790, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꞑ */ + { 0xA791, AF_IGNORE_SMALL_BOTTOM } , /* ꞑ */ + { 0xA794, AF_IGNORE_SMALL_BOTTOM } , /* ꞔ */ + { 0xA795, AF_IGNORE_SMALL_BOTTOM } , /* ꞕ */ + + { 0xA7C0, AF_IGNORE_CAPITAL_TOP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ꟁ */ + { 0xA7C1, AF_IGNORE_SMALL_TOP | AF_IGNORE_SMALL_BOTTOM }, /* ꟁ */ + { 0xA7C4, AF_IGNORE_CAPITAL_BOTTOM } , /* Ꞔ */ + { 0xA7C5, AF_IGNORE_CAPITAL_BOTTOM } , /* Ʂ */ + { 0xA7C6, AF_IGNORE_CAPITAL_BOTTOM } , /* Ᶎ */ + { 0xA7CC, AF_IGNORE_CAPITAL_TOP | AF_IGNORE_CAPITAL_BOTTOM }, /* Ꟍ */ + { 0xA7CD, AF_IGNORE_SMALL_TOP | AF_IGNORE_SMALL_BOTTOM }, /* ꟍ */ + + /* Latin Extended-E */ + { 0xAB3C, AF_IGNORE_SMALL_BOTTOM } , /* ꬼ */ + + { 0xAB46, AF_IGNORE_SMALL_BOTTOM } , /* ꭆ */ + + { 0xAB5C, AF_IGNORE_SMALL_BOTTOM } , /* ꭜ */ + + { 0xAB66, AF_IGNORE_SMALL_BOTTOM } , /* ꭦ */ + { 0xAB67, AF_IGNORE_SMALL_BOTTOM } , /* ꭧ */ + }; + + + FT_LOCAL_DEF( FT_UInt32 ) + af_adjustment_database_lookup( FT_UInt32 codepoint ) + { + /* Binary search for database entry */ + FT_Offset low = 0; + FT_Offset high = AF_ADJUSTMENT_DATABASE_LENGTH - 1; + + + while ( high >= low ) + { + FT_Offset mid = ( low + high ) / 2; + FT_UInt32 mid_codepoint = adjustment_database[mid].codepoint; + + + if ( mid_codepoint < codepoint ) + low = mid + 1; + else if ( mid_codepoint > codepoint ) + high = mid - 1; + else + return adjustment_database[mid].flags; + } + + return 0; + } + + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + + static FT_Error + add_substitute( FT_Int glyph_idx, + size_t value, + FT_UInt32 codepoint, + FT_Hash reverse_map, + FT_Hash subst_map, + FT_Memory memory ) + { + FT_Error error; + + FT_Int first_substitute = (FT_Int)( value & 0xFFFF ); + + FT_UInt used = reverse_map->used; + + + /* + OpenType features like 'unic' map lowercase letter glyphs to uppercase + forms (and vice versa), which could lead to the use of wrong entries + in the adjustment database. For this reason we don't overwrite, + prioritizing cmap entries. + + XXX Note, however, that this cannot cover all cases since there might + be contradictory entries for glyphs not in the cmap. A possible + solution might be to specially mark pairs of related lowercase and + uppercase characters in the adjustment database that have diacritics + on different vertical sides (for example, U+0122 'Ģ' and U+0123 'ģ'). + The auto-hinter could then perform a topological analysis to do the + right thing. + */ + error = ft_hash_num_insert_no_overwrite( first_substitute, codepoint, + reverse_map, memory ); + if ( error ) + return error; + + if ( reverse_map->used > used ) + { + size_t* subst = ft_hash_num_lookup( first_substitute, subst_map ); + + + if ( subst ) + { + error = add_substitute( first_substitute, *subst, codepoint, + reverse_map, subst_map, memory ); + if ( error ) + return error; + } + } + + /* The remaining substitutes. */ + if ( value & 0xFFFF0000U ) + { + FT_UInt num_substitutes = value >> 16; + + FT_UInt i; + + + for ( i = 1; i <= num_substitutes; i++ ) + { + FT_Int idx = glyph_idx + (FT_Int)( i << 16 ); + size_t* substitute = ft_hash_num_lookup( idx, subst_map ); + + + used = reverse_map->used; + + error = ft_hash_num_insert_no_overwrite( *substitute, + codepoint, + reverse_map, + memory ); + if ( error ) + return error; + + if ( reverse_map->used > used ) + { + size_t* subst = ft_hash_num_lookup( *substitute, subst_map ); + + + if ( subst ) + { + error = add_substitute( *substitute, *subst, codepoint, + reverse_map, subst_map, memory ); + if ( error ) + return error; + } + } + } + } + + return FT_Err_Ok; + } + +#endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */ + + + /* Construct a 'reverse cmap' (i.e., a mapping from glyph indices to */ + /* character codes) for all glyphs that an input code point could turn */ + /* into. */ + /* */ + /* If HarfBuzz support is not available, this is the direct inversion */ + /* of the cmap table, otherwise the mapping gets extended with data */ + /* from the 'GSUB' table. */ + FT_LOCAL_DEF( FT_Error ) + af_reverse_character_map_new( FT_Hash *map, + AF_StyleMetrics metrics ) + { + FT_Error error; + + AF_FaceGlobals globals = metrics->globals; + FT_Face face = globals->face; + FT_Memory memory = face->memory; + + FT_CharMap old_charmap; + + FT_UInt32 codepoint; + FT_Offset i; + + + FT_TRACE4(( "af_reverse_character_map_new:" + " building reverse character map (style `%s')\n", + af_style_names[metrics->style_class->style] )); + + /* Search for a unicode charmap. */ + /* If there isn't one, create a blank map. */ + + /* Back up `face->charmap` because `find_unicode_charmap` sets it. */ + old_charmap = face->charmap; + + if ( ( error = find_unicode_charmap( face ) ) ) + goto Exit; + + *map = NULL; + if ( FT_QNEW( *map ) ) + goto Exit; + + error = ft_hash_num_init( *map, memory ); + if ( error ) + goto Exit; + + /* Initialize reverse cmap with data directly from the cmap table. */ + for ( i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ ) + { + FT_Int cmap_glyph; + + + /* + We cannot restrict `codepoint` to character ranges; we have no + control what data the script-specific portion of the GSUB table + actually holds. + + An example is `arial.ttf` version 7.00; in this font, there are + lookups for Cyrillic (lookup 43), Greek (lookup 44), and Latin + (lookup 45) that map capital letter glyphs to small capital glyphs. + It is tempting to expect that script-specific versions of the 'c2sc' + feature only use script-specific lookups. However, this is not the + case in this font: the feature uses all three lookups regardless of + the script. + + The auto-hinter, while assigning glyphs to styles, uses the first + coverage result it encounters for a particular glyph. For example, + if the coverage for Cyrillic is tested before Latin (as is currently + the case), glyphs without a cmap entry that are covered in 'c2sc' + are treated as Cyrillic. + + If we now look at glyph 3498, which is a small-caps version of the + Latin character 'A grave' (U+00C0, glyph 172), we can see that it is + registered as belonging to a Cyrillic style due to the algorithm + just described. As a result, checking only for characters from the + Latin range would miss this glyph; we thus have to test all + character codes in the database. + */ + codepoint = adjustment_database[i].codepoint; + + cmap_glyph = (FT_Int)FT_Get_Char_Index( face, codepoint ); + if ( cmap_glyph == 0 ) + continue; + + error = ft_hash_num_insert( cmap_glyph, codepoint, *map, memory ); + if ( error ) + goto Exit; + } + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + + if ( ft_hb_enabled( globals ) ) + { + hb_font_t *hb_font; + hb_face_t *hb_face; + + hb_set_t *gsub_lookups; + hb_script_t script; + + unsigned int script_count = 1; + hb_tag_t script_tags[2] = { HB_TAG_NONE, HB_TAG_NONE }; + + FT_Hash subst_map = NULL; + + hb_codepoint_t idx; + FT_UInt hash_idx; + FT_Int glyph_idx; + size_t value; + + + /* No need to check whether HarfBuzz has allocation issues; */ + /* it continues to work in such cases and simply returns */ + /* 'empty' objects that do nothing. */ + + hb_font = globals->hb_font; + hb_face = hb( font_get_face )( hb_font ); + + gsub_lookups = hb( set_create )(); + + script = af_hb_scripts[metrics->style_class->script]; + + hb( ot_tags_from_script_and_language )( script, NULL, + &script_count, script_tags, + NULL, NULL ); + + /* Compute set of all script-specific GSUB lookups. */ + hb( ot_layout_collect_lookups )( hb_face, + HB_OT_TAG_GSUB, + script_tags, NULL, NULL, + gsub_lookups ); + +#ifdef FT_DEBUG_LEVEL_TRACE + { + FT_Bool have_idx = FALSE; + + + FT_TRACE4(( " GSUB lookups to check:\n" )); + + FT_TRACE4(( " " )); + idx = HB_SET_VALUE_INVALID; + while ( hb( set_next )( gsub_lookups, &idx ) ) + if ( globals->gsub_lookups_single_alternate[idx] ) + { + have_idx = TRUE; + FT_TRACE4(( " %u", idx )); + } + if ( !have_idx ) + FT_TRACE4(( " (none)" )); + FT_TRACE4(( "\n" )); + + FT_TRACE4(( "\n" )); + } +#endif + + if ( FT_QNEW( subst_map ) ) + goto Exit_HarfBuzz; + + error = ft_hash_num_init( subst_map, memory ); + if ( error ) + goto Exit_HarfBuzz; + + idx = HB_SET_VALUE_INVALID; + while ( hb( set_next )( gsub_lookups, &idx ) ) + { + FT_UInt32 offset = globals->gsub_lookups_single_alternate[idx]; + + + /* Put all substitutions into a single hash table. Note that */ + /* the hash values usually contain more than a single character */ + /* code; this can happen if different 'SingleSubst' subtables */ + /* map a given glyph index to different substitutions, or if */ + /* 'AlternateSubst' subtable entries are present. */ + if ( offset ) + af_map_lookup( globals, subst_map, offset ); + } + + /* + Now iterate over the collected substitution data in `subst_map` + (using recursion to resolve one-to-many mappings) and insert the + data into the reverse cmap. + + As an example, suppose we have the following cmap and substitution + data: + + cmap: X -> a + Y -> b + Z -> c + + substitutions: a -> b + b -> c, d + d -> e + + The reverse map now becomes as follows. + + a -> X + b -> Y + c -> Z (via cmap, ignoring mapping from 'b') + d -> Y (via 'b') + e -> Y (via 'b' and 'd') + */ + + hash_idx = 0; + while ( ft_hash_num_iterator( &hash_idx, + &glyph_idx, + &value, + subst_map ) ) + { + size_t* val; + + + /* Ignore keys that do not point to the first substitute. */ + if ( (FT_UInt)glyph_idx & 0xFFFF0000U ) + continue; + + /* Ignore glyph indices that are not related to accents. */ + val = ft_hash_num_lookup( glyph_idx, *map ); + if ( !val ) + continue; + + codepoint = *val; + + error = add_substitute( glyph_idx, value, codepoint, + *map, subst_map, memory ); + if ( error ) + break; + } + + Exit_HarfBuzz: + hb( set_destroy )( gsub_lookups ); + + ft_hash_num_free( subst_map, memory ); + FT_FREE( subst_map ); + + if ( error ) + goto Exit; + } + +#endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */ + + FT_TRACE4(( " reverse character map built successfully" + " with %u entries\n", ( *map )->used )); + +#ifdef FT_DEBUG_LEVEL_TRACE + + { + FT_UInt cnt; + + + FT_TRACE7(( " gidx code flags\n" )); + /* " XXXXX 0xXXXX XXXXXXXXXXX..." */ + FT_TRACE7(( " ------------------------------\n" )); + + for ( cnt = 0; cnt < globals->glyph_count; cnt++ ) + { + size_t* val; + FT_UInt32 adj_type; + + const char* flag_names[] = + { + "up", /* AF_ADJUST_UP */ + "down", /* AF_ADJUST_DOWN */ + "double up", /* AF_ADJUST_UP2 */ + "double down", /* AF_ADJUST_DOWN2 */ + + "top tilde", /* AF_ADJUST_TILDE_TOP */ + "bottom tilde", /* AF_ADJUST_TILDE_BOTTOM */ + "below-top tilde", /* AF_ADJUST_TILDE_TOP2 */ + "above-bottom tilde", /* AF_ADJUST_TILDE_BOTTOM2 */ + + "ignore capital top", /* AF_IGNORE_CAPITAL_TOP */ + "ignore capital bottom", /* AF_IGNORE_CAPITAL_BOTTOM */ + "ignore small top", /* AF_IGNORE_SMALL_TOP */ + "ignore small bottom", /* AF_IGNORE_SMALL_BOTTOM */ + }; + size_t flag_names_size = sizeof ( flag_names ) / sizeof ( char* ); + + char flag_str[256]; + int need_comma; + + size_t j; + + + val = ft_hash_num_lookup( (FT_Int)cnt, *map ); + if ( !val ) + continue; + codepoint = *val; + + adj_type = af_adjustment_database_lookup( codepoint ); + if ( !adj_type ) + continue; + + flag_str[0] = '\0'; + need_comma = 0; + + for ( j = 0; j < flag_names_size; j++ ) + { + if ( adj_type & (1 << j ) ) + { + if ( !need_comma ) + need_comma = 1; + else + strcat( flag_str, ", " ); + strcat( flag_str, flag_names[j] ); + } + } + + FT_TRACE7(( " %5u 0x%04X %s\n", cnt, codepoint, flag_str )); + } + } + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + + Exit: + face->charmap = old_charmap; + + if ( error ) + { + FT_TRACE4(( " error while building reverse character map." + " Using blank map.\n" )); + + if ( *map ) + ft_hash_num_free( *map, memory ); + + FT_FREE( *map ); + *map = NULL; + return error; + } + + return FT_Err_Ok; + } + + + FT_LOCAL_DEF( FT_Error ) + af_reverse_character_map_done( FT_Hash map, + FT_Memory memory ) + { + if ( map ) + ft_hash_num_free( map, memory ); + FT_FREE( map ); + + return FT_Err_Ok; + } + + +/* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afadjust.h b/src/java.desktop/share/native/libfreetype/src/autofit/afadjust.h new file mode 100644 index 00000000000..4837451ae4c --- /dev/null +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afadjust.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * + * afadjust.h + * + * Auto-fitter routines to adjust components based on charcode (header). + * + * Copyright (C) 2023-2025 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * Written by Craig White . + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + + +#ifndef AFADJUST_H_ +#define AFADJUST_H_ + +#include + +#include "afglobal.h" +#include "aftypes.h" + + +FT_BEGIN_HEADER + + /* + * Adjustment type flags. + * + * They also specify topological constraints that the auto-hinter relies + * on. For example, using `AF_ADJUST_UP` implies that we have two + * enclosing contours, one for the base glyph and one for the diacritic + * above, and no other contour inbetween or above. With 'enclosing' it is + * meant that such a contour can contain more inner contours. + * + */ + + /* Find the topmost contour and push it up until its lowest point is */ + /* one pixel above the highest point not enclosed by that contour. */ +#define AF_ADJUST_UP 0x01 + + /* Find the bottommost contour and push it down until its highest point */ + /* is one pixel below the lowest point not enclosed by that contour. */ +#define AF_ADJUST_DOWN 0x02 + + /* Find the contour below the topmost contour and push it up, together */ + /* with the topmost contour, until its lowest point is one pixel above */ + /* the highest point not enclosed by that contour. This flag is */ + /* mutually exclusive with `AF_ADJUST_UP`. */ +#define AF_ADJUST_UP2 0x04 + + /* Find the contour above the bottommost contour and push it down, */ + /* together with the bottommost contour, until its highest point is */ + /* one pixel below the lowest point not enclosed by that contour. */ + /* This flag is mutually exclusive with `AF_ADJUST_DOWN`. */ +#define AF_ADJUST_DOWN2 0x08 + + /* The topmost contour is a tilde. Enlarge it vertically so that it */ + /* stays legible at small sizes, not degenerating to a horizontal line. */ +#define AF_ADJUST_TILDE_TOP 0x10 + + /* The bottommost contour is a tilde. Enlarge it vertically so that it */ + /* stays legible at small sizes, not degenerating to a horizontal line. */ +#define AF_ADJUST_TILDE_BOTTOM 0x20 + + /* The contour below the topmost contour is a tilde. Enlarge it */ + /* vertically so that it stays legible at small sizes, not degenerating */ + /* to a horizontal line. To be used with `AF_ADJUST_UP2` only. */ +#define AF_ADJUST_TILDE_TOP2 0x40 + + /* The contour above the bottommost contour is a tilde. Enlarge it */ + /* vertically so that it stays legible at small sizes, not degenerating */ + /* to a horizontal line. To be used with `AF_ADJUST_DOWN2` only. */ +#define AF_ADJUST_TILDE_BOTTOM2 0x80 + + /* Make the auto-hinter ignore any diacritic (either a separate contour */ + /* or part of the base character outline) that is attached to the top */ + /* of an uppercase base character. */ +#define AF_IGNORE_CAPITAL_TOP 0x100 + + /* Make the auto-hinter ignore any diacritic (either a separate contour */ + /* or part of the base character outline) that is attached to the */ + /* bottom of an uppercase base character. */ +#define AF_IGNORE_CAPITAL_BOTTOM 0x200 + + /* Make the auto-hinter ignore any diacritic (either a separate contour */ + /* or part of the base character outline) that is attached to the top */ + /* of a lowercase base character. */ +#define AF_IGNORE_SMALL_TOP 0x400 + + /* Make the auto-hinter ignore any diacritic (either a separate contour */ + /* or part of the base character outline) that is attached to the */ + /* bottom of a lowercase base character. */ +#define AF_IGNORE_SMALL_BOTTOM 0x800 + + /* By default, the AF_ADJUST_XXX flags are applied only if diacritics */ + /* have a 'small' height (based on some heuristic checks). If this */ + /* flag is set, no such check is performed. */ +#define AF_ADJUST_NO_HEIGHT_CHECK 0x1000 + + /* No adjustment, i.e., no flag is set. */ +#define AF_ADJUST_NONE 0x00 + + + FT_LOCAL( FT_UInt32 ) + af_adjustment_database_lookup( FT_UInt32 codepoint ); + + /* Allocate and populate the reverse character map, */ + /* using the character map within the face. */ + FT_LOCAL( FT_Error ) + af_reverse_character_map_new( FT_Hash *map, + AF_StyleMetrics metrics ); + + /* Free the reverse character map. */ + FT_LOCAL( FT_Error ) + af_reverse_character_map_done( FT_Hash map, + FT_Memory memory ); + + +FT_END_HEADER + +#endif /* AFADJUST_H_ */ + + +/* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c index ea83969cdc9..a6219bdfe41 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.c @@ -7,7 +7,7 @@ * * Auto-fitter data for blue strings (body). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -467,24 +467,24 @@ af_blue_stringsets[] = { /* */ - { AF_BLUE_STRING_ADLAM_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_ADLAM_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_ADLAM_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_ADLAM_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, { AF_BLUE_STRING_ADLAM_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_ADLAM_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_ADLAM_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_ARABIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_ARABIC_BOTTOM, 0 }, { AF_BLUE_STRING_ARABIC_JOIN, AF_BLUE_PROPERTY_LATIN_NEUTRAL }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_ARMENIAN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_ARMENIAN_CAPITAL_BOTTOM, 0 }, - { AF_BLUE_STRING_ARMENIAN_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_ARMENIAN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_ARMENIAN_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, + { AF_BLUE_STRING_ARMENIAN_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_ARMENIAN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_ARMENIAN_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_ARMENIAN_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_ARMENIAN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_ARMENIAN_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_AVESTAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_AVESTAN_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, @@ -508,14 +508,14 @@ { AF_BLUE_STRING_CHAKMA_BOTTOM, 0 }, { AF_BLUE_STRING_CHAKMA_DESCENDER, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_CANADIAN_SYLLABICS_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_CANADIAN_SYLLABICS_BOTTOM, 0 }, + { AF_BLUE_STRING_CANADIAN_SYLLABICS_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_CANADIAN_SYLLABICS_BOTTOM, 0 }, { AF_BLUE_STRING_CANADIAN_SYLLABICS_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_CANADIAN_SYLLABICS_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_BOTTOM, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_CANADIAN_SYLLABICS_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_BOTTOM, 0 }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_CARIAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_CARIAN_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, @@ -527,24 +527,24 @@ { AF_BLUE_STRING_CHEROKEE_SMALL, 0 }, { AF_BLUE_STRING_CHEROKEE_SMALL_DESCENDER, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_COPTIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_COPTIC_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_COPTIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_COPTIC_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, { AF_BLUE_STRING_COPTIC_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_COPTIC_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_COPTIC_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_CYPRIOT_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_CYPRIOT_BOTTOM, 0 }, { AF_BLUE_STRING_CYPRIOT_SMALL, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_CYPRIOT_SMALL, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, { AF_BLUE_STRING_CYRILLIC_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_CYRILLIC_SMALL, 0 }, - { AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_CYRILLIC_SMALL, 0 }, + { AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_DEVANAGARI_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_DEVANAGARI_HEAD, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_DEVANAGARI_BASE, AF_BLUE_PROPERTY_LATIN_TOP | @@ -553,12 +553,12 @@ { AF_BLUE_STRING_DEVANAGARI_BASE, 0 }, { AF_BLUE_STRING_DEVANAGARI_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_DESERET_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_DESERET_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_DESERET_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_DESERET_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, { AF_BLUE_STRING_DESERET_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_DESERET_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_DESERET_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_ETHIOPIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_ETHIOPIC_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, @@ -578,23 +578,23 @@ { AF_BLUE_STRING_GEORGIAN_NUSKHURI_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_GEORGIAN_NUSKHURI_DESCENDER, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_BOTTOM, 0 }, + { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, { AF_BLUE_STRING_GLAGOLITIC_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_GLAGOLITIC_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_GLAGOLITIC_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_GOTHIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_GOTHIC_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_GREEK_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM, 0 }, - { AF_BLUE_STRING_GREEK_SMALL_BETA_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_GREEK_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, + { AF_BLUE_STRING_GREEK_SMALL_BETA_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_GREEK_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_GREEK_SMALL, 0 }, - { AF_BLUE_STRING_GREEK_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_GREEK_SMALL, 0 }, + { AF_BLUE_STRING_GREEK_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_GUJARATI_TOP, AF_BLUE_PROPERTY_LATIN_TOP | AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, { AF_BLUE_STRING_GUJARATI_BOTTOM, 0 }, @@ -643,45 +643,45 @@ { AF_BLUE_STRING_LAO_LARGE_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_LAO_DESCENDER, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_LATIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM, 0 }, - { AF_BLUE_STRING_LATIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, + { AF_BLUE_STRING_LATIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_LATIN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_LATIN_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_LATIN_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM, 0 }, - { AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_LATIN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_LATIN_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, + { AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_LATIN_SUBS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_LATIN_SUBS_SMALL, 0 }, - { AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM, 0 }, - { AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_LATIN_SUBS_SMALL, 0 }, + { AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, + { AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_LATIN_SUPS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_LATIN_SUPS_SMALL, 0 }, - { AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_LATIN_SUPS_SMALL, 0 }, + { AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_LISU_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_LISU_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_MALAYALAM_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_MALAYALAM_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_BOTTOM, 0 }, - { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, + { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MEDEFAIDRIN_DIGIT_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MEDEFAIDRIN_DIGIT_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_MONGOLIAN_TOP_BASE, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_MONGOLIAN_BOTTOM_BASE, 0 }, { AF_BLUE_STRING_MAX, 0 }, @@ -691,12 +691,12 @@ { AF_BLUE_STRING_MYANMAR_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_MYANMAR_DESCENDER, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_NKO_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_NKO_BOTTOM, 0 }, + { AF_BLUE_STRING_NKO_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_NKO_BOTTOM, 0 }, { AF_BLUE_STRING_NKO_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_NKO_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_NKO_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_OL_CHIKI, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_OL_CHIKI, 0 }, @@ -704,15 +704,15 @@ { AF_BLUE_STRING_OLD_TURKIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_OLD_TURKIC_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_OSAGE_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_OSAGE_CAPITAL_BOTTOM, 0 }, - { AF_BLUE_STRING_OSAGE_CAPITAL_DESCENDER, 0 }, - { AF_BLUE_STRING_OSAGE_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_OSAGE_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_OSAGE_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_OSAGE_SMALL_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_OSAGE_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_OSAGE_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM }, + { AF_BLUE_STRING_OSAGE_CAPITAL_DESCENDER, 0 }, + { AF_BLUE_STRING_OSAGE_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_OSAGE_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_OSAGE_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_OSAGE_SMALL_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_OSMANYA_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_OSMANYA_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, @@ -723,13 +723,13 @@ { AF_BLUE_STRING_SAURASHTRA_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_SAURASHTRA_BOTTOM, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_SHAVIAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_SHAVIAN_BOTTOM, 0 }, - { AF_BLUE_STRING_SHAVIAN_DESCENDER, 0 }, + { AF_BLUE_STRING_SHAVIAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, + { AF_BLUE_STRING_SHAVIAN_BOTTOM, 0 }, + { AF_BLUE_STRING_SHAVIAN_DESCENDER, 0 }, { AF_BLUE_STRING_SHAVIAN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, - { AF_BLUE_STRING_SHAVIAN_SMALL_BOTTOM, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + AF_BLUE_PROPERTY_LATIN_X_HEIGHT }, + { AF_BLUE_STRING_SHAVIAN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM }, + { AF_BLUE_STRING_MAX, 0 }, { AF_BLUE_STRING_SINHALA_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, { AF_BLUE_STRING_SINHALA_BOTTOM, 0 }, { AF_BLUE_STRING_SINHALA_DESCENDER, 0 }, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin index d2270fac744..786c6b3b9e6 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.cin @@ -4,7 +4,7 @@ * * Auto-fitter data for blue strings (body). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat index 88bab2632ab..f6e96ff8189 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.dat @@ -2,7 +2,7 @@ // // Auto-fitter data for blue strings. // -// Copyright (C) 2013-2024 by +// Copyright (C) 2013-2025 by // David Turner, Robert Wilhelm, and Werner Lemberg. // // This file is part of the FreeType project, and may only be used, @@ -699,12 +699,12 @@ AF_BLUE_STRING_ENUM AF_BLUE_STRINGS_ARRAY AF_BLUE_STRING_MAX_LEN: AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: AF_BLUE_STRINGSET_ADLM - { AF_BLUE_STRING_ADLAM_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_ADLAM_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_ADLAM_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_ADLAM_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } { AF_BLUE_STRING_ADLAM_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_ADLAM_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_ADLAM_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_ARAB { AF_BLUE_STRING_ARABIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -713,14 +713,14 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_ARMN - { AF_BLUE_STRING_ARMENIAN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_ARMENIAN_CAPITAL_BOTTOM, 0 } - { AF_BLUE_STRING_ARMENIAN_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_ARMENIAN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_ARMENIAN_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } + { AF_BLUE_STRING_ARMENIAN_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } { AF_BLUE_STRING_ARMENIAN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_ARMENIAN_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_ARMENIAN_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_ARMENIAN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_ARMENIAN_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_AVST { AF_BLUE_STRING_AVESTAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -756,14 +756,14 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_CANS - { AF_BLUE_STRING_CANADIAN_SYLLABICS_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_CANADIAN_SYLLABICS_BOTTOM, 0 } + { AF_BLUE_STRING_CANADIAN_SYLLABICS_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_CANADIAN_SYLLABICS_BOTTOM, 0 } { AF_BLUE_STRING_CANADIAN_SYLLABICS_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_CANADIAN_SYLLABICS_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_BOTTOM, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_CANADIAN_SYLLABICS_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_CANADIAN_SYLLABICS_SUPS_BOTTOM, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_CARI { AF_BLUE_STRING_CARIAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -781,12 +781,12 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_COPT - { AF_BLUE_STRING_COPTIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_COPTIC_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_COPTIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_COPTIC_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } { AF_BLUE_STRING_COPTIC_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_COPTIC_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_COPTIC_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_CPRT { AF_BLUE_STRING_CYPRIOT_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -796,13 +796,13 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_CYRL - { AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_CYRILLIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_CYRILLIC_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } { AF_BLUE_STRING_CYRILLIC_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_CYRILLIC_SMALL, 0 } - { AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_CYRILLIC_SMALL, 0 } + { AF_BLUE_STRING_CYRILLIC_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_DEVA { AF_BLUE_STRING_DEVANAGARI_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -815,12 +815,12 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_DSRT - { AF_BLUE_STRING_DESERET_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_DESERET_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_DESERET_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_DESERET_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } { AF_BLUE_STRING_DESERET_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_DESERET_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_DESERET_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_ETHI { AF_BLUE_STRING_ETHIOPIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -848,12 +848,12 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_GLAG - { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_BOTTOM, 0 } + { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_GLAGOLITIC_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } { AF_BLUE_STRING_GLAGOLITIC_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_GLAGOLITIC_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_GLAGOLITIC_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_GOTH { AF_BLUE_STRING_GOTHIC_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -861,14 +861,14 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_GREK - { AF_BLUE_STRING_GREEK_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM, 0 } - { AF_BLUE_STRING_GREEK_SMALL_BETA_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_GREEK_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_GREEK_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } + { AF_BLUE_STRING_GREEK_SMALL_BETA_TOP, AF_BLUE_PROPERTY_LATIN_TOP } { AF_BLUE_STRING_GREEK_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_GREEK_SMALL, 0 } - { AF_BLUE_STRING_GREEK_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_GREEK_SMALL, 0 } + { AF_BLUE_STRING_GREEK_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_GUJR { AF_BLUE_STRING_GUJARATI_TOP, AF_BLUE_PROPERTY_LATIN_TOP | @@ -935,34 +935,34 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_LATN - { AF_BLUE_STRING_LATIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM, 0 } - { AF_BLUE_STRING_LATIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } + { AF_BLUE_STRING_LATIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } { AF_BLUE_STRING_LATIN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_LATIN_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_LATIN_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_LATIN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_LATIN_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_LATB - { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM, 0 } - { AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUBS_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } + { AF_BLUE_STRING_LATIN_SUBS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } { AF_BLUE_STRING_LATIN_SUBS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_LATIN_SUBS_SMALL, 0 } - { AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_LATIN_SUBS_SMALL, 0 } + { AF_BLUE_STRING_LATIN_SUBS_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_LATP - { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM, 0 } - { AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_LATIN_SUPS_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } + { AF_BLUE_STRING_LATIN_SUPS_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } { AF_BLUE_STRING_LATIN_SUPS_SMALL, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_LATIN_SUPS_SMALL, 0 } - { AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_LATIN_SUPS_SMALL, 0 } + { AF_BLUE_STRING_LATIN_SUPS_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_LISU { AF_BLUE_STRING_LISU_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -975,15 +975,15 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_MEDF - { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_BOTTOM, 0 } - { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_MEDEFAIDRIN_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } + { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_F_TOP, AF_BLUE_PROPERTY_LATIN_TOP } { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MEDEFAIDRIN_DIGIT_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_MEDEFAIDRIN_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MEDEFAIDRIN_DIGIT_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_MONG { AF_BLUE_STRING_MONGOLIAN_TOP_BASE, AF_BLUE_PROPERTY_LATIN_TOP } @@ -999,12 +999,12 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_NKOO - { AF_BLUE_STRING_NKO_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_NKO_BOTTOM, 0 } + { AF_BLUE_STRING_NKO_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_NKO_BOTTOM, 0 } { AF_BLUE_STRING_NKO_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_NKO_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_NKO_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_NONE { AF_BLUE_STRING_MAX, 0 } @@ -1020,15 +1020,15 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_OSGE - { AF_BLUE_STRING_OSAGE_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_OSAGE_CAPITAL_BOTTOM, 0 } - { AF_BLUE_STRING_OSAGE_CAPITAL_DESCENDER, 0 } - { AF_BLUE_STRING_OSAGE_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_OSAGE_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_OSAGE_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_OSAGE_SMALL_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + { AF_BLUE_STRING_OSAGE_CAPITAL_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_OSAGE_CAPITAL_BOTTOM, AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM } + { AF_BLUE_STRING_OSAGE_CAPITAL_DESCENDER, 0 } + { AF_BLUE_STRING_OSAGE_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_OSAGE_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_OSAGE_SMALL_ASCENDER, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_OSAGE_SMALL_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_OSMA { AF_BLUE_STRING_OSMANYA_TOP, AF_BLUE_PROPERTY_LATIN_TOP } @@ -1047,13 +1047,13 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_SHAW - { AF_BLUE_STRING_SHAVIAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_SHAVIAN_BOTTOM, 0 } - { AF_BLUE_STRING_SHAVIAN_DESCENDER, 0 } + { AF_BLUE_STRING_SHAVIAN_TOP, AF_BLUE_PROPERTY_LATIN_TOP } + { AF_BLUE_STRING_SHAVIAN_BOTTOM, 0 } + { AF_BLUE_STRING_SHAVIAN_DESCENDER, 0 } { AF_BLUE_STRING_SHAVIAN_SMALL_TOP, AF_BLUE_PROPERTY_LATIN_TOP | - AF_BLUE_PROPERTY_LATIN_X_HEIGHT } - { AF_BLUE_STRING_SHAVIAN_SMALL_BOTTOM, 0 } - { AF_BLUE_STRING_MAX, 0 } + AF_BLUE_PROPERTY_LATIN_X_HEIGHT } + { AF_BLUE_STRING_SHAVIAN_SMALL_BOTTOM, AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM } + { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_SINH { AF_BLUE_STRING_SINHALA_TOP, AF_BLUE_PROPERTY_LATIN_TOP } diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h index 2aa9d0984ef..5bb8406dc2b 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.h @@ -7,7 +7,7 @@ * * Auto-fitter data for blue strings (specification). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -314,14 +314,17 @@ FT_BEGIN_HEADER /* Properties are specific to a writing system. We assume that a given */ /* blue string can't be used in more than a single writing system, which */ /* is a safe bet. */ -#define AF_BLUE_PROPERTY_LATIN_TOP ( 1U << 0 ) /* must have value 1 */ +#define AF_BLUE_PROPERTY_LATIN_TOP ( 1U << 0 ) /* must be value 1 */ #define AF_BLUE_PROPERTY_LATIN_SUB_TOP ( 1U << 1 ) #define AF_BLUE_PROPERTY_LATIN_NEUTRAL ( 1U << 2 ) #define AF_BLUE_PROPERTY_LATIN_X_HEIGHT ( 1U << 3 ) #define AF_BLUE_PROPERTY_LATIN_LONG ( 1U << 4 ) -#define AF_BLUE_PROPERTY_CJK_TOP ( 1U << 0 ) /* must have value 1 */ -#define AF_BLUE_PROPERTY_CJK_HORIZ ( 1U << 1 ) /* must have value 2 */ +#define AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM ( 1U << 5 ) +#define AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM ( 1U << 6 ) + +#define AF_BLUE_PROPERTY_CJK_TOP ( 1U << 0 ) /* must be value 1 */ +#define AF_BLUE_PROPERTY_CJK_HORIZ ( 1U << 1 ) /* must be value 2 */ #define AF_BLUE_PROPERTY_CJK_RIGHT AF_BLUE_PROPERTY_CJK_TOP diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin index 38031505a85..dbac14548d5 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afblue.hin @@ -4,7 +4,7 @@ * * Auto-fitter data for blue strings (specification). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -99,14 +99,17 @@ FT_BEGIN_HEADER /* Properties are specific to a writing system. We assume that a given */ /* blue string can't be used in more than a single writing system, which */ /* is a safe bet. */ -#define AF_BLUE_PROPERTY_LATIN_TOP ( 1U << 0 ) /* must have value 1 */ +#define AF_BLUE_PROPERTY_LATIN_TOP ( 1U << 0 ) /* must be value 1 */ #define AF_BLUE_PROPERTY_LATIN_SUB_TOP ( 1U << 1 ) #define AF_BLUE_PROPERTY_LATIN_NEUTRAL ( 1U << 2 ) #define AF_BLUE_PROPERTY_LATIN_X_HEIGHT ( 1U << 3 ) #define AF_BLUE_PROPERTY_LATIN_LONG ( 1U << 4 ) -#define AF_BLUE_PROPERTY_CJK_TOP ( 1U << 0 ) /* must have value 1 */ -#define AF_BLUE_PROPERTY_CJK_HORIZ ( 1U << 1 ) /* must have value 2 */ +#define AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM ( 1U << 5 ) +#define AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM ( 1U << 6 ) + +#define AF_BLUE_PROPERTY_CJK_TOP ( 1U << 0 ) /* must be value 1 */ +#define AF_BLUE_PROPERTY_CJK_HORIZ ( 1U << 1 ) /* must be value 2 */ #define AF_BLUE_PROPERTY_CJK_RIGHT AF_BLUE_PROPERTY_CJK_TOP diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c index 869b60487c2..7086601838c 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.c @@ -4,7 +4,7 @@ * * Auto-fitter hinting routines for CJK writing system (body). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -90,12 +90,8 @@ /* If HarfBuzz is not available, we need a pointer to a single */ /* unsigned long value. */ -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - void* shaper_buf; -#else FT_ULong shaper_buf_; void* shaper_buf = &shaper_buf_; -#endif const char* p; @@ -105,9 +101,8 @@ p = script_class->standard_charstring; -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - shaper_buf = af_shaper_buf_create( face ); -#endif + if ( ft_hb_enabled( metrics->root.globals ) ) + shaper_buf = af_shaper_buf_create( metrics->root.globals ); /* We check a list of standard characters. The first match wins. */ @@ -144,7 +139,7 @@ break; } - af_shaper_buf_destroy( face, shaper_buf ); + af_shaper_buf_destroy( metrics->root.globals, shaper_buf ); if ( !glyph_index ) goto Exit; @@ -152,7 +147,7 @@ if ( !glyph_index ) goto Exit; - FT_TRACE5(( "standard character: U+%04lX (glyph index %ld)\n", + FT_TRACE5(( "standard character: U+%04lX (glyph index %lu)\n", ch, glyph_index )); error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); @@ -297,12 +292,8 @@ /* If HarfBuzz is not available, we need a pointer to a single */ /* unsigned long value. */ -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - void* shaper_buf; -#else FT_ULong shaper_buf_; void* shaper_buf = &shaper_buf_; -#endif /* we walk over the blue character strings as specified in the */ @@ -313,9 +304,8 @@ FT_TRACE5(( "==========================\n" )); FT_TRACE5(( "\n" )); -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - shaper_buf = af_shaper_buf_create( face ); -#endif + if ( ft_hb_enabled( metrics->root.globals ) ) + shaper_buf = af_shaper_buf_create( metrics->root.globals ); for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ ) { @@ -340,7 +330,7 @@ }; - FT_TRACE5(( "blue zone %d (%s):\n", + FT_TRACE5(( "blue zone %u (%s):\n", axis->blue_count, cjk_blue_name[AF_CJK_IS_HORIZ_BLUE( bs ) | AF_CJK_IS_TOP_BLUE( bs ) ] )); @@ -553,7 +543,7 @@ } /* end for loop */ - af_shaper_buf_destroy( face, shaper_buf ); + af_shaper_buf_destroy( metrics->root.globals, shaper_buf ); FT_TRACE5(( "\n" )); @@ -572,23 +562,20 @@ /* If HarfBuzz is not available, we need a pointer to a single */ /* unsigned long value. */ -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - void* shaper_buf; -#else FT_ULong shaper_buf_; void* shaper_buf = &shaper_buf_; -#endif /* in all supported charmaps, digits have character codes 0x30-0x39 */ const char digits[] = "0 1 2 3 4 5 6 7 8 9"; const char* p; + FT_UNUSED( face ); + p = digits; -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - shaper_buf = af_shaper_buf_create( face ); -#endif + if ( ft_hb_enabled( metrics->root.globals ) ) + shaper_buf = af_shaper_buf_create( metrics->root.globals ); while ( *p ) { @@ -624,7 +611,7 @@ } } - af_shaper_buf_destroy( face, shaper_buf ); + af_shaper_buf_destroy( metrics->root.globals, shaper_buf ); metrics->root.digits_have_same_width = same_width; } @@ -710,7 +697,7 @@ FT_Pos delta1, delta2; - blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); + blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); /* shoot is under shoot for cjk */ delta1 = FT_DivFix( blue->ref.fit, scale ) - blue->shoot.org; @@ -736,7 +723,7 @@ blue->shoot.fit = blue->ref.fit - delta2; - FT_TRACE5(( ">> active cjk blue zone %c%d[%ld/%ld]:\n", + FT_TRACE5(( ">> active cjk blue zone %c%u[%ld/%ld]:\n", ( dim == AF_DIMENSION_HORZ ) ? 'H' : 'V', nn, blue->ref.org, blue->shoot.org )); FT_TRACE5(( " ref: cur=%.2f fit=%.2f\n", @@ -1378,7 +1365,7 @@ } - /* Initalize hinting engine. */ + /* Initialize hinting engine. */ FT_LOCAL_DEF( FT_Error ) af_cjk_hints_init( AF_GlyphHints hints, @@ -2185,7 +2172,7 @@ af_cjk_align_edge_points( AF_GlyphHints hints, AF_Dimension dim ) { - AF_AxisHints axis = & hints->axis[dim]; + AF_AxisHints axis = &hints->axis[dim]; AF_Edge edges = axis->edges; AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges ); AF_Edge edge; diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h index bc5aaf12e6e..bd1b39358e0 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afcjk.h @@ -4,7 +4,7 @@ * * Auto-fitter hinting routines for CJK writing system (specification). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h b/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h index 7980cf2e979..b93bcd1a2c5 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afcover.h @@ -4,7 +4,7 @@ * * Auto-fitter coverages (specification only). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c index ad667d2edc7..8613544f913 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.c @@ -5,7 +5,7 @@ * Auto-fitter dummy routines to be used if no hinting should be * performed (body). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h index 613c2f88a38..78a79439d95 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afdummy.h @@ -5,7 +5,7 @@ * Auto-fitter dummy routines to be used if no hinting should be * performed (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h b/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h index ae584ff06db..f3093fc90df 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/aferrors.h @@ -4,7 +4,7 @@ * * Autofitter error codes (specification only). * - * Copyright (C) 2005-2024 by + * Copyright (C) 2005-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c index b7403fa65e1..e74d8141161 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.c @@ -4,7 +4,7 @@ * * Auto-fitter routines to compute global hinting values (body). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -22,6 +22,11 @@ #include "afws-decl.h" #include +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ +# include "afgsub.h" +# include "ft-hb-ft.h" +#endif + /************************************************************************** * @@ -184,7 +189,7 @@ if ( gindex != 0 && gindex < globals->glyph_count && ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) - gstyles[gindex] = ss; + gstyles[gindex] = ss | AF_HAS_CMAP_ENTRY; for (;;) { @@ -195,7 +200,7 @@ if ( gindex < globals->glyph_count && ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) - gstyles[gindex] = ss; + gstyles[gindex] = ss | AF_HAS_CMAP_ENTRY; } } @@ -301,7 +306,7 @@ if ( !( count % 10 ) ) FT_TRACE4(( " " )); - FT_TRACE4(( " %d", idx )); + FT_TRACE4(( " %u", idx )); count++; if ( !( count % 10 ) ) @@ -356,8 +361,21 @@ globals->scale_down_factor = 0; #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - globals->hb_font = hb_ft_font_create_( face, NULL ); - globals->hb_buf = hb_buffer_create(); + if ( ft_hb_enabled ( globals ) ) + { + globals->hb_font = ft_hb_ft_font_create( globals ); + globals->hb_buf = hb( buffer_create )(); + + af_parse_gsub( globals ); + } + else + { + globals->hb_font = NULL; + globals->hb_buf = NULL; + + globals->gsub = NULL; + globals->gsub_lookups_single_alternate = NULL; + } #endif error = af_face_globals_compute_style_coverage( globals ); @@ -405,8 +423,14 @@ } #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - hb_font_destroy( globals->hb_font ); - hb_buffer_destroy( globals->hb_buf ); + if ( ft_hb_enabled ( globals ) ) + { + hb( font_destroy )( globals->hb_font ); + hb( buffer_destroy )( globals->hb_buf ); + + FT_FREE( globals->gsub ); + FT_FREE( globals->gsub_lookups_single_alternate ); + } #endif /* no need to free `globals->glyph_styles'; */ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h index ddb54c89b27..362f56e290b 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afglobal.h @@ -5,7 +5,7 @@ * Auto-fitter routines to compute global hinting values * (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -73,15 +73,17 @@ FT_BEGIN_HEADER /* default script for OpenType; ignored if HarfBuzz isn't used */ #define AF_SCRIPT_DEFAULT AF_SCRIPT_LATN - /* a bit mask for AF_DIGIT and AF_NONBASE */ -#define AF_STYLE_MASK 0x3FFF + /* a bit mask for AF_DIGIT, AF_NONBASE, and AF_HAS_CMAP_ENTRY */ +#define AF_STYLE_MASK 0x1FFF /* an uncovered glyph */ #define AF_STYLE_UNASSIGNED AF_STYLE_MASK - /* if this flag is set, we have an ASCII digit */ + /* if this flag is set, we have an ASCII digit */ #define AF_DIGIT 0x8000U /* if this flag is set, we have a non-base character */ #define AF_NONBASE 0x4000U + /* if this flag is set, the glyph has a (direct) cmap entry */ +#define AF_HAS_CMAP_ENTRY 0x2000U /* `increase-x-height' property */ #define AF_PROP_INCREASE_X_HEIGHT_MIN 6 @@ -111,6 +113,11 @@ FT_BEGIN_HEADER #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ hb_font_t* hb_font; hb_buffer_t* hb_buf; /* for feature comparison */ + + /* The GSUB table. */ + FT_Byte* gsub; + /* Lookup offsets, with only SingleSubst and AlternateSubst non-NULL. */ + FT_UInt32* gsub_lookups_single_alternate; #endif /* per-face auto-hinter properties */ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c index 96ffe343aa4..11faa655f62 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.c @@ -4,7 +4,7 @@ * * Auto-fitter hinting routines (body). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -840,6 +840,10 @@ if ( hints->contours != hints->embedded.contours ) FT_FREE( hints->contours ); + if ( hints->contour_y_minima != hints->embedded.contour_y_minima ) + FT_FREE( hints->contour_y_minima ); + if ( hints->contour_y_maxima != hints->embedded.contour_y_maxima ) + FT_FREE( hints->contour_y_maxima ); hints->max_contours = 0; hints->num_contours = 0; @@ -896,19 +900,30 @@ { if ( !hints->contours ) { - hints->contours = hints->embedded.contours; + hints->contours = hints->embedded.contours; + hints->contour_y_minima = hints->embedded.contour_y_minima; + hints->contour_y_maxima = hints->embedded.contour_y_maxima; + hints->max_contours = AF_CONTOURS_EMBEDDED; } } else if ( new_max > old_max ) { if ( hints->contours == hints->embedded.contours ) - hints->contours = NULL; + { + hints->contours = NULL; + hints->contour_y_minima = NULL; + hints->contour_y_maxima = NULL; + } new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */ if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) goto Exit; + if ( FT_RENEW_ARRAY( hints->contour_y_minima, old_max, new_max ) ) + goto Exit; + if ( FT_RENEW_ARRAY( hints->contour_y_maxima, old_max, new_max ) ) + goto Exit; hints->max_contours = new_max; } @@ -1324,7 +1339,7 @@ af_glyph_hints_align_edge_points( AF_GlyphHints hints, AF_Dimension dim ) { - AF_AxisHints axis = & hints->axis[dim]; + AF_AxisHints axis = &hints->axis[dim]; AF_Segment segments = axis->segments; AF_Segment segment_limit = FT_OFFSET( segments, axis->num_segments ); AF_Segment seg; diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h index 76fe83006a5..46b3ed3366f 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afhints.h @@ -4,7 +4,7 @@ * * Auto-fitter hinting routines (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -222,6 +222,9 @@ FT_BEGIN_HEADER /* the distance to the next point is very small */ #define AF_FLAG_NEAR ( 1U << 5 ) + /* prevent the auto-hinter from adding such a point to a segment */ +#define AF_FLAG_IGNORE ( 1U << 6 ) + /* edge hint flags */ #define AF_EDGE_NORMAL 0 @@ -229,6 +232,7 @@ FT_BEGIN_HEADER #define AF_EDGE_SERIF ( 1U << 1 ) #define AF_EDGE_DONE ( 1U << 2 ) #define AF_EDGE_NEUTRAL ( 1U << 3 ) /* edge aligns to a neutral blue zone */ +#define AF_EDGE_NO_BLUE ( 1U << 4 ) /* do not align edge to blue zone */ typedef struct AF_PointRec_* AF_Point; @@ -303,6 +307,7 @@ FT_BEGIN_HEADER } AF_EdgeRec; + #define AF_SEGMENTS_EMBEDDED 18 /* number of embedded segments */ #define AF_EDGES_EMBEDDED 12 /* number of embedded edges */ @@ -346,9 +351,11 @@ FT_BEGIN_HEADER FT_Int num_points; /* number of used points */ AF_Point points; /* points array */ - FT_Int max_contours; /* number of allocated contours */ - FT_Int num_contours; /* number of used contours */ - AF_Point* contours; /* contours array */ + FT_Int max_contours; /* number of allocated contours */ + FT_Int num_contours; /* number of used contours */ + AF_Point* contours; /* contours array */ + FT_Pos* contour_y_minima; /* array with y maxima of contours */ + FT_Pos* contour_y_maxima; /* array with y minima of contours */ AF_AxisHintsRec axis[AF_DIMENSION_MAX]; @@ -357,11 +364,13 @@ FT_BEGIN_HEADER /* implementations */ AF_StyleMetrics metrics; - /* Two arrays to avoid allocation penalty. */ + /* Some arrays to avoid allocation penalty. */ /* The `embedded' structure must be the last element! */ struct { AF_Point contours[AF_CONTOURS_EMBEDDED]; + FT_Pos contour_y_minima[AF_CONTOURS_EMBEDDED]; + FT_Pos contour_y_maxima[AF_CONTOURS_EMBEDDED]; AF_PointRec points[AF_POINTS_EMBEDDED]; } embedded; diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c index c6d23efd86f..a2cd14f8817 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.c @@ -4,7 +4,7 @@ * * Auto-fitter hinting routines for Indic writing system (body). * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * Rahul Bhalerao , . * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h index a7f73f25153..a2e825e9f86 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afindic.h @@ -5,7 +5,7 @@ * Auto-fitter hinting routines for Indic writing system * (specification). * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * Rahul Bhalerao , . * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c index 89287f7ea5a..cb5667ff793 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.c @@ -4,7 +4,7 @@ * * Auto-fitter hinting routines for latin writing system (body). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -22,6 +22,7 @@ #include "afglobal.h" #include "aflatin.h" #include "aferrors.h" +#include "afadjust.h" /************************************************************************** @@ -81,12 +82,8 @@ /* If HarfBuzz is not available, we need a pointer to a single */ /* unsigned long value. */ -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - void* shaper_buf; -#else FT_ULong shaper_buf_; void* shaper_buf = &shaper_buf_; -#endif const char* p; @@ -97,9 +94,9 @@ p = script_class->standard_charstring; -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - shaper_buf = af_shaper_buf_create( face ); -#endif + if ( ft_hb_enabled ( metrics->root.globals ) ) + shaper_buf = af_shaper_buf_create( metrics->root.globals ); + /* * We check a list of standard characters to catch features like * `c2sc' (small caps from caps) that don't contain lowercase letters @@ -140,7 +137,7 @@ break; } - af_shaper_buf_destroy( face, shaper_buf ); + af_shaper_buf_destroy( metrics->root.globals, shaper_buf ); if ( !glyph_index ) { @@ -149,7 +146,7 @@ goto Exit; } - FT_TRACE5(( "standard character: U+%04lX (glyph index %ld)\n", + FT_TRACE5(( "standard character: U+%04lX (glyph index %lu)\n", ch, glyph_index )); error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); @@ -334,12 +331,8 @@ /* If HarfBuzz is not available, we need a pointer to a single */ /* unsigned long value. */ -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - void* shaper_buf; -#else FT_ULong shaper_buf_; void* shaper_buf = &shaper_buf_; -#endif /* we walk over the blue character strings as specified in the */ @@ -349,9 +342,8 @@ FT_TRACE5(( "============================\n" )); FT_TRACE5(( "\n" )); -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - shaper_buf = af_shaper_buf_create( face ); -#endif + if ( ft_hb_enabled ( metrics->root.globals ) ) + shaper_buf = af_shaper_buf_create( metrics->root.globals ); for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ ) { @@ -367,7 +359,7 @@ FT_Bool have_flag = 0; - FT_TRACE5(( "blue zone %d", axis->blue_count )); + FT_TRACE5(( "blue zone %u", axis->blue_count )); if ( bs->properties ) { @@ -407,6 +399,20 @@ FT_TRACE5(( "long" )); } + if ( AF_LATIN_IS_CAPITAL_BOTTOM_BLUE( bs ) ) + { + if ( have_flag ) + FT_TRACE5(( ", " )); + FT_TRACE5(( "capital bottom" )); + } + + if ( AF_LATIN_IS_SMALL_BOTTOM_BLUE( bs ) ) + { + if ( have_flag ) + FT_TRACE5(( ", " )); + FT_TRACE5(( "small bottom" )); + } + FT_TRACE5(( ")" )); } @@ -454,9 +460,9 @@ } if ( AF_LATIN_IS_TOP_BLUE( bs ) ) - best_y_extremum = FT_INT_MIN; + best_y_extremum = FT_LONG_MIN; else - best_y_extremum = FT_INT_MAX; + best_y_extremum = FT_LONG_MAX; /* iterate over all glyph elements of the character cluster */ /* and get the data of the `biggest' one */ @@ -487,7 +493,7 @@ if ( num_idx == 1 ) FT_TRACE5(( " U+%04lX contains no (usable) outlines\n", ch )); else - FT_TRACE5(( " component %d of cluster starting with U+%04lX" + FT_TRACE5(( " component %u of cluster starting with U+%04lX" " contains no (usable) outlines\n", i, ch )); #endif continue; @@ -825,7 +831,7 @@ if ( num_idx == 1 ) FT_TRACE5(( " U+%04lX: best_y = %5ld", ch, best_y )); else - FT_TRACE5(( " component %d of cluster starting with U+%04lX:" + FT_TRACE5(( " component %u of cluster starting with U+%04lX:" " best_y = %5ld", i, ch, best_y )); #endif @@ -879,8 +885,8 @@ } /* end for loop */ - if ( !( best_y_extremum == FT_INT_MIN || - best_y_extremum == FT_INT_MAX ) ) + if ( !( best_y_extremum == FT_LONG_MIN || + best_y_extremum == FT_LONG_MAX ) ) { if ( best_round ) rounds[num_rounds++] = best_y_extremum; @@ -959,6 +965,10 @@ blue->flags |= AF_LATIN_BLUE_SUB_TOP; if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) blue->flags |= AF_LATIN_BLUE_NEUTRAL; + if ( AF_LATIN_IS_CAPITAL_BOTTOM_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_BOTTOM; + if ( AF_LATIN_IS_SMALL_BOTTOM_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_BOTTOM_SMALL; /* * The following flag is used later to adjust the y and x scales @@ -973,7 +983,7 @@ } /* end for loop */ - af_shaper_buf_destroy( face, shaper_buf ); + af_shaper_buf_destroy( metrics->root.globals, shaper_buf ); if ( axis->blue_count ) { @@ -1070,23 +1080,20 @@ /* If HarfBuzz is not available, we need a pointer to a single */ /* unsigned long value. */ -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - void* shaper_buf; -#else FT_ULong shaper_buf_; void* shaper_buf = &shaper_buf_; -#endif /* in all supported charmaps, digits have character codes 0x30-0x39 */ const char digits[] = "0 1 2 3 4 5 6 7 8 9"; const char* p; + FT_UNUSED( face ); + p = digits; -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - shaper_buf = af_shaper_buf_create( face ); -#endif + if ( ft_hb_enabled ( metrics->root.globals ) ) + shaper_buf = af_shaper_buf_create( metrics->root.globals ); while ( *p ) { @@ -1122,7 +1129,7 @@ } } - af_shaper_buf_destroy( face, shaper_buf ); + af_shaper_buf_destroy( metrics->root.globals, shaper_buf ); metrics->root.digits_have_same_width = same_width; } @@ -1155,6 +1162,9 @@ af_latin_metrics_check_digits( metrics, face ); } + af_reverse_character_map_new( &metrics->root.reverse_charmap, + &metrics->root ); + Exit: face->charmap = oldmap; return error; @@ -1263,7 +1273,7 @@ max_height = FT_MAX( max_height, -Axis->blues[nn].descender ); } - dist = FT_MulFix( max_height, new_scale - scale ); + dist = FT_MulFix( max_height, new_scale - scale ); if ( -128 < dist && dist < 128 ) { @@ -1466,13 +1476,13 @@ AF_LatinBlue blue = &axis->blues[nn]; - FT_TRACE5(( " reference %d: %ld scaled to %.2f%s\n", + FT_TRACE5(( " reference %u: %ld scaled to %.2f%s\n", nn, blue->ref.org, (double)blue->ref.fit / 64, ( blue->flags & AF_LATIN_BLUE_ACTIVE ) ? "" : " (inactive)" )); - FT_TRACE5(( " overshoot %d: %ld scaled to %.2f%s\n", + FT_TRACE5(( " overshoot %u: %ld scaled to %.2f%s\n", nn, blue->shoot.org, (double)blue->shoot.fit / 64, @@ -1484,6 +1494,17 @@ } + FT_CALLBACK_DEF( void ) + af_latin_metrics_done( AF_StyleMetrics metrics_ ) + { + AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_; + + + af_reverse_character_map_done( metrics->root.reverse_charmap, + metrics->root.globals->face->memory ); + } + + /* Scale global values in both directions. */ FT_LOCAL_DEF( void ) @@ -1617,7 +1638,8 @@ FT_Pos prev_max_on_coord = max_on_coord; - if ( FT_ABS( last->out_dir ) == major_dir && + if ( !( point->flags & AF_FLAG_IGNORE ) && + FT_ABS( last->out_dir ) == major_dir && FT_ABS( point->out_dir ) == major_dir ) { /* we are already on an edge, try to locate its start */ @@ -1676,13 +1698,17 @@ max_on_coord = v; } - if ( point->out_dir != segment_dir || point == last ) + if ( point->flags & AF_FLAG_IGNORE || + point->out_dir != segment_dir || + point == last ) { /* check whether the new segment's start point is identical to */ /* the previous segment's end point; for example, this might */ /* happen for spikes */ - if ( !prev_segment || segment->first != prev_segment->last ) + if ( point->flags & AF_FLAG_IGNORE || + !prev_segment || + segment->first != prev_segment->last ) { /* points are different: we are just leaving an edge, thus */ /* record a new segment */ @@ -1842,7 +1868,8 @@ /* if we are not on an edge, check whether the major direction */ /* coincides with the current point's `out' direction, or */ /* whether we have a single-point contour */ - if ( !on_edge && + if ( !( point->flags & AF_FLAG_IGNORE ) && + !on_edge && ( FT_ABS( point->out_dir ) == major_dir || point == point->prev ) ) { @@ -2521,6 +2548,9 @@ FT_Pos best_dist; /* initial threshold */ + if ( edge->flags & AF_EDGE_NO_BLUE ) + continue; + /* compute the initial threshold as a fraction of the EM size */ /* (the value 40 is heuristic) */ best_dist = FT_MulFix( metrics->units_per_em / 40, scale ); @@ -2610,7 +2640,7 @@ } - /* Initalize hinting engine. */ + /* Initialize hinting engine. */ static FT_Error af_latin_hints_init( AF_GlyphHints hints, @@ -2737,6 +2767,1192 @@ } +#undef FT_COMPONENT +#define FT_COMPONENT afadjust + + + static void + af_move_contour_vertically( AF_Point contour, + FT_Int movement ) + { + AF_Point point = contour; + AF_Point first_point = point; + + + if ( point ) + { + do + { + point->y += movement; + point = point->next; + + } while ( point != first_point ); + } + } + + + /* Move all contours higher than `limit` by `delta`. */ + static void + af_move_contours_up( AF_GlyphHints hints, + FT_Pos limit, + FT_Pos delta ) + { + FT_Int contour; + + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos min_y = hints->contour_y_minima[contour]; + FT_Pos max_y = hints->contour_y_maxima[contour]; + + + if ( min_y < max_y && + min_y > limit ) + af_move_contour_vertically( hints->contours[contour], + delta ); + } + } + + + static void + af_move_contours_down( AF_GlyphHints hints, + FT_Pos limit, + FT_Pos delta ) + { + FT_Int contour; + + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos min_y = hints->contour_y_minima[contour]; + FT_Pos max_y = hints->contour_y_maxima[contour]; + + + if ( min_y < max_y && + max_y < limit ) + af_move_contour_vertically( hints->contours[contour], + -delta ); + } + } + + + /* Compute vertical extrema of all contours and store them in the */ + /* `contour_y_minima` and `contour_y_maxima` arrays of `hints`. */ + static void + af_compute_vertical_extrema( AF_GlyphHints hints ) + { + FT_Int contour; + + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos min_y = FT_LONG_MAX; + FT_Pos max_y = FT_LONG_MIN; + + AF_Point first_point = hints->contours[contour]; + AF_Point point = first_point; + + + if ( !first_point || first_point->next->next == first_point ) + goto End_loop; + + do + { + if ( point->y < min_y ) + min_y = point->y; + if ( point->y > max_y ) + max_y = point->y; + + point = point->next; + + } while ( point != first_point ); + + End_loop: + hints->contour_y_minima[contour] = min_y; + hints->contour_y_maxima[contour] = max_y; + } + } + + + static FT_Int + af_find_highest_contour( AF_GlyphHints hints ) + { + FT_Int highest_contour = 0; + FT_Pos highest_min_y = FT_LONG_MAX; + FT_Pos highest_max_y = FT_LONG_MIN; + + FT_Int contour; + + + /* At this point we have one 'lower' (usually the base glyph) */ + /* and one 'upper' object (usually the diacritic glyph). If */ + /* there are more contours, they must be enclosed within either */ + /* 'lower' or 'upper'. To find this enclosing 'upper' contour */ + /* it is thus sufficient to search for the contour with the */ + /* highest y maximum value. */ + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos current_min_y = hints->contour_y_minima[contour]; + FT_Pos current_max_y = hints->contour_y_maxima[contour]; + + + /* If we have two contours with the same maximum value, take */ + /* the one that has a smaller height. */ + if ( current_max_y > highest_max_y || + ( current_max_y == highest_max_y && + current_min_y > highest_min_y ) ) + { + highest_min_y = current_min_y; + highest_max_y = current_max_y; + highest_contour = contour; + } + } + + return highest_contour; + } + + + static FT_Int + af_find_second_highest_contour( AF_GlyphHints hints ) + { + FT_Int highest_contour; + FT_Pos highest_min_y; + + FT_Int second_highest_contour = 0; + FT_Pos second_highest_max_y = FT_LONG_MIN; + + FT_Int contour; + + + if ( hints->num_contours < 3 ) + return 0; + + highest_contour = af_find_highest_contour( hints ); + highest_min_y = hints->contour_y_minima[highest_contour]; + + /* Search the contour with the largest vertical maximum that has a */ + /* vertical minimum lower than the vertical minimum of the topmost */ + /* contour. */ + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos current_min_y; + FT_Pos current_max_y; + + + if ( contour == highest_contour ) + continue; + + current_min_y = hints->contour_y_minima[contour]; + current_max_y = hints->contour_y_maxima[contour]; + + if ( current_max_y > second_highest_max_y && + current_min_y < highest_min_y ) + { + second_highest_max_y = current_max_y; + second_highest_contour = contour; + } + } + + return second_highest_contour; + } + + + static FT_Int + af_find_lowest_contour( AF_GlyphHints hints ) + { + FT_Int lowest_contour = 0; + FT_Pos lowest_min_y = FT_LONG_MAX; + FT_Pos lowest_max_y = FT_LONG_MIN; + + FT_Int contour; + + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos current_min_y = hints->contour_y_minima[contour]; + FT_Pos current_max_y = hints->contour_y_maxima[contour]; + + + if ( current_min_y < lowest_min_y || + ( current_min_y == lowest_min_y && + current_max_y < lowest_max_y ) ) + { + lowest_min_y = current_min_y; + lowest_max_y = current_max_y; + lowest_contour = contour; + } + } + + return lowest_contour; + } + + + static FT_Int + af_find_second_lowest_contour( AF_GlyphHints hints ) + { + FT_Int lowest_contour; + FT_Pos lowest_max_y; + + FT_Int second_lowest_contour = 0; + FT_Pos second_lowest_min_y = FT_LONG_MAX; + + FT_Int contour; + + + if ( hints->num_contours < 3 ) + return 0; + + lowest_contour = af_find_lowest_contour( hints ); + lowest_max_y = hints->contour_y_maxima[lowest_contour]; + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos current_min_y; + FT_Pos current_max_y; + + + if ( contour == lowest_contour ) + continue; + + current_min_y = hints->contour_y_minima[contour]; + current_max_y = hints->contour_y_maxima[contour]; + + if ( current_min_y < second_lowest_min_y && + current_max_y > lowest_max_y ) + { + second_lowest_min_y = current_min_y; + second_lowest_contour = contour; + } + } + + return second_lowest_contour; + } + + + /* While aligning edges to blue zones, make the auto-hinter */ + /* ignore the ones that are higher than `pos`. */ + static void + af_prevent_top_blue_alignment( AF_GlyphHints hints, + FT_Pos pos ) + { + AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT]; + + AF_Edge edges = axis->edges; + AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges ); + AF_Edge edge; + + + for ( edge = edges; edge < edge_limit; edge++ ) + if ( edge->pos > pos ) + edge->flags |= AF_EDGE_NO_BLUE; + } + + + static void + af_prevent_bottom_blue_alignment( AF_GlyphHints hints, + FT_Pos pos ) + { + AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT]; + + AF_Edge edges = axis->edges; + AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges ); + AF_Edge edge; + + + for ( edge = edges; edge < edge_limit; edge++ ) + if ( edge->pos < pos ) + edge->flags |= AF_EDGE_NO_BLUE; + } + + + static void + af_latin_get_base_glyph_blues( AF_GlyphHints hints, + FT_Bool is_capital, + AF_LatinBlue* top, + AF_LatinBlue* bottom ) + { + AF_LatinMetrics metrics = (AF_LatinMetrics)hints->metrics; + AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT]; + + FT_UInt top_flag; + FT_UInt bottom_flag; + + FT_UInt i; + + + top_flag = is_capital ? AF_LATIN_BLUE_TOP + : AF_LATIN_BLUE_ADJUSTMENT; + top_flag |= AF_LATIN_BLUE_ACTIVE; + + for ( i = 0; i < axis->blue_count; i++ ) + if ( ( axis->blues[i].flags & top_flag ) == top_flag ) + break; + if ( i < axis->blue_count ) + *top = &axis->blues[i]; + + bottom_flag = is_capital ? AF_LATIN_BLUE_BOTTOM + : AF_LATIN_BLUE_BOTTOM_SMALL; + bottom_flag |= AF_LATIN_BLUE_ACTIVE; + + for ( i = 0; i < axis->blue_count; i++ ) + if ( ( axis->blues[i].flags & bottom_flag ) == bottom_flag ) + break; + if ( i < axis->blue_count ) + *bottom = &axis->blues[i]; + } + + + /* Make the auto-hinter ignore top blue zones while aligning edges. */ + /* This affects everything that is higher than a vertical position */ + /* based on the lowercase or uppercase top and bottom blue zones */ + /* (depending on `is_capital`). */ + static void + af_latin_ignore_top( AF_GlyphHints hints, + AF_LatinBlue top_blue, + AF_LatinBlue bottom_blue ) + { + FT_Pos base_glyph_height; + FT_Pos limit; + + + /* Ignore blue zones that are higher than a heuristic threshold */ + /* (value 7 corresponds to approx. 14%, which should be sufficient */ + /* to exceed the height of uppercase serifs. We also add a quarter */ + /* of a pixel as a safety measure. */ + base_glyph_height = top_blue->shoot.cur - bottom_blue->shoot.cur; + limit = top_blue->shoot.cur + base_glyph_height / 7 + 16; + + af_prevent_top_blue_alignment( hints, limit ); + } + + + static void + af_latin_ignore_bottom( AF_GlyphHints hints, + AF_LatinBlue top_blue, + AF_LatinBlue bottom_blue ) + { + FT_Pos base_glyph_height; + FT_Pos limit; + + + base_glyph_height = top_blue->shoot.cur - bottom_blue->shoot.cur; + limit = bottom_blue->shoot.cur - base_glyph_height / 7 - 16; + + af_prevent_bottom_blue_alignment( hints, limit ); + } + + + static void + af_touch_contour( AF_GlyphHints hints, + FT_Int contour ) + { + AF_Point first_point = hints->contours[contour]; + AF_Point p = first_point; + + + do + { + p = p->next; + + p->flags |= AF_FLAG_IGNORE; + if ( !( p->flags & AF_FLAG_CONTROL ) ) + p->flags |= AF_FLAG_TOUCH_Y; + + } while ( p != first_point ); + } + + + static void + af_touch_top_contours( AF_GlyphHints hints, + FT_Int limit_contour ) + { + FT_Pos limit = hints->contour_y_minima[limit_contour]; + + FT_Int contour; + + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos min_y = hints->contour_y_minima[contour]; + FT_Pos max_y = hints->contour_y_maxima[contour]; + + + if ( min_y < max_y && + min_y >= limit ) + af_touch_contour( hints, contour ); + } + } + + + static void + af_touch_bottom_contours( AF_GlyphHints hints, + FT_Int limit_contour ) + { + FT_Pos limit = hints->contour_y_minima[limit_contour]; + + FT_Int contour; + + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos min_y = hints->contour_y_minima[contour]; + FT_Pos max_y = hints->contour_y_maxima[contour]; + + + if ( min_y < max_y && + max_y <= limit ) + af_touch_contour( hints, contour ); + } + } + + + /* Stretch tilde vertically, if necessary, and return the height */ + /* difference between the original and the stretched outline. */ + static FT_Pos + af_latin_stretch_top_tilde( AF_GlyphHints hints, + FT_Int tilde_contour ) + { + AF_Point p = hints->contours[tilde_contour]; + AF_Point first_point = p; + + FT_Pos min_y = hints->contour_y_minima[tilde_contour]; + FT_Pos max_y = hints->contour_y_maxima[tilde_contour]; + + FT_Pos min_measurement = FT_LONG_MAX; + FT_Bool measurement_taken = FALSE; + + FT_Pos height; + FT_Pos extremum_threshold; + FT_Pos target_height; + + + if ( min_y == max_y ) + return 0; + + FT_TRACE4(( "af_latin_stretch_top_tilde: min y: %ld, max y: %ld\n", + min_y, max_y )); + + height = SUB_LONG( max_y, min_y ); + extremum_threshold = height / 8; /* Value 8 is heuristic. */ + + /* Find points that are local vertical round extrema, and which */ + /* do not coincide with the vertical extreme values (i.e., we */ + /* search for the 'other' wiggles in the tilde), then measure the */ + /* distance to the vertical extreme values. Try to find the one */ + /* with the smallest distance. */ + /* */ + /* The algorithm only works for tilde shapes that don't deviate */ + /* from the standard shape too much. In particular, the wiggles */ + /* must be round extrema. */ + do + { + p = p->next; + + if ( !( p->flags & AF_FLAG_CONTROL ) && + p->prev->y == p->y && p->next->y == p->y && + p->y != min_y && p->y != max_y && + p->prev->flags & AF_FLAG_CONTROL && + p->next->flags & AF_FLAG_CONTROL ) + { + /* This point could be a candidate. Find the next and previous */ + /* on-curve points, and make sure they are both either above or */ + /* below the point, then make the measurement. */ + AF_Point prev_on = p->prev; + AF_Point next_on = p->next; + + FT_Pos measurement; + + + while ( prev_on->flags & AF_FLAG_CONTROL ) + prev_on = prev_on->prev; + while ( next_on->flags & AF_FLAG_CONTROL ) + next_on = next_on->next; + + if ( next_on->y > p->y && prev_on->y > p->y ) + measurement = p->y - min_y; + else if ( next_on->y < p->y && prev_on->y < p->y ) + measurement = max_y - p->y; + else + continue; + + /* Ignore hits that are too near to a vertical extremum. */ + if ( measurement < extremum_threshold ) + continue; + + if ( !measurement_taken || measurement < min_measurement ) + { + measurement_taken = TRUE; + min_measurement = measurement; + } + } + + } while ( p != first_point ); + + if ( !measurement_taken ) + min_measurement = 0; + + FT_TRACE4(( "af_latin_stretch_top_tilde: min measurement %ld\n", + min_measurement )); + + /* To preserve the stretched shape we prevent that the tilde */ + /* gets auto-hinted; we do this for all contours equal or */ + /* above the vertical minimum of `tilde_contour`. */ + af_touch_top_contours( hints, tilde_contour ); + + /* XXX This is an important element of the algorithm; */ + /* we need a description. */ + target_height = min_measurement + 64; + if ( height >= target_height ) + return 0; + + /* Do the scaling. */ + p = first_point; + do + { + p = p->next; + /* We adjust the height of the diacritic only, which means */ + /* we are never dealing with large numbers and can thus avoid */ + /* `FT_MulFix`. */ + p->y = ( ( p->y - min_y ) * target_height / height ) + min_y; + + } while ( p != first_point ); + + return target_height - height; + } + + + static FT_Pos + af_latin_stretch_bottom_tilde( AF_GlyphHints hints, + FT_Int tilde_contour ) + { + AF_Point p = hints->contours[tilde_contour]; + AF_Point first_point = p; + + FT_Pos min_y = hints->contour_y_minima[tilde_contour]; + FT_Pos max_y = hints->contour_y_maxima[tilde_contour]; + + FT_Pos min_measurement = FT_LONG_MAX; + FT_Bool measurement_taken = FALSE; + + FT_Pos height; + FT_Pos extremum_threshold; + FT_Pos target_height; + + + if ( min_y == max_y ) + return 0; + + FT_TRACE4(( "af_latin_stretch_bottom_tilde: min y: %ld, max y: %ld\n", + min_y, max_y )); + + height = SUB_LONG( max_y, min_y ); + extremum_threshold = height / 8; + + do + { + p = p->next; + + if ( !( p->flags & AF_FLAG_CONTROL ) && + p->prev->y == p->y && p->next->y == p->y && + p->y != min_y && p->y != max_y && + p->prev->flags & AF_FLAG_CONTROL && + p->next->flags & AF_FLAG_CONTROL ) + { + AF_Point prev_on = p->prev; + AF_Point next_on = p->next; + + FT_Pos measurement; + + + while ( prev_on->flags & AF_FLAG_CONTROL ) + prev_on = prev_on->prev; + while ( next_on->flags & AF_FLAG_CONTROL ) + next_on = next_on->next; + + if ( next_on->y > p->y && prev_on->y > p->y ) + measurement = p->y - min_y; + else if ( next_on->y < p->y && prev_on->y < p->y ) + measurement = max_y - p->y; + else + continue; + + if ( measurement < extremum_threshold ) + continue; + + if ( !measurement_taken || measurement < min_measurement ) + { + measurement_taken = TRUE; + min_measurement = measurement; + } + } + + } while ( p != first_point ); + + if ( !measurement_taken ) + min_measurement = 0; + + FT_TRACE4(( "af_latin_stretch_bottom_tilde: min measurement %ld\n", + min_measurement )); + + af_touch_bottom_contours( hints, tilde_contour ); + + target_height = min_measurement + 64; + if ( height >= target_height ) + return 0; + + p = first_point; + do + { + p = p->next; + p->y = ( ( p->y - max_y ) * target_height / height ) + max_y; + + } while ( p != first_point ); + + return target_height - height; + } + + + /* + As part of `af_latin_stretch_top_tilde`, normally all points in the + tilde are marked as touched, so the existing grid fitting will leave the + tilde misaligned with the grid. + + This function moves the tilde contour down to be grid-fitted. We assume + that if moving the tilde down would cause it to touch or overlap another + countour, the vertical adjustment step will fix it. + + Because the vertical adjustment step comes after all other grid-fitting + steps, the top edge of the contour under the tilde is usually aligned + with a horizontal grid line. The vertical gap enforced by the vertical + adjustment is exactly one pixel, so if the top edge of the contour below + the tilde is on a grid line, the resulting tilde contour will also be + grid-aligned. + + But in cases where the gap is already big enough so that the vertical + adjustment does nothing, this function ensures that even without the + intervention of the vertical adjustment step, the tilde will be + grid-aligned. + + Return the vertical alignment amount. + */ + static FT_Pos + af_latin_align_top_tilde( AF_GlyphHints hints, + FT_Int tilde_contour ) + { + AF_Point p = hints->contours[tilde_contour]; + AF_Point first_point = p; + + FT_Pos min_y = p->y; + FT_Pos max_y = p->y; + + FT_Pos min_y_rounded; + FT_Pos delta; + FT_Pos height; + + + /* Find vertical extrema of the (now stretched) tilde contour. */ + do + { + p = p->next; + if ( p->y < min_y ) + min_y = p->y; + if ( p->y > max_y ) + max_y = p->y; + + } while ( p != first_point ); + + /* Align bottom of the tilde to the grid. */ + min_y_rounded = FT_PIX_ROUND( min_y ); + delta = min_y_rounded - min_y; + height = max_y - min_y; + + /* If the tilde is less than 3 pixels tall, snap the center of it */ + /* to the grid instead of the bottom to improve readability. */ + if ( height < 64 * 3 ) + delta += ( FT_PIX_ROUND( height ) - height ) / 2; + + af_move_contour_vertically( first_point, delta ); + + return delta; + } + + + static FT_Pos + af_latin_align_bottom_tilde( AF_GlyphHints hints, + FT_Int tilde_contour ) + { + AF_Point p = hints->contours[tilde_contour]; + AF_Point first_point = p; + + FT_Pos min_y = p->y; + FT_Pos max_y = p->y; + + FT_Pos max_y_rounded; + FT_Pos delta; + FT_Pos height; + + + do + { + p = p->next; + if ( p->y < min_y ) + min_y = p->y; + if ( p->y > max_y ) + max_y = p->y; + + } while ( p != first_point ); + + max_y_rounded = FT_PIX_ROUND( max_y ); + delta = max_y_rounded - max_y; + height = max_y - min_y; + + if ( height < 64 * 3 ) + delta -= ( FT_PIX_ROUND( height ) - height ) / 2; + + af_move_contour_vertically( first_point, delta ); + + return delta; + } + + + /* Return 1 if the given contour overlaps horizontally with the bounding */ + /* box of all other contours combined. This is a helper for function */ + /* `af_glyph_hints_apply_vertical_separation_adjustments`. */ + static FT_Bool + af_check_contour_horizontal_overlap( AF_GlyphHints hints, + FT_Int contour_index ) + { + FT_Pos contour_max_x = FT_LONG_MIN; + FT_Pos contour_min_x = FT_LONG_MAX; + FT_Pos others_max_x = FT_LONG_MIN; + FT_Pos others_min_x = FT_LONG_MAX; + + FT_Int contour; + + FT_Bool horizontal_overlap; + + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + AF_Point first_point = hints->contours[contour]; + AF_Point p = first_point; + + + /* Ignore dimensionless contours (i.e., contours with only one or */ + /* two points). */ + if ( first_point->next->next == first_point ) + continue; + + do + { + p = p->next; + + if ( contour == contour_index ) + { + if ( p->x < contour_min_x ) + contour_min_x = p->x; + if ( p->x > contour_max_x ) + contour_max_x = p->x; + } + else + { + if ( p->x < others_min_x ) + others_min_x = p->x; + if ( p->x > others_max_x ) + others_max_x = p->x; + } + } while ( p != first_point ); + } + + horizontal_overlap = + ( others_min_x <= contour_max_x && contour_max_x <= others_max_x ) || + ( others_min_x <= contour_min_x && contour_min_x <= others_max_x ) || + ( contour_max_x >= others_max_x && contour_min_x <= others_min_x ); + + return horizontal_overlap; + } + + + static void + af_glyph_hints_apply_vertical_separation_adjustments( + AF_GlyphHints hints, + AF_Dimension dim, + FT_UInt glyph_index, + FT_Pos accent_height_limit, + FT_Hash reverse_charmap ) + { + FT_Bool adjust_top = FALSE; + FT_Bool adjust_below_top = FALSE; + + FT_Bool adjust_bottom = FALSE; + FT_Bool adjust_above_bottom = FALSE; + + size_t* val; + FT_UInt32 adj_type = AF_ADJUST_NONE; + + + FT_TRACE4(( "Entering" + " af_glyph_hints_apply_vertical_separation_adjustments\n" )); + + if ( dim != AF_DIMENSION_VERT ) + return; + + val = ft_hash_num_lookup( (FT_Int)glyph_index, reverse_charmap ); + if ( val ) + { + FT_UInt codepoint = *val; + + + adj_type = af_adjustment_database_lookup( codepoint ); + + if ( adj_type ) + { + adjust_top = !!( adj_type & AF_ADJUST_UP ); + adjust_below_top = !!( adj_type & AF_ADJUST_UP2 ); + + adjust_bottom = !!( adj_type & AF_ADJUST_DOWN ); + adjust_above_bottom = !!( adj_type & AF_ADJUST_DOWN2 ); + } + } + + if ( ( ( adjust_top || adjust_bottom ) && + hints->num_contours >= 2 ) || + ( ( adjust_below_top || adjust_above_bottom ) && + hints->num_contours >= 3 ) ) + { + /* Recompute vertical extrema, this time acting on already */ + /* auto-hinted outlines. */ + af_compute_vertical_extrema( hints ); + } + + if ( ( adjust_top && hints->num_contours >= 2 ) || + ( adjust_below_top && hints->num_contours >= 3 ) ) + { + FT_Int high_contour; + FT_Pos high_min_y; + FT_Pos high_max_y; + FT_Pos high_height; + + FT_Int tilde_contour; + FT_Pos tilde_min_y; + FT_Pos tilde_max_y; + FT_Pos tilde_height; + + FT_Int contour; + FT_Bool horizontal_overlap; + + FT_Pos min_distance = 64; + FT_Pos adjustment_amount; + FT_Pos calculated_amount; + FT_Pos centering_adjustment = 0; + FT_Pos pos; + + FT_Bool is_top_tilde = !!( adj_type & AF_ADJUST_TILDE_TOP ); + FT_Bool is_below_top_tilde = !!( adj_type & AF_ADJUST_TILDE_TOP2 ); + + + FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n" + " Applying vertical adjustment: %s\n", + adjust_top ? "AF_ADJUST_TOP" : "AF_ADJUST_TOP2" )); + + high_contour = adjust_below_top + ? af_find_second_highest_contour( hints ) + : af_find_highest_contour( hints ); + + /* Check for a horizontal overlap between the high contour and the */ + /* rest. If there is no overlap, do not adjust. */ + horizontal_overlap = + af_check_contour_horizontal_overlap( hints, high_contour ); + if ( !horizontal_overlap ) + { + FT_TRACE4(( " High contour does not horizontally overlap" + " with other contours.\n" + " Skipping adjustment.\n" )); + return; + } + + high_min_y = hints->contour_y_minima[high_contour]; + high_max_y = hints->contour_y_maxima[high_contour]; + high_height = high_max_y - high_min_y; + + if ( high_height > accent_height_limit ) + { + FT_TRACE4(( " High contour height (%.2f) exceeds accent height" + " limit (%.2f).\n" + " Skipping adjustment.\n", + (double)high_height / 64, + (double)accent_height_limit / 64 )); + return; + } + + /* If the difference between the vertical minimum of the high */ + /* contour and the vertical maximum of another contour is less */ + /* than a pixel, shift up the high contour to make the distance */ + /* one pixel. */ + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos min_y; + FT_Pos max_y; + FT_Pos distance; + + + if ( contour == high_contour ) + continue; + + min_y = hints->contour_y_minima[contour]; + max_y = hints->contour_y_maxima[contour]; + + /* We also check that the y minimum of the 'other' contour */ + /* is below the high contour to avoid potential false hits */ + /* with contours enclosed in the high one. */ + distance = high_min_y - max_y; + if ( distance < 64 && + distance < min_distance && + min_y < high_min_y ) + min_distance = distance; + } + + adjustment_amount = 64 - min_distance; + + if ( is_top_tilde || is_below_top_tilde ) + { + tilde_contour = adjust_top + ? high_contour + : ( is_below_top_tilde + ? high_contour + : af_find_highest_contour( hints ) ); + + tilde_min_y = hints->contour_y_minima[tilde_contour]; + tilde_max_y = hints->contour_y_maxima[tilde_contour]; + tilde_height = tilde_max_y - tilde_min_y; + + /* The vertical separation adjustment potentially undoes a */ + /* tilde center alignment. If it would grid-align a tilde */ + /* less than 3 pixels in height, shift additionally to */ + /* re-center the tilde. */ + + pos = high_min_y + adjustment_amount; + if ( adjust_below_top && is_top_tilde ) + pos += high_height; + + if ( pos % 64 == 0 && tilde_height < 3 * 64 ) + { + centering_adjustment = ( FT_PIX_ROUND( tilde_height ) - + tilde_height ) / 2; + + FT_TRACE4(( " Additional tilde centering adjustment: %ld\n", + centering_adjustment )); + } + } + + if ( ( adjust_top && is_top_tilde ) || + ( adjust_below_top && is_below_top_tilde ) ) + calculated_amount = adjustment_amount + centering_adjustment; + else + calculated_amount = adjustment_amount; + + /* allow a delta of 2/64px to handle rounding differences */ + FT_TRACE4(( " Calculated adjustment amount: %ld%s\n", + calculated_amount, + ( calculated_amount < -2 || + ( adjustment_amount > 66 && calculated_amount > 66 ) ) + ? " (out of range [-2;66], not adjusting)" : "" )); + + if ( calculated_amount != 0 && + calculated_amount >= -2 && + ( calculated_amount <= 66 || adjustment_amount <= 66 ) ) + { + /* Value 8 is heuristic. */ + FT_Pos height_delta = high_height / 8; + FT_Pos min_y_limit = high_min_y - height_delta; + + + FT_TRACE4(( " Pushing high contour %ld units up\n", + calculated_amount )); + + /* While we use only a single contour (the 'high' one) for */ + /* computing `adjustment_amount`, we apply it to all contours */ + /* that are (approximately) in the same vertical range or */ + /* higher. This covers, for example, the inner contour of */ + /* the Czech ring accent or the second acute accent in the */ + /* Hungarian double acute accent. */ + af_move_contours_up( hints, min_y_limit, adjustment_amount ); + + if ( adjust_below_top && is_top_tilde ) + { + FT_TRACE4(( " Pushing top tilde %ld units up\n", + centering_adjustment )); + + af_move_contours_up( hints, + min_y_limit + high_height, + centering_adjustment ); + } + } + } + + if ( ( adjust_bottom && hints->num_contours >= 2 ) || + ( adjust_above_bottom && hints->num_contours >= 3 ) ) + { + FT_Int low_contour; + FT_Pos low_min_y; + FT_Pos low_max_y; + FT_Pos low_height; + + FT_Int tilde_contour; + FT_Pos tilde_min_y; + FT_Pos tilde_max_y; + FT_Pos tilde_height; + + FT_Int contour; + FT_Bool horizontal_overlap; + + FT_Pos min_distance = 64; + FT_Pos adjustment_amount; + FT_Pos calculated_amount; + FT_Pos centering_adjustment = 0; + FT_Pos pos; + + FT_Bool is_bottom_tilde = + !!( adj_type & AF_ADJUST_TILDE_BOTTOM ); + FT_Bool is_above_bottom_tilde = + !!( adj_type & AF_ADJUST_TILDE_BOTTOM2 ); + + + FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n" + " Applying vertical adjustment: %s\n", + adjust_bottom ? "AF_ADJUST_DOWN": "AF_ADJUST_DOWN2" )); + + low_contour = adjust_above_bottom + ? af_find_second_lowest_contour( hints ) + : af_find_lowest_contour( hints ); + + horizontal_overlap = + af_check_contour_horizontal_overlap( hints, low_contour ); + if ( !horizontal_overlap ) + { + FT_TRACE4(( " Low contour does not horizontally overlap" + " with other contours.\n" + " Skipping adjustment.\n" )); + return; + } + + low_min_y = hints->contour_y_minima[low_contour]; + low_max_y = hints->contour_y_maxima[low_contour]; + low_height = low_max_y - low_min_y; + + if ( low_height > accent_height_limit ) + { + FT_TRACE4(( " Low contour height (%.2f) exceeds accent height" + " limit (%.2f).\n" + " Skipping adjustment.\n", + (double)low_height / 64, + (double)accent_height_limit / 64 )); + return; + } + + for ( contour = 0; contour < hints->num_contours; contour++ ) + { + FT_Pos min_y; + FT_Pos max_y; + FT_Pos distance; + + + if ( contour == low_contour ) + continue; + + min_y = hints->contour_y_minima[contour]; + max_y = hints->contour_y_maxima[contour]; + + distance = min_y - low_max_y; + if ( distance < 64 && + distance < min_distance && + max_y > low_max_y ) + min_distance = distance; + } + + adjustment_amount = 64 - min_distance; + + if ( is_bottom_tilde || is_above_bottom_tilde ) + { + tilde_contour = adjust_bottom + ? low_contour + : ( is_above_bottom_tilde + ? low_contour + : af_find_lowest_contour( hints ) ); + + tilde_min_y = hints->contour_y_minima[tilde_contour]; + tilde_max_y = hints->contour_y_maxima[tilde_contour]; + tilde_height = tilde_max_y - tilde_min_y; + + pos = low_max_y - adjustment_amount; + if ( adjust_above_bottom && is_bottom_tilde ) + pos -= low_height; + + if ( pos % 64 == 0 && tilde_height < 3 * 64 ) + { + centering_adjustment = ( FT_PIX_ROUND( tilde_height ) - + tilde_height ) / 2; + + FT_TRACE4(( " Additional tilde centering adjustment: %ld\n", + centering_adjustment )); + } + } + + if ( ( adjust_bottom && is_bottom_tilde ) || + ( adjust_above_bottom && is_above_bottom_tilde ) ) + calculated_amount = adjustment_amount + centering_adjustment; + else + calculated_amount = adjustment_amount; + + FT_TRACE4(( " Calculated adjustment amount: %ld%s\n", + calculated_amount, + ( calculated_amount < -2 || + ( adjustment_amount > 66 && calculated_amount > 66 ) ) + ? " (out of range [-2;66], not adjusting)" : "" )); + + if ( calculated_amount != 0 && + calculated_amount >= -2 && + ( calculated_amount <= 66 || adjustment_amount <= 66 ) ) + { + FT_Pos height_delta = low_height / 8; + FT_Pos max_y_limit = low_max_y + height_delta; + + + FT_TRACE4(( " Pushing low contour %ld units down\n", + calculated_amount )); + + af_move_contours_down( hints, max_y_limit, adjustment_amount ); + + if ( adjust_above_bottom && is_bottom_tilde ) + { + FT_TRACE4(( " Pushing bottom tilde %ld units down\n", + centering_adjustment )); + + af_move_contours_down( hints, + max_y_limit - low_height, + centering_adjustment ); + } + } + } + +#ifdef FT_DEBUG_LEVEL_TRACE + if ( !( ( ( adjust_top || adjust_bottom ) && + hints->num_contours >= 2 ) || + ( ( adjust_below_top || adjust_above_bottom ) && + hints->num_contours >= 3 ) ) ) + FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n" + " No vertical adjustment applied\n" )); +#endif + + FT_TRACE4(( "Exiting" + " af_glyph_hints_apply_vertical_separation_adjustments\n" )); + } + + +#undef FT_COMPONENT +#define FT_COMPONENT aflatin + + /* Compute the snapped width of a given stem, ignoring very thin ones. */ /* There is a lot of voodoo in this function; changing the hard-coded */ /* parameters influence the whole hinting process. */ @@ -2998,13 +4214,15 @@ af_latin_hint_edges( AF_GlyphHints hints, AF_Dimension dim ) { - AF_AxisHints axis = &hints->axis[dim]; - AF_Edge edges = axis->edges; - AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges ); - FT_PtrDist n_edges; - AF_Edge edge; - AF_Edge anchor = NULL; - FT_Int has_serifs = 0; + AF_AxisHints axis = &hints->axis[dim]; + + AF_Edge edges = axis->edges; + AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges ); + AF_Edge edge; + FT_PtrDist n_edges; + + AF_Edge anchor = NULL; + FT_Bool has_non_stem_edges = 0; AF_StyleClass style_class = hints->metrics->style_class; AF_ScriptClass script_class = af_script_classes[style_class->script]; @@ -3131,7 +4349,7 @@ edge2 = edge->link; if ( !edge2 ) { - has_serifs++; + has_non_stem_edges = TRUE; continue; } @@ -3408,7 +4626,7 @@ } } - if ( has_serifs || !anchor ) + if ( has_non_stem_edges || !anchor ) { /* * now hint the remaining edges (serifs and single) in order @@ -3426,9 +4644,75 @@ if ( edge->serif ) { + AF_Edge e, top, bottom; + FT_Pos min_pos, max_pos; + + + /* Check whether we have a real serif -- if there are */ + /* other edges with overlapping (or enclosed) segments */ + /* between the primary and serif edge, we have not. */ + /* */ + /* Such a situation might happen if an accent is very */ + /* near to its base glyph (for example, Vietnamese */ + /* uppercase letters with two accents in `arial.ttf`), */ + /* and the segment detection algorithm classifies the */ + /* top of the accent incorrectly as a serif. */ delta = edge->serif->opos - edge->opos; if ( delta < 0 ) + { delta = -delta; + + top = edge; + bottom = edge->serif; + } + else + { + top = edge->serif; + bottom = edge; + } + + if ( delta < 64 + 32 ) + { + /* take care of outline orientation while computing extrema */ + min_pos = FT_MIN( FT_MIN( FT_MIN( top->first->first->v, + top->first->last->v ), + FT_MIN( top->last->first->v, + top->last->last->v ) ), + FT_MIN( FT_MIN( bottom->first->first->v, + bottom->first->last->v ), + FT_MIN( bottom->last->first->v, + bottom->last->last->v ) ) ); + max_pos = FT_MAX( FT_MAX( FT_MAX( top->first->first->v, + top->first->last->v ), + FT_MAX( top->last->first->v, + top->last->last->v ) ), + FT_MAX( FT_MAX( bottom->first->first->v, + bottom->first->last->v ), + FT_MAX( bottom->last->first->v, + bottom->last->last->v ) ) ); + + for ( e = bottom + 1; e < top; e++ ) + { + FT_Pos e_min = FT_MIN( FT_MIN( e->first->first->v, + e->first->last->v ), + FT_MIN( e->last->first->v, + e->last->last->v ) ); + FT_Pos e_max = FT_MAX( FT_MAX( e->first->first->v, + e->first->last->v ), + FT_MAX( e->last->first->v, + e->last->last->v ) ); + + if ( !( ( e_min < min_pos && e_max < min_pos ) || + ( e_min > max_pos && e_max > max_pos ) ) ) + { + delta = 1000; /* not a real serif */ + break; + } + } + + if ( delta == 1000 ) + continue; + } } if ( delta < 64 + 16 ) @@ -3562,6 +4846,8 @@ AF_LatinAxis axis; + FT_Pos accent_height_limit = 0; + error = af_glyph_hints_reload( hints, outline ); if ( error ) @@ -3581,11 +4867,172 @@ if ( AF_HINTS_DO_VERTICAL( hints ) ) { + size_t* val; + + FT_Int top_tilde_contour = 0; + FT_Int bottom_tilde_contour = 0; + + FT_Int below_top_tilde_contour = 0; + FT_Int above_bottom_tilde_contour = 0; + + AF_LatinBlue capital_top_blue = NULL; + AF_LatinBlue capital_bottom_blue = NULL; + + AF_LatinBlue small_top_blue = NULL; + AF_LatinBlue small_bottom_blue = NULL; + + FT_Bool have_flags = FALSE; + + FT_Bool is_top_tilde = FALSE; + FT_Bool is_bottom_tilde = FALSE; + + FT_Bool is_below_top_tilde = FALSE; + FT_Bool is_above_bottom_tilde = FALSE; + + FT_Bool ignore_capital_top = FALSE; + FT_Bool ignore_capital_bottom = FALSE; + + FT_Bool ignore_small_top = FALSE; + FT_Bool ignore_small_bottom = FALSE; + + FT_Bool do_height_check = TRUE; + + FT_Pos limit; + FT_Pos y_offset; + + + val = ft_hash_num_lookup( (FT_Int)glyph_index, + metrics->root.reverse_charmap ); + if ( val ) + { + FT_UInt codepoint = *val; + FT_UInt32 adj_type = af_adjustment_database_lookup( codepoint ); + + + if ( adj_type ) + { + have_flags = !!adj_type; + + is_top_tilde = !!( adj_type & AF_ADJUST_TILDE_TOP ); + is_bottom_tilde = !!( adj_type & AF_ADJUST_TILDE_BOTTOM ); + + is_below_top_tilde = !!( adj_type & AF_ADJUST_TILDE_TOP2 ); + is_above_bottom_tilde = !!( adj_type & AF_ADJUST_TILDE_BOTTOM2 ); + + ignore_capital_top = !!( adj_type & AF_IGNORE_CAPITAL_TOP ); + ignore_capital_bottom = !!( adj_type & AF_IGNORE_CAPITAL_BOTTOM ); + + ignore_small_top = !!( adj_type & AF_IGNORE_SMALL_TOP ); + ignore_small_bottom = !!( adj_type & AF_IGNORE_SMALL_BOTTOM ); + + do_height_check = !( adj_type & AF_ADJUST_NO_HEIGHT_CHECK ); + } + } + + if ( is_top_tilde || is_bottom_tilde || + is_below_top_tilde || is_above_bottom_tilde ) + af_compute_vertical_extrema( hints ); + + /* Process inner tilde glyphs first. */ + if ( is_below_top_tilde ) + { + below_top_tilde_contour = af_find_second_highest_contour( hints ); + + y_offset = af_latin_stretch_top_tilde( + hints, below_top_tilde_contour ); + y_offset += af_latin_align_top_tilde( + hints, below_top_tilde_contour ); + + limit = hints->contour_y_minima[below_top_tilde_contour]; + af_move_contours_up( hints, limit, y_offset ); + } + if ( is_above_bottom_tilde ) + { + above_bottom_tilde_contour = af_find_second_lowest_contour( hints ); + + y_offset = af_latin_stretch_bottom_tilde( + hints, above_bottom_tilde_contour ); + y_offset -= af_latin_align_bottom_tilde( + hints, above_bottom_tilde_contour ); + + limit = hints->contour_y_maxima[above_bottom_tilde_contour]; + af_move_contours_down( hints, limit, y_offset ); + } + + if ( is_top_tilde ) + { + top_tilde_contour = af_find_highest_contour( hints ); + + (void)af_latin_stretch_top_tilde( hints, top_tilde_contour ); + (void)af_latin_align_top_tilde( hints, top_tilde_contour ); + } + if ( is_bottom_tilde ) + { + bottom_tilde_contour = af_find_lowest_contour( hints ); + + (void)af_latin_stretch_bottom_tilde( hints, bottom_tilde_contour ); + (void)af_latin_align_bottom_tilde( hints, bottom_tilde_contour ); + } + axis = &metrics->axis[AF_DIMENSION_VERT]; error = af_latin_hints_detect_features( hints, axis->width_count, axis->widths, AF_DIMENSION_VERT ); + + if ( have_flags ) + { + af_latin_get_base_glyph_blues( hints, + TRUE, + &capital_top_blue, + &capital_bottom_blue ); + af_latin_get_base_glyph_blues( hints, + FALSE, + &small_top_blue, + &small_bottom_blue ); + + if ( do_height_check ) + { + /* Set a heuristic limit for the accent height so that */ + /* `af_glyph_hints_apply_vertical_separation_adjustments` */ + /* can correctly ignore the case where an accent is */ + /* unexpectedly not the highest (or lowest) contour. */ + + /* Either 2/3 of the lowercase blue zone height... */ + if ( small_top_blue && small_bottom_blue ) + accent_height_limit = 2 * ( small_top_blue->shoot.cur - + small_bottom_blue->shoot.cur ) / 3; + /* or 1/2 of the uppercase blue zone height... */ + else if ( capital_top_blue && capital_bottom_blue ) + accent_height_limit = ( capital_top_blue->shoot.cur - + capital_bottom_blue->shoot.cur ) / 2; + /* or half of the standard PostScript ascender value (8/10) */ + /* of the EM value, scaled. */ + else + accent_height_limit = FT_MulFix( metrics->units_per_em * 4 / 10, + metrics->root.scaler.y_scale ); + } + } + + if ( capital_top_blue && capital_bottom_blue ) + { + if ( ignore_capital_top ) + af_latin_ignore_top( hints, + capital_top_blue, capital_bottom_blue ); + if ( ignore_capital_bottom ) + af_latin_ignore_bottom( hints, + capital_top_blue, capital_bottom_blue ); + } + if ( small_top_blue && small_bottom_blue ) + { + if ( ignore_small_top ) + af_latin_ignore_top( hints, + small_top_blue, small_bottom_blue ); + if ( ignore_small_bottom ) + af_latin_ignore_bottom( hints, + small_top_blue, small_bottom_blue ); + } + if ( error ) goto Exit; @@ -3604,6 +5051,12 @@ af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim ); af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim ); af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim ); + af_glyph_hints_apply_vertical_separation_adjustments( + hints, + (AF_Dimension)dim, + glyph_index, + accent_height_limit, + metrics->root.reverse_charmap ); } } @@ -3632,7 +5085,7 @@ (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, /* style_metrics_init */ (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, /* style_metrics_scale */ - (AF_WritingSystem_DoneMetricsFunc) NULL, /* style_metrics_done */ + (AF_WritingSystem_DoneMetricsFunc) af_latin_metrics_done, /* style_metrics_done */ (AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, /* style_metrics_getstdw */ (AF_WritingSystem_InitHintsFunc) af_latin_hints_init, /* style_hints_init */ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h index 54e50615021..82b4b0d480d 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/aflatin.h @@ -5,7 +5,7 @@ * Auto-fitter hinting routines for latin writing system * (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -61,17 +61,26 @@ FT_BEGIN_HEADER ( (b)->properties & AF_BLUE_PROPERTY_LATIN_X_HEIGHT ) #define AF_LATIN_IS_LONG_BLUE( b ) \ ( (b)->properties & AF_BLUE_PROPERTY_LATIN_LONG ) +#define AF_LATIN_IS_CAPITAL_BOTTOM_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_CAPITAL_BOTTOM ) +#define AF_LATIN_IS_SMALL_BOTTOM_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_SMALL_BOTTOM ) #define AF_LATIN_MAX_WIDTHS 16 -#define AF_LATIN_BLUE_ACTIVE ( 1U << 0 ) /* zone height is <= 3/4px */ -#define AF_LATIN_BLUE_TOP ( 1U << 1 ) /* we have a top blue zone */ -#define AF_LATIN_BLUE_SUB_TOP ( 1U << 2 ) /* we have a subscript top */ - /* blue zone */ -#define AF_LATIN_BLUE_NEUTRAL ( 1U << 3 ) /* we have neutral blue zone */ -#define AF_LATIN_BLUE_ADJUSTMENT ( 1U << 4 ) /* used for scale adjustment */ - /* optimization */ +#define AF_LATIN_BLUE_ACTIVE ( 1U << 0 ) /* zone height is <= 3/4px */ +#define AF_LATIN_BLUE_TOP ( 1U << 1 ) /* we have a top blue zone */ +#define AF_LATIN_BLUE_SUB_TOP ( 1U << 2 ) /* we have a subscript */ + /* top blue zone */ +#define AF_LATIN_BLUE_NEUTRAL ( 1U << 3 ) /* we have a neutral blue */ + /* zone */ +#define AF_LATIN_BLUE_ADJUSTMENT ( 1U << 4 ) /* used for scale adjustm. */ + /* optimization */ +#define AF_LATIN_BLUE_BOTTOM ( 1U << 5 ) /* we have a capital */ + /* letter bottom blue zone */ +#define AF_LATIN_BLUE_BOTTOM_SMALL ( 1U << 6 ) /* we have a small letter */ + /* bottom blue zone */ typedef struct AF_LatinBlueRec_ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c index af1d59a6896..d84adc09679 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.c @@ -4,7 +4,7 @@ * * Auto-fitter glyph loading routines (body). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h index 99f0e15f92b..a04b4df0b3b 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afloader.h @@ -4,7 +4,7 @@ * * Auto-fitter glyph loading routines (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c index 726f6ca2b78..22d85a889e8 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.c @@ -4,7 +4,7 @@ * * Auto-fitter module implementation (body). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -146,7 +146,7 @@ if ( !af_style_classes[ss] ) { - FT_TRACE2(( "af_property_set: Invalid value %d for property `%s'\n", + FT_TRACE2(( "af_property_set: Invalid value %u for property `%s'\n", *fallback_script, property_name )); return FT_THROW( Invalid_Argument ); } @@ -412,6 +412,11 @@ module->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4; module->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4; +#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \ + defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC ) + ft_hb_funcs_init( module ); +#endif + return FT_Err_Ok; } @@ -421,6 +426,11 @@ { FT_UNUSED( ft_module ); +#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \ + defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC ) + ft_hb_funcs_done( (AF_Module)ft_module ); +#endif + #ifdef FT_DEBUG_AUTOFIT if ( af_debug_hints_rec_->memory ) af_glyph_hints_done( af_debug_hints_rec_ ); diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h index 91a1abfef1f..c62421ef696 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afmodule.h @@ -4,7 +4,7 @@ * * Auto-fitter module implementation (specification). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -22,6 +22,7 @@ #include #include +#include "ft-hb.h" FT_BEGIN_HEADER @@ -40,6 +41,11 @@ FT_BEGIN_HEADER FT_Bool no_stem_darkening; FT_Int darken_params[8]; +#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \ + defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC ) + ft_hb_funcs_t* hb_funcs; +#endif + } AF_ModuleRec, *AF_Module; diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c index 007b4328189..fd54948f3a5 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.c @@ -4,7 +4,7 @@ * * Auto-fitter Unicode script ranges (body). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -73,9 +73,11 @@ { AF_UNIRANGE_REC( 0x0600, 0x06FF ), /* Arabic */ AF_UNIRANGE_REC( 0x0750, 0x07FF ), /* Arabic Supplement */ + AF_UNIRANGE_REC( 0x0870, 0x089F ), /* Arabic Extended-B */ AF_UNIRANGE_REC( 0x08A0, 0x08FF ), /* Arabic Extended-A */ AF_UNIRANGE_REC( 0xFB50, 0xFDFF ), /* Arabic Presentation Forms-A */ AF_UNIRANGE_REC( 0xFE70, 0xFEFF ), /* Arabic Presentation Forms-B */ + AF_UNIRANGE_REC( 0x10EC0, 0x10EFF ), /* Arabic Extended-C */ AF_UNIRANGE_REC( 0x1EE00, 0x1EEFF ), /* Arabic Mathematical Alphabetic Symbols */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -90,8 +92,9 @@ AF_UNIRANGE_REC( 0x06DF, 0x06E4 ), AF_UNIRANGE_REC( 0x06E7, 0x06E8 ), AF_UNIRANGE_REC( 0x06EA, 0x06ED ), - AF_UNIRANGE_REC( 0x08D4, 0x08E1 ), - AF_UNIRANGE_REC( 0x08D3, 0x08FF ), + AF_UNIRANGE_REC( 0x0897, 0x089F ), + AF_UNIRANGE_REC( 0x08CA, 0x08E1 ), + AF_UNIRANGE_REC( 0x08E3, 0x08FF ), AF_UNIRANGE_REC( 0xFBB2, 0xFBC1 ), AF_UNIRANGE_REC( 0xFE70, 0xFE70 ), AF_UNIRANGE_REC( 0xFE72, 0xFE72 ), @@ -101,6 +104,7 @@ AF_UNIRANGE_REC( 0xFE7A, 0xFE7A ), AF_UNIRANGE_REC( 0xFE7C, 0xFE7C ), AF_UNIRANGE_REC( 0xFE7E, 0xFE7E ), + AF_UNIRANGE_REC( 0x10EFD, 0x10EFF ), AF_UNIRANGE_REC( 0, 0 ) }; @@ -198,8 +202,9 @@ const AF_Script_UniRangeRec af_cans_uniranges[] = { - AF_UNIRANGE_REC( 0x1400, 0x167F ), /* Unified Canadian Aboriginal Syllabics */ - AF_UNIRANGE_REC( 0x18B0, 0x18FF ), /* Unified Canadian Aboriginal Syllabics Extended */ + AF_UNIRANGE_REC( 0x1400, 0x167F ), /* Unified Canadian Aboriginal Syllabics */ + AF_UNIRANGE_REC( 0x18B0, 0x18FF ), /* Unified Canadian Aboriginal Syllabics Extended */ + AF_UNIRANGE_REC( 0x11AB0, 0x11ABF ), /* Unified Canadian Aboriginal Syllabics Extended-A */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -259,6 +264,9 @@ }; + /* TODO: Split off data for new 'cyrb' (subscript) and 'cyrp' */ + /* (superscript) groups (mainly from the Extended-D block), */ + /* in analogy to 'latb' and 'latp'? */ const AF_Script_UniRangeRec af_cyrl_uniranges[] = { AF_UNIRANGE_REC( 0x0400, 0x04FF ), /* Cyrillic */ @@ -266,6 +274,7 @@ AF_UNIRANGE_REC( 0x2DE0, 0x2DFF ), /* Cyrillic Extended-A */ AF_UNIRANGE_REC( 0xA640, 0xA69F ), /* Cyrillic Extended-B */ AF_UNIRANGE_REC( 0x1C80, 0x1C8F ), /* Cyrillic Extended-C */ + AF_UNIRANGE_REC( 0x1E030, 0x1E08F ), /* Cyrillic Extended-D */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -285,15 +294,16 @@ const AF_Script_UniRangeRec af_deva_uniranges[] = { - AF_UNIRANGE_REC( 0x0900, 0x093B ), /* Devanagari */ + AF_UNIRANGE_REC( 0x0900, 0x093B ), /* Devanagari */ /* omitting U+093C nukta */ - AF_UNIRANGE_REC( 0x093D, 0x0950 ), /* ... continued */ + AF_UNIRANGE_REC( 0x093D, 0x0950 ), /* ... continued */ /* omitting U+0951 udatta, U+0952 anudatta */ - AF_UNIRANGE_REC( 0x0953, 0x0963 ), /* ... continued */ + AF_UNIRANGE_REC( 0x0953, 0x0963 ), /* ... continued */ /* omitting U+0964 danda, U+0965 double danda */ - AF_UNIRANGE_REC( 0x0966, 0x097F ), /* ... continued */ - AF_UNIRANGE_REC( 0x20B9, 0x20B9 ), /* (new) Rupee sign */ - AF_UNIRANGE_REC( 0xA8E0, 0xA8FF ), /* Devanagari Extended */ + AF_UNIRANGE_REC( 0x0966, 0x097F ), /* ... continued */ + AF_UNIRANGE_REC( 0x20B9, 0x20B9 ), /* (new) Rupee sign */ + AF_UNIRANGE_REC( 0xA8E0, 0xA8FF ), /* Devanagari Extended */ + AF_UNIRANGE_REC( 0x11B00, 0x11B5F ), /* Devanagari Extended-A */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -329,6 +339,7 @@ AF_UNIRANGE_REC( 0x1380, 0x139F ), /* Ethiopic Supplement */ AF_UNIRANGE_REC( 0x2D80, 0x2DDF ), /* Ethiopic Extended */ AF_UNIRANGE_REC( 0xAB00, 0xAB2F ), /* Ethiopic Extended-A */ + AF_UNIRANGE_REC( 0x1E7E0, 0x1E7FF ), /* Ethiopic Extended-B */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -534,7 +545,7 @@ { AF_UNIRANGE_REC( 0x0EB1, 0x0EB1 ), AF_UNIRANGE_REC( 0x0EB4, 0x0EBC ), - AF_UNIRANGE_REC( 0x0EC8, 0x0ECD ), + AF_UNIRANGE_REC( 0x0EC8, 0x0ECE ), AF_UNIRANGE_REC( 0, 0 ) }; @@ -567,12 +578,15 @@ AF_UNIRANGE_REC( 0x2C7E, 0x2C7F ), /* ... continued */ AF_UNIRANGE_REC( 0x2E00, 0x2E7F ), /* Supplemental Punctuation */ AF_UNIRANGE_REC( 0xA720, 0xA76F ), /* Latin Extended-D */ - AF_UNIRANGE_REC( 0xA771, 0xA7F7 ), /* ... continued */ + AF_UNIRANGE_REC( 0xA771, 0xA7F0 ), /* ... continued */ + AF_UNIRANGE_REC( 0xA7F2, 0xA7F7 ), /* ... continued */ AF_UNIRANGE_REC( 0xA7FA, 0xA7FF ), /* ... continued */ AF_UNIRANGE_REC( 0xAB30, 0xAB5B ), /* Latin Extended-E */ - AF_UNIRANGE_REC( 0xAB60, 0xAB6F ), /* ... continued */ + AF_UNIRANGE_REC( 0xAB60, 0xAB68 ), /* ... continued */ + AF_UNIRANGE_REC( 0xAB6A, 0xAB6F ), /* ... continued */ AF_UNIRANGE_REC( 0xFB00, 0xFB06 ), /* Alphab. Present. Forms (Latin Ligs) */ AF_UNIRANGE_REC( 0x1D400, 0x1D7FF ), /* Mathematical Alphanumeric Symbols */ + AF_UNIRANGE_REC( 0x1DF00, 0x1DFFF ), /* Latin Extended-G */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -588,7 +602,7 @@ AF_UNIRANGE_REC( 0x02B9, 0x02DF ), AF_UNIRANGE_REC( 0x02E5, 0x02FF ), AF_UNIRANGE_REC( 0x0300, 0x036F ), - AF_UNIRANGE_REC( 0x1AB0, 0x1ABE ), + AF_UNIRANGE_REC( 0x1AB0, 0x1AEB ), AF_UNIRANGE_REC( 0x1DC0, 0x1DFF ), AF_UNIRANGE_REC( 0x2017, 0x2017 ), AF_UNIRANGE_REC( 0x203E, 0x203E ), @@ -625,8 +639,11 @@ AF_UNIRANGE_REC( 0x2070, 0x207F ), /* superscript digits and letters */ AF_UNIRANGE_REC( 0x2C7D, 0x2C7D ), /* modifier letter capital v */ AF_UNIRANGE_REC( 0xA770, 0xA770 ), /* modifier letter us */ + AF_UNIRANGE_REC( 0xA7F1, 0xA7F1 ), /* modifier letter capital s */ AF_UNIRANGE_REC( 0xA7F8, 0xA7F9 ), /* more modifier letters */ AF_UNIRANGE_REC( 0xAB5C, 0xAB5F ), /* more modifier letters */ + AF_UNIRANGE_REC( 0xAB69, 0xAB69 ), /* modifier letter small turned w */ + AF_UNIRANGE_REC( 0x10780, 0x107FB ), /* Latin Extended-F */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -638,7 +655,8 @@ const AF_Script_UniRangeRec af_lisu_uniranges[] = { - AF_UNIRANGE_REC( 0xA4D0, 0xA4FF ), /* Lisu */ + AF_UNIRANGE_REC( 0xA4D0, 0xA4FF ), /* Lisu */ + AF_UNIRANGE_REC( 0x11FB0, 0x11FBF ), /* Lisu Supplement */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -696,6 +714,7 @@ AF_UNIRANGE_REC( 0x1000, 0x109F ), /* Myanmar */ AF_UNIRANGE_REC( 0xA9E0, 0xA9FF ), /* Myanmar Extended-B */ AF_UNIRANGE_REC( 0xAA60, 0xAA7F ), /* Myanmar Extended-A */ + AF_UNIRANGE_REC( 0x116D0, 0x116FF ), /* Myanmar Extended-C */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -836,6 +855,7 @@ const AF_Script_UniRangeRec af_sinh_nonbase_uniranges[] = { + AF_UNIRANGE_REC( 0x0D81, 0x0D81 ), AF_UNIRANGE_REC( 0x0DCA, 0x0DCA ), AF_UNIRANGE_REC( 0x0DD2, 0x0DD6 ), AF_UNIRANGE_REC( 0, 0 ) @@ -859,7 +879,8 @@ const AF_Script_UniRangeRec af_taml_uniranges[] = { - AF_UNIRANGE_REC( 0x0B80, 0x0BFF ), /* Tamil */ + AF_UNIRANGE_REC( 0x0B80, 0x0BFF ), /* Tamil */ + AF_UNIRANGE_REC( 0x11FC0, 0x11FFF ), /* Tamil Supplement */ AF_UNIRANGE_REC( 0, 0 ) }; @@ -899,6 +920,7 @@ { AF_UNIRANGE_REC( 0x0C00, 0x0C00 ), AF_UNIRANGE_REC( 0x0C04, 0x0C04 ), + AF_UNIRANGE_REC( 0x0C3C, 0x0C3C ), AF_UNIRANGE_REC( 0x0C3E, 0x0C40 ), AF_UNIRANGE_REC( 0x0C46, 0x0C56 ), AF_UNIRANGE_REC( 0x0C62, 0x0C63 ), @@ -992,6 +1014,7 @@ AF_UNIRANGE_REC( 0xA806, 0xA806 ), AF_UNIRANGE_REC( 0xA80B, 0xA80B ), AF_UNIRANGE_REC( 0xA825, 0xA826 ), + AF_UNIRANGE_REC( 0xA82C, 0xA82C ), AF_UNIRANGE_REC( 0, 0 ) }; @@ -1048,15 +1071,21 @@ AF_UNIRANGE_REC( 0xFE10, 0xFE1F ), /* Vertical forms */ AF_UNIRANGE_REC( 0xFE30, 0xFE4F ), /* CJK Compatibility Forms */ AF_UNIRANGE_REC( 0xFF00, 0xFFEF ), /* Halfwidth and Fullwidth Forms */ + AF_UNIRANGE_REC( 0x1AFF0, 0x1AFFF ), /* Kana Extended-B */ AF_UNIRANGE_REC( 0x1B000, 0x1B0FF ), /* Kana Supplement */ AF_UNIRANGE_REC( 0x1B100, 0x1B12F ), /* Kana Extended-A */ + AF_UNIRANGE_REC( 0x1B130, 0x1B16F ), /* Small Kana Extension */ AF_UNIRANGE_REC( 0x1D300, 0x1D35F ), /* Tai Xuan Hing Symbols */ AF_UNIRANGE_REC( 0x20000, 0x2A6DF ), /* CJK Unified Ideographs Extension B */ AF_UNIRANGE_REC( 0x2A700, 0x2B73F ), /* CJK Unified Ideographs Extension C */ AF_UNIRANGE_REC( 0x2B740, 0x2B81F ), /* CJK Unified Ideographs Extension D */ AF_UNIRANGE_REC( 0x2B820, 0x2CEAF ), /* CJK Unified Ideographs Extension E */ AF_UNIRANGE_REC( 0x2CEB0, 0x2EBEF ), /* CJK Unified Ideographs Extension F */ + AF_UNIRANGE_REC( 0x2EBF0, 0x2EE5D ), /* CJK Unified Ideographs Extension I */ AF_UNIRANGE_REC( 0x2F800, 0x2FA1F ), /* CJK Compatibility Ideographs Supplement */ + AF_UNIRANGE_REC( 0x30000, 0x3134A ), /* CJK Unified Ideographs Extension G */ + AF_UNIRANGE_REC( 0x31350, 0x323AF ), /* CJK Unified Ideographs Extension H */ + AF_UNIRANGE_REC( 0x323B0, 0x33479 ), /* CJK Unified Ideographs Extension J */ AF_UNIRANGE_REC( 0, 0 ) }; diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h index 813b3ee78ef..fa00eb75046 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afranges.h @@ -4,7 +4,7 @@ * * Auto-fitter Unicode script ranges (specification). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h b/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h index 0a83d771501..5c4cbbcb922 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afscript.h @@ -4,7 +4,7 @@ * * Auto-fitter scripts (specification only). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c index df0f46ada89..f3c0744fd9d 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.c @@ -4,7 +4,7 @@ * * HarfBuzz interface for accessing OpenType features (body). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -22,8 +22,8 @@ #include "aftypes.h" #include "afshaper.h" -#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ /************************************************************************** * @@ -89,17 +89,18 @@ #define SCRIPT( s, S, d, h, H, ss ) h, - static const hb_script_t scripts[] = + FT_LOCAL_ARRAY_DEF( hb_script_t ) + af_hb_scripts[] = { #include "afscript.h" }; - FT_Error - af_shaper_get_coverage( AF_FaceGlobals globals, - AF_StyleClass style_class, - FT_UShort* gstyles, - FT_Bool default_script ) + static FT_Error + af_shaper_get_coverage_hb( AF_FaceGlobals globals, + AF_StyleClass style_class, + FT_UShort* gstyles, + FT_Bool default_script ) { hb_face_t* face; @@ -124,10 +125,10 @@ if ( !globals || !style_class || !gstyles ) return FT_THROW( Invalid_Argument ); - face = hb_font_get_face( globals->hb_font ); + face = hb( font_get_face )( globals->hb_font ); coverage_tags = coverages[style_class->coverage]; - script = scripts[style_class->script]; + script = af_hb_scripts[style_class->script]; /* Convert a HarfBuzz script tag into the corresponding OpenType */ /* tag or tags -- some Indic scripts like Devanagari have an old */ @@ -137,19 +138,19 @@ hb_tag_t tags[3]; - hb_ot_tags_from_script_and_language( script, - HB_LANGUAGE_INVALID, - &tags_count, - tags, - NULL, - NULL ); + hb( ot_tags_from_script_and_language )( script, + HB_LANGUAGE_INVALID, + &tags_count, + tags, + NULL, + NULL ); script_tags[0] = tags_count > 0 ? tags[0] : HB_TAG_NONE; script_tags[1] = tags_count > 1 ? tags[1] : HB_TAG_NONE; script_tags[2] = tags_count > 2 ? tags[2] : HB_TAG_NONE; } - /* If the second tag is HB_OT_TAG_DEFAULT_SCRIPT, change that to */ - /* HB_TAG_NONE except for the default script. */ + /* If the second tag is HB_OT_TAG_DEFAULT_SCRIPT, change that to */ + /* HB_TAG_NONE except for the default script. */ if ( default_script ) { if ( script_tags[0] == HB_TAG_NONE ) @@ -170,15 +171,15 @@ goto Exit; } - gsub_lookups = hb_set_create(); - hb_ot_layout_collect_lookups( face, - HB_OT_TAG_GSUB, - script_tags, - NULL, - coverage_tags, - gsub_lookups ); + gsub_lookups = hb( set_create )(); + hb( ot_layout_collect_lookups )( face, + HB_OT_TAG_GSUB, + script_tags, + NULL, + coverage_tags, + gsub_lookups ); - if ( hb_set_is_empty( gsub_lookups ) ) + if ( hb( set_is_empty )( gsub_lookups ) ) goto Exit; /* nothing to do */ FT_TRACE4(( "GSUB lookups (style `%s'):\n", @@ -189,22 +190,22 @@ count = 0; #endif - gsub_glyphs = hb_set_create(); - for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); ) + gsub_glyphs = hb( set_create )(); + for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gsub_lookups, &idx ); ) { #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE4(( " %d", idx )); + FT_TRACE4(( " %u", idx )); count++; #endif /* get output coverage of GSUB feature */ - hb_ot_layout_lookup_collect_glyphs( face, - HB_OT_TAG_GSUB, - idx, - NULL, - NULL, - NULL, - gsub_glyphs ); + hb( ot_layout_lookup_collect_glyphs )( face, + HB_OT_TAG_GSUB, + idx, + NULL, + NULL, + NULL, + gsub_glyphs ); } #ifdef FT_DEBUG_LEVEL_TRACE @@ -218,34 +219,34 @@ af_style_names[style_class->style] )); FT_TRACE4(( " " )); - gpos_lookups = hb_set_create(); - hb_ot_layout_collect_lookups( face, - HB_OT_TAG_GPOS, - script_tags, - NULL, - coverage_tags, - gpos_lookups ); + gpos_lookups = hb( set_create )(); + hb( ot_layout_collect_lookups )( face, + HB_OT_TAG_GPOS, + script_tags, + NULL, + coverage_tags, + gpos_lookups ); #ifdef FT_DEBUG_LEVEL_TRACE count = 0; #endif - gpos_glyphs = hb_set_create(); - for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); ) + gpos_glyphs = hb( set_create )(); + for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gpos_lookups, &idx ); ) { #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE4(( " %d", idx )); + FT_TRACE4(( " %u", idx )); count++; #endif /* get input coverage of GPOS feature */ - hb_ot_layout_lookup_collect_glyphs( face, - HB_OT_TAG_GPOS, - idx, - NULL, - gpos_glyphs, - NULL, - NULL ); + hb( ot_layout_lookup_collect_glyphs )( face, + HB_OT_TAG_GPOS, + idx, + NULL, + gpos_glyphs, + NULL, + NULL ); } #ifdef FT_DEBUG_LEVEL_TRACE @@ -281,14 +282,14 @@ GET_UTF8_CHAR( ch, p ); - for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, - &idx ); ) + for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gsub_lookups, + &idx ); ) { hb_codepoint_t gidx = FT_Get_Char_Index( globals->face, ch ); - if ( hb_ot_layout_lookup_would_substitute( face, idx, - &gidx, 1, 1 ) ) + if ( hb( ot_layout_lookup_would_substitute )( face, idx, + &gidx, 1, 1 ) ) { found = 1; break; @@ -352,14 +353,14 @@ * */ if ( style_class->coverage != AF_COVERAGE_DEFAULT ) - hb_set_subtract( gsub_glyphs, gpos_glyphs ); + hb( set_subtract )( gsub_glyphs, gpos_glyphs ); #ifdef FT_DEBUG_LEVEL_TRACE FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" )); count = 0; #endif - for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); ) + for ( idx = HB_SET_VALUE_INVALID; hb( set_next )( gsub_glyphs, &idx ); ) { #ifdef FT_DEBUG_LEVEL_TRACE if ( !( count % 10 ) ) @@ -368,7 +369,7 @@ FT_TRACE4(( " " )); } - FT_TRACE4(( " %d", idx )); + FT_TRACE4(( " %u", idx )); count++; #endif @@ -397,10 +398,10 @@ #endif Exit: - hb_set_destroy( gsub_lookups ); - hb_set_destroy( gsub_glyphs ); - hb_set_destroy( gpos_lookups ); - hb_set_destroy( gpos_glyphs ); + hb( set_destroy )( gsub_lookups ); + hb( set_destroy )( gsub_glyphs ); + hb( set_destroy )( gpos_lookups ); + hb( set_destroy )( gpos_glyphs ); return FT_Err_Ok; } @@ -437,31 +438,33 @@ }; - void* - af_shaper_buf_create( FT_Face face ) + static void* + af_shaper_buf_create_hb( AF_FaceGlobals globals ) { - FT_UNUSED( face ); + FT_UNUSED( globals ); - return (void*)hb_buffer_create(); + return (void*)hb( buffer_create )(); } - void - af_shaper_buf_destroy( FT_Face face, - void* buf ) + static void + af_shaper_buf_destroy_hb( AF_FaceGlobals globals, + void* buf ) { - FT_UNUSED( face ); + FT_UNUSED( globals ); - hb_buffer_destroy( (hb_buffer_t*)buf ); + hb( buffer_destroy )( (hb_buffer_t*)buf ); } - const char* - af_shaper_get_cluster( const char* p, - AF_StyleMetrics metrics, - void* buf_, - unsigned int* count ) + static const char* + af_shaper_get_cluster_hb( const char* p, + AF_StyleMetrics metrics, + void* buf_, + unsigned int* count ) { + AF_FaceGlobals globals = metrics->globals; + AF_StyleClass style_class; const hb_feature_t* feature; FT_Int upem; @@ -472,6 +475,8 @@ hb_font_t* font; hb_codepoint_t dummy; + FT_UNUSED( globals ); + upem = (FT_Int)metrics->globals->face->units_per_EM; style_class = metrics->style_class; @@ -480,7 +485,7 @@ font = metrics->globals->hb_font; /* we shape at a size of units per EM; this means font units */ - hb_font_set_scale( font, upem, upem ); + hb( font_set_scale )( font, upem, upem ); while ( *p == ' ' ) p++; @@ -492,15 +497,15 @@ len = (int)( q - p ); /* feed character(s) to the HarfBuzz buffer */ - hb_buffer_clear_contents( buf ); - hb_buffer_add_utf8( buf, p, len, 0, len ); + hb( buffer_clear_contents )( buf ); + hb( buffer_add_utf8 )( buf, p, len, 0, len ); /* we let HarfBuzz guess the script and writing direction */ - hb_buffer_guess_segment_properties( buf ); + hb( buffer_guess_segment_properties )( buf ); /* shape buffer, which means conversion from character codes to */ /* glyph indices, possibly applying a feature */ - hb_shape( font, buf, feature, feature ? 1 : 0 ); + hb( shape )( font, buf, feature, feature ? 1 : 0 ); if ( feature ) { @@ -517,13 +522,13 @@ /* glyph indices; otherwise the affected glyph or glyphs aren't */ /* available at all in the feature */ - hb_buffer_clear_contents( hb_buf ); - hb_buffer_add_utf8( hb_buf, p, len, 0, len ); - hb_buffer_guess_segment_properties( hb_buf ); - hb_shape( font, hb_buf, NULL, 0 ); + hb( buffer_clear_contents )( hb_buf ); + hb( buffer_add_utf8 )( hb_buf, p, len, 0, len ); + hb( buffer_guess_segment_properties )( hb_buf ); + hb( shape )( font, hb_buf, NULL, 0 ); - ginfo = hb_buffer_get_glyph_infos( buf, &gcount ); - hb_ginfo = hb_buffer_get_glyph_infos( hb_buf, &hb_gcount ); + ginfo = hb( buffer_get_glyph_infos )( buf, &gcount ); + hb_ginfo = hb( buffer_get_glyph_infos )( hb_buf, &hb_gcount ); if ( gcount == hb_gcount ) { @@ -537,12 +542,12 @@ if ( i == gcount ) { /* both buffers have identical glyph indices */ - hb_buffer_clear_contents( buf ); + hb( buffer_clear_contents )( buf ); } } } - *count = hb_buffer_get_length( buf ); + *count = hb( buffer_get_length )( buf ); #ifdef FT_DEBUG_LEVEL_TRACE if ( feature && *count > 1 ) @@ -554,23 +559,25 @@ } - FT_ULong - af_shaper_get_elem( AF_StyleMetrics metrics, - void* buf_, - unsigned int idx, - FT_Long* advance, - FT_Long* y_offset ) + static FT_ULong + af_shaper_get_elem_hb( AF_StyleMetrics metrics, + void* buf_, + unsigned int idx, + FT_Long* advance, + FT_Long* y_offset ) { + AF_FaceGlobals globals = metrics->globals; + hb_buffer_t* buf = (hb_buffer_t*)buf_; hb_glyph_info_t* ginfo; hb_glyph_position_t* gpos; unsigned int gcount; - FT_UNUSED( metrics ); + FT_UNUSED( globals ); - ginfo = hb_buffer_get_glyph_infos( buf, &gcount ); - gpos = hb_buffer_get_glyph_positions( buf, &gcount ); + ginfo = hb( buffer_get_glyph_infos )( buf, &gcount ); + gpos = hb( buffer_get_glyph_positions )( buf, &gcount ); if ( idx >= gcount ) return 0; @@ -584,14 +591,14 @@ } -#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */ +#endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */ - FT_Error - af_shaper_get_coverage( AF_FaceGlobals globals, - AF_StyleClass style_class, - FT_UShort* gstyles, - FT_Bool default_script ) + static FT_Error + af_shaper_get_coverage_nohb( AF_FaceGlobals globals, + AF_StyleClass style_class, + FT_UShort* gstyles, + FT_Bool default_script ) { FT_UNUSED( globals ); FT_UNUSED( style_class ); @@ -602,29 +609,29 @@ } - void* - af_shaper_buf_create( FT_Face face ) + static void* + af_shaper_buf_create_nohb( AF_FaceGlobals globals ) { - FT_UNUSED( face ); + FT_UNUSED( globals ); return NULL; } - void - af_shaper_buf_destroy( FT_Face face, - void* buf ) + static void + af_shaper_buf_destroy_nohb( AF_FaceGlobals globals, + void* buf ) { - FT_UNUSED( face ); + FT_UNUSED( globals ); FT_UNUSED( buf ); } - const char* - af_shaper_get_cluster( const char* p, - AF_StyleMetrics metrics, - void* buf_, - unsigned int* count ) + static const char* + af_shaper_get_cluster_nohb( const char* p, + AF_StyleMetrics metrics, + void* buf_, + unsigned int* count ) { FT_Face face = metrics->globals->face; FT_ULong ch, dummy = 0; @@ -656,12 +663,12 @@ } - FT_ULong - af_shaper_get_elem( AF_StyleMetrics metrics, - void* buf_, - unsigned int idx, - FT_Long* advance, - FT_Long* y_offset ) + static FT_ULong + af_shaper_get_elem_nohb( AF_StyleMetrics metrics, + void* buf_, + unsigned int idx, + FT_Long* advance, + FT_Long* y_offset ) { FT_Face face = metrics->globals->face; FT_ULong glyph_index = *(FT_ULong*)buf_; @@ -684,7 +691,90 @@ } -#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */ + /********************************************************************/ + + FT_Error + af_shaper_get_coverage( AF_FaceGlobals globals, + AF_StyleClass style_class, + FT_UShort* gstyles, + FT_Bool default_script ) + { +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + if ( ft_hb_enabled( globals ) ) + return af_shaper_get_coverage_hb( globals, + style_class, + gstyles, + default_script ); + else +#endif + return af_shaper_get_coverage_nohb( globals, + style_class, + gstyles, + default_script ); + } + + + void* + af_shaper_buf_create( AF_FaceGlobals globals ) + { +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + if ( ft_hb_enabled( globals ) ) + return af_shaper_buf_create_hb( globals ); + else +#endif + return af_shaper_buf_create_nohb( globals ); + } + + + void + af_shaper_buf_destroy( AF_FaceGlobals globals, + void* buf ) + { +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + if ( ft_hb_enabled( globals ) ) + af_shaper_buf_destroy_hb( globals, buf ); + else +#endif + af_shaper_buf_destroy_nohb( globals, buf ); + } + + + const char* + af_shaper_get_cluster( const char* p, + AF_StyleMetrics metrics, + void* buf_, + unsigned int* count ) + { +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + if ( ft_hb_enabled( metrics->globals ) ) + return af_shaper_get_cluster_hb( p, metrics, buf_, count ); + else +#endif + return af_shaper_get_cluster_nohb( p, metrics, buf_, count ); + } + + + FT_ULong + af_shaper_get_elem( AF_StyleMetrics metrics, + void* buf_, + unsigned int idx, + FT_Long* advance, + FT_Long* y_offset ) + { +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + if ( ft_hb_enabled( metrics->globals ) ) + return af_shaper_get_elem_hb( metrics, + buf_, + idx, + advance, + y_offset ); +#endif + return af_shaper_get_elem_nohb( metrics, + buf_, + idx, + advance, + y_offset ); + } /* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h index 2eb03bb5d98..757368fc9c0 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afshaper.h @@ -4,7 +4,7 @@ * * HarfBuzz interface for accessing OpenType features (specification). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -23,17 +23,14 @@ #include +FT_BEGIN_HEADER + #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ - -#include -#include -#include "ft-hb.h" - + FT_LOCAL_ARRAY( hb_script_t ) + af_hb_scripts[]; #endif -FT_BEGIN_HEADER - FT_Error af_shaper_get_coverage( AF_FaceGlobals globals, AF_StyleClass style_class, @@ -42,11 +39,11 @@ FT_BEGIN_HEADER void* - af_shaper_buf_create( FT_Face face ); + af_shaper_buf_create( AF_FaceGlobals globals ); void - af_shaper_buf_destroy( FT_Face face, - void* buf ); + af_shaper_buf_destroy( AF_FaceGlobals globals, + void* buf ); const char* af_shaper_get_cluster( const char* p, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h b/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h index 7a33f37a856..206232efe25 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afstyles.h @@ -4,7 +4,7 @@ * * Auto-fitter styles (specification only). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -50,36 +50,36 @@ AF_COVERAGE_ ## C ) #undef META_STYLE_LATIN -#define META_STYLE_LATIN( s, S, ds ) \ - STYLE_LATIN( s, S, c2cp, C2CP, ds, \ +#define META_STYLE_LATIN( s, S, ds ) \ + STYLE_LATIN( s, S, c2cp, C2CP, ds, \ "petite capitals from capitals", \ - PETITE_CAPITALS_FROM_CAPITALS ) \ - STYLE_LATIN( s, S, c2sc, C2SC, ds, \ + PETITE_CAPITALS_FROM_CAPITALS ) \ + STYLE_LATIN( s, S, c2sc, C2SC, ds, \ "small capitals from capitals", \ - SMALL_CAPITALS_FROM_CAPITALS ) \ - STYLE_LATIN( s, S, ordn, ORDN, ds, \ - "ordinals", \ - ORDINALS ) \ - STYLE_LATIN( s, S, pcap, PCAP, ds, \ - "petite capitals", \ - PETITE_CAPITALS ) \ - STYLE_LATIN( s, S, sinf, SINF, ds, \ - "scientific inferiors", \ - SCIENTIFIC_INFERIORS ) \ - STYLE_LATIN( s, S, smcp, SMCP, ds, \ - "small capitals", \ - SMALL_CAPITALS ) \ - STYLE_LATIN( s, S, subs, SUBS, ds, \ - "subscript", \ - SUBSCRIPT ) \ - STYLE_LATIN( s, S, sups, SUPS, ds, \ - "superscript", \ - SUPERSCRIPT ) \ - STYLE_LATIN( s, S, titl, TITL, ds, \ - "titling", \ - TITLING ) \ - STYLE_LATIN( s, S, dflt, DFLT, ds, \ - "default", \ + SMALL_CAPITALS_FROM_CAPITALS ) \ + STYLE_LATIN( s, S, ordn, ORDN, ds, \ + "ordinals", \ + ORDINALS ) \ + STYLE_LATIN( s, S, pcap, PCAP, ds, \ + "petite capitals", \ + PETITE_CAPITALS ) \ + STYLE_LATIN( s, S, sinf, SINF, ds, \ + "scientific inferiors", \ + SCIENTIFIC_INFERIORS ) \ + STYLE_LATIN( s, S, smcp, SMCP, ds, \ + "small capitals", \ + SMALL_CAPITALS ) \ + STYLE_LATIN( s, S, subs, SUBS, ds, \ + "subscript", \ + SUBSCRIPT ) \ + STYLE_LATIN( s, S, sups, SUPS, ds, \ + "superscript", \ + SUPERSCRIPT ) \ + STYLE_LATIN( s, S, titl, TITL, ds, \ + "titling", \ + TITLING ) \ + STYLE_LATIN( s, S, dflt, DFLT, ds, \ + "default", \ DEFAULT ) diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h b/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h index 27e4185e9f8..959640a12ec 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/aftypes.h @@ -4,7 +4,7 @@ * * Auto-fitter types (specification only). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -406,6 +407,7 @@ extern void* af_debug_hints_; typedef struct AF_FaceGlobalsRec_* AF_FaceGlobals; + /* This is the main structure that combines everything. Autofit modules */ /* specific to writing systems derive their structures from it, for */ /* example `AF_LatinMetrics'. */ @@ -418,6 +420,8 @@ extern void* af_debug_hints_; AF_FaceGlobals globals; /* to access properties */ + FT_Hash reverse_charmap; + } AF_StyleMetricsRec; diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h b/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h index b78745af74e..12fa7a27a2b 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afws-decl.h @@ -4,7 +4,7 @@ * * Auto-fitter writing system declarations (specification only). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h b/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h index c86d609a352..1752697b375 100644 --- a/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h +++ b/src/java.desktop/share/native/libfreetype/src/autofit/afws-iter.h @@ -4,7 +4,7 @@ * * Auto-fitter writing systems iterator (specification only). * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.c b/src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.c new file mode 100644 index 00000000000..3c145d04640 --- /dev/null +++ b/src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.c @@ -0,0 +1,197 @@ +/**************************************************************************** + * + * ft-hb.c + * + * FreeType-HarfBuzz bridge (body). + * + * Copyright (C) 2025 by + * Behdad Esfahbod. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + + +#if !defined( _WIN32 ) && !defined( _GNU_SOURCE ) +# define _GNU_SOURCE 1 /* for RTLD_DEFAULT */ +#endif + +#include +#include + +#include "afglobal.h" + +#include "ft-hb.h" + + +#if defined( FT_CONFIG_OPTION_USE_HARFBUZZ ) && \ + defined( FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC ) + +#ifndef FT_LIBHARFBUZZ +# ifdef _WIN32 +# define FT_LIBHARFBUZZ "libharfbuzz-0.dll" +# else +# ifdef __APPLE__ +# define FT_LIBHARFBUZZ "libharfbuzz.0.dylib" +# else +# define FT_LIBHARFBUZZ "libharfbuzz.so.0" +# endif +# endif +#endif + +#ifdef _WIN32 + +# include + +#else /* !_WIN32 */ + +# include + + /* The GCC pragma suppresses the warning "ISO C forbids */ + /* assignment between function pointer and 'void *'", which */ + /* inevitably gets emitted with `-Wpedantic`; see the man */ + /* page of function `dlsym` for more information. */ +# if defined( __GNUC__ ) +# pragma GCC diagnostic push +# ifndef __cplusplus +# pragma GCC diagnostic ignored "-Wpedantic" +# endif +# endif + +#endif /* !_WIN32 */ + + + FT_LOCAL_DEF( void ) + ft_hb_funcs_init( struct AF_ModuleRec_ *af_module ) + { + FT_Memory memory = af_module->root.memory; + FT_Error error; + + ft_hb_funcs_t *funcs = NULL; + ft_hb_version_atleast_func_t version_atleast = NULL; + +#ifdef _WIN32 + HANDLE lib; +# define DLSYM( lib, name ) \ + (ft_ ## name ## _func_t)GetProcAddress( lib, #name ) +#else + void *lib; +# define DLSYM( lib, name ) \ + (ft_ ## name ## _func_t)dlsym( lib, #name ) +#endif + + + af_module->hb_funcs = NULL; + + if ( FT_NEW( funcs ) ) + return; + FT_ZERO( funcs ); + +#ifdef _WIN32 + + lib = LoadLibraryA( FT_LIBHARFBUZZ ); + if ( !lib ) + goto Fail; + version_atleast = DLSYM( lib, hb_version_atleast ); + +#else /* !_WIN32 */ + +# ifdef RTLD_DEFAULT +# define FT_RTLD_FLAGS RTLD_LAZY | RTLD_GLOBAL + lib = RTLD_DEFAULT; + version_atleast = DLSYM( lib, hb_version_atleast ); +# else +# define FT_RTLD_FLAGS RTLD_LAZY +# endif + + if ( !version_atleast ) + { + /* Load the HarfBuzz library. + * + * We never close the library, since we opened it with RTLD_GLOBAL. + * This is important for the case where we are using HarfBuzz as a + * shared library, and we want to use the symbols from the library in + * other shared libraries or clients. HarfBuzz holds onto global + * variables, and closing the library will cause them to be + * invalidated. + */ + lib = dlopen( FT_LIBHARFBUZZ, FT_RTLD_FLAGS ); + if ( !lib ) + goto Fail; + version_atleast = DLSYM( lib, hb_version_atleast ); + } + +#endif /* !_WIN32 */ + + if ( !version_atleast ) + goto Fail; + + /* Load all symbols we use. */ +#define HB_EXTERN( ret, name, args ) \ + { \ + funcs->name = DLSYM( lib, name ); \ + if ( !funcs->name ) \ + goto Fail; \ + } +#include "ft-hb-decls.h" +#undef HB_EXTERN + +#undef DLSYM + + af_module->hb_funcs = funcs; + return; + + Fail: + if ( funcs ) + FT_FREE( funcs ); + } + + + FT_LOCAL_DEF( void ) + ft_hb_funcs_done( struct AF_ModuleRec_ *af_module ) + { + FT_Memory memory = af_module->root.memory; + + + if ( af_module->hb_funcs ) + { + FT_FREE( af_module->hb_funcs ); + af_module->hb_funcs = NULL; + } + } + + + FT_LOCAL_DEF( FT_Bool ) + ft_hb_enabled( struct AF_FaceGlobalsRec_ *globals ) + { + return globals->module->hb_funcs != NULL; + } + +#ifndef _WIN32 +# if defined( __GNUC__ ) +# pragma GCC diagnostic pop +# endif +#endif + +#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */ + + FT_LOCAL_DEF( FT_Bool ) + ft_hb_enabled( struct AF_FaceGlobalsRec_ *globals ) + { + FT_UNUSED( globals ); + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + return TRUE; +#else + return FALSE; +#endif + } + +#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */ + + +/* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.h b/src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.h new file mode 100644 index 00000000000..95914deb8d3 --- /dev/null +++ b/src/java.desktop/share/native/libfreetype/src/autofit/ft-hb.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * + * ft-hb.h + * + * FreeType-HarfBuzz bridge (specification). + * + * Copyright (C) 2025 by + * Behdad Esfahbod. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + + +#ifndef FT_HB_H +#define FT_HB_H + +#include +#include + + +FT_BEGIN_HEADER + +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + +# include "ft-hb-types.h" + +# ifdef FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC + +# define HB_EXTERN( ret, name, args ) \ + typedef ret (*ft_ ## name ## _func_t) args; +# include "ft-hb-decls.h" +# undef HB_EXTERN + + typedef struct ft_hb_funcs_t + { +# define HB_EXTERN( ret, name, args ) \ + ft_ ## name ## _func_t name; +# include "ft-hb-decls.h" +# undef HB_EXTERN + } ft_hb_funcs_t; + + struct AF_ModuleRec_; + + FT_LOCAL( void ) + ft_hb_funcs_init( struct AF_ModuleRec_ *af_module ); + + FT_LOCAL( void ) + ft_hb_funcs_done( struct AF_ModuleRec_ *af_module ); + +# define hb( x ) globals->module->hb_funcs->hb_ ## x + +# else /* !FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */ + +# define HB_EXTERN( ret, name, args ) \ + ret name args; +# include "ft-hb-decls.h" +# undef HB_EXTERN + +# define hb( x ) hb_ ## x + +# endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC */ + +#endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */ + + + struct AF_FaceGlobalsRec_; + + FT_LOCAL( FT_Bool ) + ft_hb_enabled( struct AF_FaceGlobalsRec_ *globals ); + + +FT_END_HEADER + +#endif /* FT_HB_H */ + + +/* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c b/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c index 717f7d08b35..ff0fc510503 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftadvanc.c @@ -4,7 +4,7 @@ * * Quick computation of advance widths (body). * - * Copyright (C) 2008-2024 by + * Copyright (C) 2008-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftbase.h b/src/java.desktop/share/native/libfreetype/src/base/ftbase.h index 1d98b26dd51..66f091165fe 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftbase.h +++ b/src/java.desktop/share/native/libfreetype/src/base/ftbase.h @@ -4,7 +4,7 @@ * * Private functions used in the `base' module (specification). * - * Copyright (C) 2008-2024 by + * Copyright (C) 2008-2025 by * David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya. * * This file is part of the FreeType project, and may only be used, @@ -34,7 +34,7 @@ FT_BEGIN_HEADER #ifdef FT_CONFIG_OPTION_MAC_FONTS /* MacOS resource fork cannot exceed 16MB at least for Carbon code; */ - /* see https://support.microsoft.com/en-us/kb/130437 */ + /* see https://jeffpar.github.io/kbarchive/kb/130/Q130437/ */ #define FT_MAC_RFORK_MAX_LEN 0x00FFFFFFUL diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c b/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c index d6aa5d56df8..feccdee5dd7 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftbbox.c @@ -4,7 +4,7 @@ * * FreeType bbox computation (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c b/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c index 4be145679fd..2c8e44b905d 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftbitmap.c @@ -4,7 +4,7 @@ * * FreeType utility functions for bitmaps (body). * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -876,13 +876,13 @@ #ifdef FT_DEBUG_LEVEL_TRACE FT_TRACE5(( "FT_Bitmap_Blend:\n" )); - FT_TRACE5(( " source bitmap: (%ld, %ld) -- (%ld, %ld); %d x %d\n", + FT_TRACE5(( " source bitmap: (%ld, %ld) -- (%ld, %ld); %u x %u\n", source_llx / 64, source_lly / 64, source_urx / 64, source_ury / 64, source_->width, source_->rows )); if ( target->width && target->rows ) - FT_TRACE5(( " target bitmap: (%ld, %ld) -- (%ld, %ld); %d x %d\n", + FT_TRACE5(( " target bitmap: (%ld, %ld) -- (%ld, %ld); %u x %u\n", target_llx / 64, target_lly / 64, target_urx / 64, target_ury / 64, target->width, target->rows )); @@ -890,7 +890,7 @@ FT_TRACE5(( " target bitmap: empty\n" )); if ( final_width && final_rows ) - FT_TRACE5(( " final bitmap: (%ld, %ld) -- (%ld, %ld); %d x %d\n", + FT_TRACE5(( " final bitmap: (%ld, %ld) -- (%ld, %ld); %u x %u\n", final_llx / 64, final_lly / 64, final_urx / 64, final_ury / 64, final_width, final_rows )); @@ -924,7 +924,7 @@ if ( FT_LONG_MAX / target->pitch < (int)target->rows ) { - FT_TRACE5(( "FT_Blend_Bitmap: target bitmap too large (%d x %d)\n", + FT_TRACE5(( "FT_Blend_Bitmap: target bitmap too large (%u x %u)\n", final_width, final_rows )); return FT_THROW( Invalid_Argument ); } @@ -952,7 +952,7 @@ if ( FT_LONG_MAX / new_pitch < (int)final_rows ) { - FT_TRACE5(( "FT_Blend_Bitmap: target bitmap too large (%d x %d)\n", + FT_TRACE5(( "FT_Blend_Bitmap: target bitmap too large (%u x %u)\n", final_width, final_rows )); return FT_THROW( Invalid_Argument ); } diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c b/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c index 92de09ed877..7d6e12e2543 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftcalc.c @@ -4,7 +4,7 @@ * * Arithmetic computations (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -38,24 +38,11 @@ #include #include - -#ifdef FT_MULFIX_ASSEMBLER -#undef FT_MulFix + /* cancel inlining macro from internal/ftcalc.h */ +#ifdef FT_MulFix +# undef FT_MulFix #endif -/* we need to emulate a 64-bit data type if a real one isn't available */ - -#ifndef FT_INT64 - - typedef struct FT_Int64_ - { - FT_UInt32 lo; - FT_UInt32 hi; - - } FT_Int64; - -#endif /* !FT_INT64 */ - /************************************************************************** * @@ -88,7 +75,7 @@ FT_EXPORT_DEF( FT_Fixed ) FT_RoundFix( FT_Fixed a ) { - return ( ADD_LONG( a, 0x8000L - ( a < 0 ) ) ) & ~0xFFFFL; + return ADD_LONG( a, 0x8000L - ( a < 0 ) ) & ~0xFFFFL; } @@ -97,7 +84,7 @@ FT_EXPORT_DEF( FT_Fixed ) FT_CeilFix( FT_Fixed a ) { - return ( ADD_LONG( a, 0xFFFFL ) ) & ~0xFFFFL; + return ADD_LONG( a, 0xFFFFL ) & ~0xFFFFL; } @@ -225,18 +212,18 @@ FT_MulFix( FT_Long a_, FT_Long b_ ) { -#ifdef FT_MULFIX_ASSEMBLER +#ifdef FT_CONFIG_OPTION_INLINE_MULFIX - return FT_MULFIX_ASSEMBLER( (FT_Int32)a_, (FT_Int32)b_ ); + return FT_MulFix_64( a_, b_ ); #else - FT_Int64 ab = (FT_Int64)a_ * (FT_Int64)b_; + FT_Int64 ab = MUL_INT64( a_, b_ ); /* this requires arithmetic right shift of signed numbers */ - return (FT_Long)( ( ab + 0x8000L - ( ab < 0 ) ) >> 16 ); + return (FT_Long)( ( ab + 0x8000L + ( ab >> 63 ) ) >> 16 ); -#endif /* FT_MULFIX_ASSEMBLER */ +#endif /* FT_CONFIG_OPTION_INLINE_MULFIX */ } @@ -975,43 +962,36 @@ #else - FT_Int result; + FT_Int64 z1, z2; + FT_Int result; - if ( ADD_LONG( FT_ABS( in_x ), FT_ABS( out_y ) ) <= 131071L && - ADD_LONG( FT_ABS( in_y ), FT_ABS( out_x ) ) <= 131071L ) + if ( (FT_ULong)FT_ABS( in_x ) + (FT_ULong)FT_ABS( out_y ) <= 92681UL ) { - FT_Long z1 = MUL_LONG( in_x, out_y ); - FT_Long z2 = MUL_LONG( in_y, out_x ); - - - if ( z1 > z2 ) - result = +1; - else if ( z1 < z2 ) - result = -1; - else - result = 0; + z1.lo = (FT_UInt32)in_x * (FT_UInt32)out_y; + z1.hi = (FT_UInt32)( (FT_Int32)z1.lo >> 31 ); /* sign-expansion */ } - else /* products might overflow 32 bits */ - { - FT_Int64 z1, z2; - - - /* XXX: this function does not allow 64-bit arguments */ + else ft_multo64( (FT_UInt32)in_x, (FT_UInt32)out_y, &z1 ); + + if ( (FT_ULong)FT_ABS( in_y ) + (FT_ULong)FT_ABS( out_x ) <= 92681UL ) + { + z2.lo = (FT_UInt32)in_y * (FT_UInt32)out_x; + z2.hi = (FT_UInt32)( (FT_Int32)z2.lo >> 31 ); /* sign-expansion */ + } + else ft_multo64( (FT_UInt32)in_y, (FT_UInt32)out_x, &z2 ); - if ( z1.hi > z2.hi ) - result = +1; - else if ( z1.hi < z2.hi ) - result = -1; - else if ( z1.lo > z2.lo ) - result = +1; - else if ( z1.lo < z2.lo ) - result = -1; - else - result = 0; - } + if ( (FT_Int32)z1.hi > (FT_Int32)z2.hi ) + result = +1; + else if ( (FT_Int32)z1.hi < (FT_Int32)z2.hi ) + result = -1; + else if ( z1.lo > z2.lo ) + result = +1; + else if ( z1.lo < z2.lo ) + result = -1; + else + result = 0; /* XXX: only the sign of return value, +1/0/-1 must be used */ return result; @@ -1065,62 +1045,4 @@ } - FT_BASE_DEF( FT_Int32 ) - FT_MulAddFix( FT_Fixed* s, - FT_Int32* f, - FT_UInt count ) - { - FT_UInt i; - FT_Int64 temp; - - -#ifdef FT_INT64 - temp = 0; - - for ( i = 0; i < count; ++i ) - temp += (FT_Int64)s[i] * f[i]; - - return (FT_Int32)( ( temp + 0x8000 ) >> 16 ); -#else - temp.hi = 0; - temp.lo = 0; - - for ( i = 0; i < count; ++i ) - { - FT_Int64 multResult; - - FT_Int sign = 1; - FT_UInt32 carry = 0; - - FT_UInt32 scalar; - FT_UInt32 factor; - - - FT_MOVE_SIGN( FT_UInt32, s[i], scalar, sign ); - FT_MOVE_SIGN( FT_UInt32, f[i], factor, sign ); - - ft_multo64( scalar, factor, &multResult ); - - if ( sign < 0 ) - { - /* Emulated `FT_Int64` negation. */ - carry = ( multResult.lo == 0 ); - - multResult.lo = ~multResult.lo + 1; - multResult.hi = ~multResult.hi + carry; - } - - FT_Add64( &temp, &multResult, &temp ); - } - - /* Shift and round value. */ - return (FT_Int32)( ( ( temp.hi << 16 ) | ( temp.lo >> 16 ) ) - + ( 1 & ( temp.lo >> 15 ) ) ); - - -#endif /* !FT_INT64 */ - - } - - /* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftcid.c b/src/java.desktop/share/native/libfreetype/src/base/ftcid.c index 4f2deb19a05..35cd0fcd2be 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftcid.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftcid.c @@ -4,7 +4,7 @@ * * FreeType API for accessing CID font information. * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * Derek Clegg and Michael Toftdal. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c b/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c index c6bf2a3cd1a..90b02b7d2de 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c @@ -4,7 +4,7 @@ * * FreeType's glyph color management (body). * - * Copyright (C) 2018-2024 by + * Copyright (C) 2018-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -56,9 +56,7 @@ FT_Color* *apalette ) { FT_Error error; - - TT_Face ttface; - SFNT_Service sfnt; + TT_Face ttface = (TT_Face)face; if ( !face ) @@ -72,14 +70,17 @@ return FT_Err_Ok; } - ttface = (TT_Face)face; - sfnt = (SFNT_Service)ttface->sfnt; + if ( palette_index != ttface->palette_index ) + { + SFNT_Service sfnt = (SFNT_Service)ttface->sfnt; - error = sfnt->set_palette( ttface, palette_index ); - if ( error ) - return error; - ttface->palette_index = palette_index; + error = sfnt->set_palette( ttface, palette_index ); + if ( error ) + return error; + + ttface->palette_index = palette_index; + } if ( apalette ) *apalette = ttface->palette; diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c b/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c index 902a5dc8bbc..7f54e759b16 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftdbgmem.c @@ -4,7 +4,7 @@ * * Memory debugger (body). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -139,7 +139,6 @@ } FT_MemTableRec; -#define FT_MEM_SIZE_MIN 7 #define FT_MEM_SIZE_MAX 13845163 #define FT_FILENAME( x ) ( (x) ? (x) : "unknown file" ) diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c b/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c index 11307eaace4..c615f29e521 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftdebug.c @@ -4,7 +4,7 @@ * * Debugging and logging component (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -64,7 +64,7 @@ * with the actual log message if set to true. * * 5. The flag `ft_timestamp_flag` prints time along with the actual log - * message if set to ture. + * message if set to true. * * 6. `ft_have_newline_char` is used to differentiate between a log * message with and without a trailing newline character. diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c b/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c index 77b4089e7e2..7f4f14ffdb0 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftfntfmt.c @@ -4,7 +4,7 @@ * * FreeType utility file for font formats (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c b/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c index 1565c3b7e25..3a95752ffaa 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftfstype.c @@ -4,7 +4,7 @@ * * FreeType utility file to access FSType data (body). * - * Copyright (C) 2008-2024 by + * Copyright (C) 2008-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c b/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c index c63d30e978c..2202240b57e 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftgasp.c @@ -4,7 +4,7 @@ * * Access of TrueType's `gasp' table (body). * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c b/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c index 484d98f1722..47781bc4d5c 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftgloadr.c @@ -4,7 +4,7 @@ * * The FreeType glyph loader (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c b/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c index 1b5849f99af..75babb1aa56 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftglyph.c @@ -4,7 +4,7 @@ * * FreeType convenience functions to handle glyphs (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/fthash.c b/src/java.desktop/share/native/libfreetype/src/base/fthash.c index 313bbbb4b27..ab248ace8bd 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/fthash.c +++ b/src/java.desktop/share/native/libfreetype/src/base/fthash.c @@ -41,6 +41,7 @@ #include #include +#include #define INITIAL_HT_SIZE 241 @@ -233,7 +234,8 @@ hash_insert( FT_Hashkey key, size_t data, FT_Hash hash, - FT_Memory memory ) + FT_Memory memory, + FT_Bool overwrite ) { FT_Hashnode nn; FT_Hashnode* bp = hash_bucket( key, hash ); @@ -259,7 +261,7 @@ hash->used++; } - else + else if ( overwrite ) nn->data = data; Exit: @@ -278,7 +280,7 @@ hk.str = key; - return hash_insert( hk, data, hash, memory ); + return hash_insert( hk, data, hash, memory, TRUE ); } @@ -293,7 +295,37 @@ hk.num = num; - return hash_insert( hk, data, hash, memory ); + return hash_insert( hk, data, hash, memory, TRUE ); + } + + + FT_Error + ft_hash_str_insert_no_overwrite( const char* key, + size_t data, + FT_Hash hash, + FT_Memory memory ) + { + FT_Hashkey hk; + + + hk.str = key; + + return hash_insert( hk, data, hash, memory, FALSE ); + } + + + FT_Error + ft_hash_num_insert_no_overwrite( FT_Int num, + size_t data, + FT_Hash hash, + FT_Memory memory ) + { + FT_Hashkey hk; + + + hk.num = num; + + return hash_insert( hk, data, hash, memory, FALSE ); } @@ -335,4 +367,68 @@ } + FT_Bool + ft_hash_num_iterator( FT_UInt *idx, + FT_Int *key, + size_t *value, + FT_Hash hash ) + { + FT_Hashnode nn = NULL; + + + while ( 1 ) + { + if ( *idx >= hash->size ) + return 0; + + nn = hash->table[*idx]; + if ( nn ) + break; + + (*idx)++; + } + + if ( key ) + *key = nn->key.num; + if ( value ) + *value = nn->data; + + (*idx)++; + + return 1; + } + + + FT_Bool + ft_hash_str_iterator( FT_UInt *idx, + const char* *key, + size_t *value, + FT_Hash hash ) + { + FT_Hashnode nn = NULL; + + + while ( 1 ) + { + if ( *idx >= hash->size ) + return 0; + + nn = hash->table[*idx]; + if ( nn ) + break; + + (*idx)++; + } + + if ( key ) + *key = nn->key.str; + if ( value ) + *value = nn->data; + + (*idx)++; + + return 1; + } + + /* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftinit.c b/src/java.desktop/share/native/libfreetype/src/base/ftinit.c index 9a6c00e13ef..37d7f87bcb9 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftinit.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftinit.c @@ -4,7 +4,7 @@ * * FreeType initialization layer (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c b/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c index 1e69d4da70f..d026da8b012 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftlcdfil.c @@ -4,7 +4,7 @@ * * FreeType API for color filtering of subpixel bitmap glyphs (body). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftmac.c b/src/java.desktop/share/native/libfreetype/src/base/ftmac.c index e8e35627b50..37d97be1838 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftmac.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftmac.c @@ -8,7 +8,7 @@ * This file is for Mac OS X only; see builds/mac/ftoldmac.c for * classic platforms built by MPW. * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftmm.c b/src/java.desktop/share/native/libfreetype/src/base/ftmm.c index cc4ca22fba3..9e67001406c 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftmm.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftmm.c @@ -4,7 +4,7 @@ * * Multiple Master font support (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -292,6 +292,9 @@ if ( num_coords && !coords ) return FT_THROW( Invalid_Argument ); + if ( !num_coords && !FT_IS_VARIATION( face ) ) + return FT_Err_Ok; /* nothing to be done */ + error = ft_face_get_mm_service( face, &service_mm ); if ( !error ) { @@ -299,15 +302,21 @@ if ( service_mm->set_var_design ) error = service_mm->set_var_design( face, num_coords, coords ); - if ( !error || error == -1 ) + if ( !error || error == -1 || error == -2 ) { FT_Bool is_variation_old = FT_IS_VARIATION( face ); - if ( num_coords ) - face->face_flags |= FT_FACE_FLAG_VARIATION; - else - face->face_flags &= ~FT_FACE_FLAG_VARIATION; + if ( error != -1 ) + { + if ( error == -2 ) /* -2 means is_variable. */ + { + face->face_flags |= FT_FACE_FLAG_VARIATION; + error = FT_Err_Ok; + } + else + face->face_flags &= ~FT_FACE_FLAG_VARIATION; + } if ( service_mm->construct_ps_name ) { @@ -474,15 +483,21 @@ if ( service_mm->set_mm_blend ) error = service_mm->set_mm_blend( face, num_coords, coords ); - if ( !error || error == -1 ) + if ( !error || error == -1 || error == -2 ) { FT_Bool is_variation_old = FT_IS_VARIATION( face ); - if ( num_coords ) - face->face_flags |= FT_FACE_FLAG_VARIATION; - else - face->face_flags &= ~FT_FACE_FLAG_VARIATION; + if ( error != -1 ) + { + if ( error == -2 ) /* -2 means is_variable. */ + { + face->face_flags |= FT_FACE_FLAG_VARIATION; + error = FT_Err_Ok; + } + else + face->face_flags &= ~FT_FACE_FLAG_VARIATION; + } if ( service_mm->construct_ps_name ) { diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c b/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c index 9b97820c379..cced4fed06b 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftobjs.c @@ -4,7 +4,7 @@ * * The FreeType private base classes (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -905,7 +905,6 @@ FT_Library library; FT_Bool autohint = FALSE; FT_Module hinter; - TT_Face ttface = (TT_Face)face; if ( !face || !face->size || !face->glyph ) @@ -983,6 +982,7 @@ { FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags ); FT_Bool is_light_type1; + TT_Face ttface = (TT_Face)face; /* only the new Adobe engine (for both CFF and Type 1) is `light'; */ @@ -994,8 +994,7 @@ /* the check for `num_locations' assures that we actually */ /* test for instructions in a TTF and not in a CFF-based OTF */ /* */ - /* since `maxSizeOfInstructions' might be unreliable, we */ - /* check the size of the `fpgm' and `prep' tables, too -- */ + /* we check the size of the `fpgm' and `prep' tables, too -- */ /* the assumption is that there don't exist real TTFs where */ /* both `fpgm' and `prep' tables are missing */ if ( ( mode == FT_RENDER_MODE_LIGHT && @@ -1003,9 +1002,8 @@ !is_light_type1 ) ) || ( FT_IS_SFNT( face ) && ttface->num_locations && - ttface->max_profile.maxSizeOfInstructions == 0 && ttface->font_program_size == 0 && - ttface->cvt_program_size == 0 ) ) + ttface->cvt_program_size <= 7 ) ) autohint = TRUE; } } @@ -1172,9 +1170,9 @@ } #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE5(( "FT_Load_Glyph: index %d, flags 0x%x\n", + FT_TRACE5(( "FT_Load_Glyph: index %u, flags 0x%x\n", glyph_index, load_flags )); - FT_TRACE5(( " bitmap %dx%d %s, %s (mode %d)\n", + FT_TRACE5(( " bitmap %ux%u %s, %s (mode %d)\n", slot->bitmap.width, slot->bitmap.rows, slot->outline.points ? @@ -1253,14 +1251,14 @@ FT_Driver driver = (FT_Driver)driver_; - /* finalize client-specific data */ - if ( size->generic.finalizer ) - size->generic.finalizer( size ); - /* finalize format-specific stuff */ if ( driver->clazz->done_size ) driver->clazz->done_size( size ); + /* finalize client-specific data */ + if ( size->generic.finalizer ) + size->generic.finalizer( size ); + FT_FREE( size->internal ); FT_FREE( size ); } @@ -1322,10 +1320,6 @@ driver ); face->size = NULL; - /* now discard client data */ - if ( face->generic.finalizer ) - face->generic.finalizer( face ); - /* discard charmaps */ destroy_charmaps( face, memory ); @@ -1340,6 +1334,10 @@ face->stream = NULL; + /* now discard client data */ + if ( face->generic.finalizer ) + face->generic.finalizer( face ); + /* get rid of it */ if ( face->internal ) { @@ -1359,21 +1357,9 @@ } - /************************************************************************** - * - * @Function: - * find_unicode_charmap - * - * @Description: - * This function finds a Unicode charmap, if there is one. - * And if there is more than one, it tries to favour the more - * extensive one, i.e., one that supports UCS-4 against those which - * are limited to the BMP (said UCS-2 encoding.) - * - * This function is called from open_face() (just below), and also - * from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ). - */ - static FT_Error + /* documentation is in ftobjs.h */ + + FT_BASE_DEF( FT_Error ) find_unicode_charmap( FT_Face face ) { FT_CharMap* first; @@ -2125,7 +2111,7 @@ if ( pfb_pos > pfb_len || pfb_pos + rlen > pfb_len ) goto Exit2; - FT_TRACE3(( " Load POST fragment #%d (%ld byte) to buffer" + FT_TRACE3(( " Load POST fragment #%d (%lu byte) to buffer" " %p + 0x%08lx\n", i, rlen, (void*)pfb_data, pfb_pos )); @@ -2398,7 +2384,7 @@ is_darwin_vfs = ft_raccess_rule_by_darwin_vfs( library, i ); if ( is_darwin_vfs && vfs_rfork_has_no_font ) { - FT_TRACE3(( "Skip rule %d: darwin vfs resource fork" + FT_TRACE3(( "Skip rule %u: darwin vfs resource fork" " is already checked and" " no font is found\n", i )); @@ -2407,7 +2393,7 @@ if ( errors[i] ) { - FT_TRACE3(( "Error 0x%x has occurred in rule %d\n", + FT_TRACE3(( "Error 0x%x has occurred in rule %u\n", errors[i], i )); continue; } @@ -2415,7 +2401,7 @@ args2.flags = FT_OPEN_PATHNAME; args2.pathname = file_names[i] ? file_names[i] : args->pathname; - FT_TRACE3(( "Try rule %d: %s (offset=%ld) ...", + FT_TRACE3(( "Try rule %u: %s (offset=%ld) ...", i, args2.pathname, offsets[i] )); error = FT_Stream_New( library, &args2, &stream2 ); @@ -5044,9 +5030,9 @@ static void Destroy_Module( FT_Module module ) { - FT_Memory memory = module->memory; - FT_Module_Class* clazz = module->clazz; - FT_Library library = module->library; + const FT_Module_Class* clazz = module->clazz; + FT_Library library = module->library; + FT_Memory memory = module->memory; if ( library && library->auto_hinter == module ) @@ -5125,9 +5111,9 @@ goto Exit; /* base initialization */ + module->clazz = clazz; module->library = library; module->memory = memory; - module->clazz = (FT_Module_Class*)clazz; /* check whether the module is a renderer - this must be performed */ /* before the normal module initialization */ diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c b/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c index ef699b3c7cd..8a15b03eb83 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftoutln.c @@ -4,7 +4,7 @@ * * FreeType outline management (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c b/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c index 2055757e023..664bc34deea 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftpatent.c @@ -5,7 +5,7 @@ * FreeType API for checking patented TrueType bytecode instructions * (body). Obsolete, retained for backward compatibility. * - * Copyright (C) 2007-2024 by + * Copyright (C) 2007-2025 by * David Turner. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c b/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c index 37a6cee6cc9..0631cd63f62 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftpsprop.c @@ -5,7 +5,7 @@ * Get and set properties of PostScript drivers (body). * See `ftdriver.h' for available properties. * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c b/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c index dc9b043d8bb..1e241f4f95b 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftrfork.c @@ -4,7 +4,7 @@ * * Embedded resource forks accessor (body). * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * Masatake YAMATO and Redhat K.K. * * FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are @@ -269,14 +269,8 @@ * According to Inside Macintosh: More Macintosh Toolbox, * "Resource IDs" (1-46), there are some reserved IDs. * However, FreeType2 is not a font synthesizer, no need - * to check the acceptable resource ID. + * to check the acceptable resource ID or its attributes. */ - if ( temp < 0 ) - { - error = FT_THROW( Invalid_Table ); - goto Exit; - } - ref[j].offset = temp & 0xFFFFFFL; FT_TRACE3(( " [%d]:" diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c b/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c index f7231fd61cc..34a67a148fc 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftsnames.c @@ -7,7 +7,7 @@ * * This is _not_ used to retrieve glyph names! * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftstream.c b/src/java.desktop/share/native/libfreetype/src/base/ftstream.c index 66722246128..c04a0506def 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftstream.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftstream.c @@ -4,7 +4,7 @@ * * I/O stream support (body). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -242,7 +242,7 @@ FT_ULong read_bytes; - FT_TRACE7(( "FT_Stream_EnterFrame: %ld bytes\n", count )); + FT_TRACE7(( "FT_Stream_EnterFrame: %lu bytes\n", count )); /* check for nested frame access */ FT_ASSERT( stream && stream->cursor == 0 ); diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c b/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c index 64f46ce43e7..591f18eaa83 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftstroke.c @@ -4,7 +4,7 @@ * * FreeType path stroker (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -1070,7 +1070,7 @@ if ( theta == FT_ANGLE_PI2 ) theta = -rotate; - phi = stroker->angle_in + theta + rotate; + phi = stroker->angle_in + theta + rotate; FT_Vector_From_Polar( &sigma, stroker->miter_limit, theta ); @@ -1371,7 +1371,7 @@ arc[1] = *control; arc[2] = stroker->center; - while ( arc >= bez_stack ) + do { FT_Angle angle_in, angle_out; @@ -1524,10 +1524,12 @@ } } - arc -= 2; - stroker->angle_in = angle_out; - } + + if ( arc == bez_stack ) + break; + arc -= 2; + } while ( 1 ); stroker->center = *to; stroker->line_length = 0; @@ -1577,7 +1579,7 @@ arc[2] = *control1; arc[3] = stroker->center; - while ( arc >= bez_stack ) + do { FT_Angle angle_in, angle_mid, angle_out; @@ -1741,10 +1743,12 @@ } } - arc -= 3; - stroker->angle_in = angle_out; - } + + if ( arc == bez_stack ) + break; + arc -= 3; + } while ( 1 ); stroker->center = *to; stroker->line_length = 0; diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c b/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c index ec05bce33a9..08bc1742202 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftsynth.c @@ -4,7 +4,7 @@ * * FreeType synthesizing code for emboldening and slanting (body). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -141,7 +141,7 @@ /* * XXX: overflow check for 16-bit system, for compatibility * with FT_GlyphSlot_Embolden() since FreeType 2.1.10. - * unfortunately, this function return no informations + * unfortunately, this function returns no information * about the cause of error. */ if ( ( ystr >> 6 ) > FT_INT_MAX || ( ystr >> 6 ) < FT_INT_MIN ) diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c b/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c index eee3642334f..186119d5581 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftsystem.c @@ -4,7 +4,7 @@ * * ANSI-specific FreeType low-level system interface (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -280,7 +280,7 @@ stream->close = ft_ansi_stream_close; FT_TRACE1(( "FT_Stream_Open:" )); - FT_TRACE1(( " opened `%s' (%ld bytes) successfully\n", + FT_TRACE1(( " opened `%s' (%lu bytes) successfully\n", filepathname, stream->size )); return FT_Err_Ok; diff --git a/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c b/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c index 4b1aced1cba..29eff639c51 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c +++ b/src/java.desktop/share/native/libfreetype/src/base/fttrigon.c @@ -4,7 +4,7 @@ * * FreeType trigonometric functions (body). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/fttype1.c b/src/java.desktop/share/native/libfreetype/src/base/fttype1.c index cedf7c40505..77978df674d 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/fttype1.c +++ b/src/java.desktop/share/native/libfreetype/src/base/fttype1.c @@ -4,7 +4,7 @@ * * FreeType utility file for PS names support (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftutil.c b/src/java.desktop/share/native/libfreetype/src/base/ftutil.c index b13512f8704..0b7382627c5 100644 --- a/src/java.desktop/share/native/libfreetype/src/base/ftutil.c +++ b/src/java.desktop/share/native/libfreetype/src/base/ftutil.c @@ -4,7 +4,7 @@ * * FreeType utility file for memory and list management (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c index ea5f8ed2885..cb69abdb90f 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.c @@ -4,7 +4,7 @@ * * CFF character mapping table (cmap) support (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h index 1dd8700cd8b..60e16d94875 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffcmap.h @@ -4,7 +4,7 @@ * * CFF character mapping table (cmap) support (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c index f6ebdb3810a..0079ddd1950 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.c @@ -4,7 +4,7 @@ * * OpenType font driver implementation (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, Werner Lemberg, and Dominik Röttsches. * * This file is part of the FreeType project, and may only be used, @@ -121,7 +121,20 @@ kerning->y = 0; if ( sfnt ) - kerning->x = sfnt->get_kerning( cffface, left_glyph, right_glyph ); + { + /* Use 'kern' table if available since that can be faster; otherwise */ + /* use GPOS kerning pairs if available. */ + if ( cffface->kern_avail_bits ) + kerning->x = sfnt->get_kerning( cffface, + left_glyph, + right_glyph ); +#ifdef TT_CONFIG_OPTION_GPOS_KERNING + else if ( cffface->num_gpos_lookups_kerning ) + kerning->x = sfnt->get_gpos_kerning( cffface, + left_glyph, + right_glyph ); +#endif + } return FT_Err_Ok; } @@ -168,25 +181,7 @@ CFF_Size cffsize = (CFF_Size)size; - if ( !cffslot ) - return FT_THROW( Invalid_Slot_Handle ); - - FT_TRACE1(( "cff_glyph_load: glyph index %d\n", glyph_index )); - - /* check whether we want a scaled outline or bitmap */ - if ( !cffsize ) - load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; - - /* reset the size object if necessary */ - if ( load_flags & FT_LOAD_NO_SCALE ) - size = NULL; - - if ( size ) - { - /* these two objects must have the same parent */ - if ( size->face != slot->face ) - return FT_THROW( Invalid_Face_Handle ); - } + FT_TRACE1(( "cff_glyph_load: glyph index %u\n", glyph_index )); /* now load the glyph outline if necessary */ error = cff_slot_load( cffslot, cffsize, glyph_index, load_flags ); @@ -205,105 +200,70 @@ FT_Int32 flags, FT_Fixed* advances ) { - FT_UInt nn; - FT_Error error = FT_Err_Ok; - FT_GlyphSlot slot = face->glyph; + CFF_Face cffface = (CFF_Face)face; + FT_Bool horz; + FT_UInt nn; - if ( FT_IS_SFNT( face ) ) + if ( !FT_IS_SFNT( face ) ) + return FT_THROW( Unimplemented_Feature ); + + horz = !( flags & FT_LOAD_VERTICAL_LAYOUT ); + + if ( horz ) { /* OpenType 1.7 mandates that the data from `hmtx' table be used; */ /* it is no longer necessary that those values are identical to */ /* the values in the `CFF' table */ + if ( !cffface->horizontal.number_Of_HMetrics ) + return FT_THROW( Unimplemented_Feature ); - CFF_Face cffface = (CFF_Face)face; - FT_Short dummy; - - - if ( flags & FT_LOAD_VERTICAL_LAYOUT ) - { #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - /* no fast retrieval for blended MM fonts without VVAR table */ - if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) && - !( cffface->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) - return FT_THROW( Unimplemented_Feature ); + /* no fast retrieval for blended MM fonts without HVAR table */ + if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) && + !( cffface->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ) + return FT_THROW( Unimplemented_Feature ); #endif + } + else /* vertical */ + { + /* check whether we have data from the `vmtx' table at all; */ + /* otherwise we extract the info from the CFF glyphstrings */ + /* (instead of synthesizing a global value using the `OS/2' */ + /* table) */ + if ( !cffface->vertical_info ) + return FT_THROW( Unimplemented_Feature ); - /* check whether we have data from the `vmtx' table at all; */ - /* otherwise we extract the info from the CFF glyphstrings */ - /* (instead of synthesizing a global value using the `OS/2' */ - /* table) */ - if ( !cffface->vertical_info ) - goto Missing_Table; - - for ( nn = 0; nn < count; nn++ ) - { - FT_UShort ah; - - - ( (SFNT_Service)cffface->sfnt )->get_metrics( cffface, - 1, - start + nn, - &dummy, - &ah ); - - FT_TRACE5(( " idx %d: advance height %d font unit%s\n", - start + nn, - ah, - ah == 1 ? "" : "s" )); - advances[nn] = ah; - } - } - else - { #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - /* no fast retrieval for blended MM fonts without HVAR table */ - if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) && - !( cffface->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ) - return FT_THROW( Unimplemented_Feature ); + /* no fast retrieval for blended MM fonts without VVAR table */ + if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) && + !( cffface->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) + return FT_THROW( Unimplemented_Feature ); #endif - - /* check whether we have data from the `hmtx' table at all */ - if ( !cffface->horizontal.number_Of_HMetrics ) - goto Missing_Table; - - for ( nn = 0; nn < count; nn++ ) - { - FT_UShort aw; - - - ( (SFNT_Service)cffface->sfnt )->get_metrics( cffface, - 0, - start + nn, - &dummy, - &aw ); - - FT_TRACE5(( " idx %d: advance width %d font unit%s\n", - start + nn, - aw, - aw == 1 ? "" : "s" )); - advances[nn] = aw; - } - } - - return error; } - Missing_Table: - flags |= (FT_UInt32)FT_LOAD_ADVANCE_ONLY; - + /* proceed to fast advances */ for ( nn = 0; nn < count; nn++ ) { - error = cff_glyph_load( slot, face->size, start + nn, flags ); - if ( error ) - break; + FT_UShort aw; + FT_Short dummy; - advances[nn] = ( flags & FT_LOAD_VERTICAL_LAYOUT ) - ? slot->linearVertAdvance - : slot->linearHoriAdvance; + + ( (SFNT_Service)cffface->sfnt )->get_metrics( cffface, + !horz, + start + nn, + &dummy, + &aw ); + + FT_TRACE5(( " idx %u: advance %s %d font unit%s\n", + start + nn, + horz ? "width" : "height", + aw, + aw == 1 ? "" : "s" )); + advances[nn] = aw; } - return error; + return FT_Err_Ok; } diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h index fd5bc37ecd4..52a1e727a6a 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffdrivr.h @@ -4,7 +4,7 @@ * * High-level OpenType driver interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h b/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h index 128adc3b716..7491886c7be 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cfferrs.h @@ -4,7 +4,7 @@ * * CFF error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c index cbb071abdfe..e8bab3c1e33 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.c @@ -4,7 +4,7 @@ * * OpenType Glyph Loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -238,24 +238,12 @@ else if ( glyph_index >= cff->num_glyphs ) return FT_THROW( Invalid_Argument ); - if ( load_flags & FT_LOAD_NO_RECURSE ) - load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; - - glyph->x_scale = 0x10000L; - glyph->y_scale = 0x10000L; - if ( size ) - { - glyph->x_scale = size->root.metrics.x_scale; - glyph->y_scale = size->root.metrics.y_scale; - } - #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS /* try to load embedded bitmap if any */ /* */ /* XXX: The convention should be emphasized in */ /* the documents because it can be confusing. */ - if ( size ) { CFF_Face cff_face = (CFF_Face)size->root.face; SFNT_Service sfnt = (SFNT_Service)cff_face->sfnt; @@ -284,9 +272,6 @@ FT_Short dummy; - glyph->root.outline.n_points = 0; - glyph->root.outline.n_contours = 0; - glyph->root.metrics.width = (FT_Pos)metrics.width * 64; glyph->root.metrics.height = (FT_Pos)metrics.height * 64; @@ -423,6 +408,25 @@ #endif /* FT_CONFIG_OPTION_SVG */ + /* top-level code ensures that FT_LOAD_NO_HINTING is set */ + /* if FT_LOAD_NO_SCALE is active */ + hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); + scaled = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); + + glyph->hint = hinting; + glyph->scaled = scaled; + + if ( scaled ) + { + glyph->x_scale = size->root.metrics.x_scale; + glyph->y_scale = size->root.metrics.y_scale; + } + else + { + glyph->x_scale = 0x10000L; + glyph->y_scale = 0x10000L; + } + /* if we have a CID subfont, use its matrix (which has already */ /* been multiplied with the root matrix) */ @@ -457,18 +461,6 @@ font_offset = cff->top_font.font_dict.font_offset; } - glyph->root.outline.n_points = 0; - glyph->root.outline.n_contours = 0; - - /* top-level code ensures that FT_LOAD_NO_HINTING is set */ - /* if FT_LOAD_NO_SCALE is active */ - hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); - scaled = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); - - glyph->hint = hinting; - glyph->scaled = scaled; - glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; /* by default */ - { #ifdef CFF_CONFIG_OPTION_OLD_ENGINE PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( face ); @@ -602,10 +594,8 @@ { /* Now, set the metrics -- this is rather simple, as */ /* the left side bearing is the xMin, and the top side */ - /* bearing the yMax. */ - - /* For composite glyphs, return only left side bearing and */ - /* advance width. */ + /* bearing the yMax. For composite glyphs, return only */ + /* left side bearing and advance width. */ if ( load_flags & FT_LOAD_NO_RECURSE ) { FT_Slot_Internal internal = glyph->root.internal; @@ -624,6 +614,12 @@ FT_Bool has_vertical_info; + glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; + + glyph->root.outline.flags = FT_OUTLINE_REVERSE_FILL; + if ( size && size->root.metrics.y_ppem < 24 ) + glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION; + if ( face->horizontal.number_Of_HMetrics ) { FT_Short horiBearingX = 0; @@ -677,14 +673,6 @@ glyph->root.linearVertAdvance = metrics->vertAdvance; - glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; - - glyph->root.outline.flags = 0; - if ( size && size->root.metrics.y_ppem < 24 ) - glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION; - - glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL; - /* apply the font matrix, if any */ if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L || font_matrix.xy != 0 || font_matrix.yx != 0 ) @@ -707,7 +695,7 @@ metrics->vertAdvance += font_offset.y; } - if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 || force_scaling ) + if ( scaled || force_scaling ) { /* scale the outline and the metrics */ FT_Int n; diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h index 346d4b11c31..662bb7cff53 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffgload.h @@ -4,7 +4,7 @@ * * OpenType Glyph Loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffload.c b/src/java.desktop/share/native/libfreetype/src/cff/cffload.c index 979fd45f6ca..326f885056f 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffload.c +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffload.c @@ -4,7 +4,7 @@ * * OpenType and CFF data/program tables loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -442,7 +442,7 @@ if ( cur_offset != 0 ) { FT_TRACE0(( "cff_index_get_pointers:" - " invalid first offset value %ld set to zero\n", + " invalid first offset value %lu set to zero\n", cur_offset )); cur_offset = 0; } @@ -559,8 +559,8 @@ idx->data_offset > stream->size - off2 + 1 ) { FT_ERROR(( "cff_index_access_element:" - " offset to next entry (%ld)" - " exceeds the end of stream (%ld)\n", + " offset to next entry (%lu)" + " exceeds the end of stream (%lu)\n", off2, stream->size - idx->data_offset + 1 )); off2 = stream->size - idx->data_offset + 1; } @@ -982,7 +982,7 @@ if ( glyph_sid > 0xFFFFL - nleft ) { FT_ERROR(( "cff_charset_load: invalid SID range trimmed" - " nleft=%d -> %ld\n", nleft, 0xFFFFL - glyph_sid )); + " nleft=%u -> %ld\n", nleft, 0xFFFFL - glyph_sid )); nleft = ( FT_UInt )( 0xFFFFL - glyph_sid ); } @@ -1315,7 +1315,7 @@ if ( numOperands > count ) { - FT_TRACE4(( " cff_blend_doBlend: Stack underflow %d argument%s\n", + FT_TRACE4(( " cff_blend_doBlend: Stack underflow %u argument%s\n", count, count == 1 ? "" : "s" )); @@ -1466,7 +1466,7 @@ if ( master == 0 ) { blend->BV[master] = FT_FIXED_ONE; - FT_TRACE4(( " build blend vector len %d\n", len )); + FT_TRACE4(( " build blend vector len %u\n", len )); FT_TRACE4(( " [ %f ", blend->BV[master] / 65536.0 )); continue; } @@ -2341,7 +2341,7 @@ if ( face_index > 0 && subfont_index >= font->name_index.count ) { FT_ERROR(( "cff_font_load:" - " invalid subfont index for pure CFF font (%d)\n", + " invalid subfont index for pure CFF font (%u)\n", subfont_index )); error = FT_THROW( Invalid_Argument ); goto Exit; diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffload.h b/src/java.desktop/share/native/libfreetype/src/cff/cffload.h index 02209245421..fdc132c8f3f 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffload.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffload.h @@ -4,7 +4,7 @@ * * OpenType & CFF data/program tables loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c index 7c6713739a1..1aeb908f4a1 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.c @@ -4,7 +4,7 @@ * * OpenType objects manager (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -537,8 +537,8 @@ sfnt_format = 1; - /* now, the font can be either an OpenType/CFF font, or an SVG CEF */ - /* font; in the latter case it doesn't have a `head' table */ + /* the font may be OpenType/CFF, SVG CEF, or sfnt/CFF; a `head' table */ + /* implies OpenType/CFF, otherwise just look for an optional cmap */ error = face->goto_table( face, TTAG_head, stream, 0 ); if ( !error ) { @@ -554,7 +554,9 @@ { /* load the `cmap' table explicitly */ error = sfnt->load_cmap( face, stream ); - if ( error ) + + /* this may fail because CID-keyed fonts don't have a cmap */ + if ( FT_ERR_NEQ( error, Table_Missing ) && FT_ERR_NEQ( error, Ok ) ) goto Exit; } @@ -651,7 +653,7 @@ { s = cff_index_get_sid_string( cff, idx ); if ( s ) - FT_TRACE4(( " %5d %s\n", idx, s )); + FT_TRACE4(( " %5u %s\n", idx, s )); } /* In Multiple Master CFFs, two SIDs hold the Normalize Design */ @@ -666,7 +668,7 @@ FT_PtrDist l; - FT_TRACE4(( " %5d ", idx + 390 )); + FT_TRACE4(( " %5u ", idx + 390 )); for ( l = 0; l < s1len; l++ ) FT_TRACE4(( "%c", s1[l] )); FT_TRACE4(( "\n" )); @@ -681,7 +683,7 @@ FT_PtrDist l; - FT_TRACE4(( " %5d ", cff->num_strings + 390 )); + FT_TRACE4(( " %5u ", cff->num_strings + 390 )); for ( l = 0; l < s1len; l++ ) FT_TRACE4(( "%c", s1[l] )); FT_TRACE4(( "\n" )); diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h index 91ad83b1cd0..982dcd64dd0 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffobjs.h @@ -4,7 +4,7 @@ * * OpenType objects manager (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c index 92a69c3b516..864b2490b3b 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.c @@ -4,7 +4,7 @@ * * CFF token stream parser (body) * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -892,7 +892,7 @@ dict->cid_supplement )); error = FT_Err_Ok; - FT_TRACE4(( " %d %d %ld\n", + FT_TRACE4(( " %u %u %ld\n", dict->cid_registry, dict->cid_ordering, dict->cid_supplement )); @@ -929,7 +929,7 @@ priv->vsindex = (FT_UInt)cff_parse_num( parser, data++ ); - FT_TRACE4(( " %d\n", priv->vsindex )); + FT_TRACE4(( " %u\n", priv->vsindex )); error = FT_Err_Ok; @@ -979,7 +979,7 @@ goto Exit; } - FT_TRACE4(( " %d value%s blended\n", + FT_TRACE4(( " %u value%s blended\n", numBlends, numBlends == 1 ? "" : "s" )); @@ -1014,7 +1014,7 @@ if ( dict->maxstack < CFF2_DEFAULT_STACK ) dict->maxstack = CFF2_DEFAULT_STACK; - FT_TRACE4(( " %d\n", dict->maxstack )); + FT_TRACE4(( " %u\n", dict->maxstack )); Exit: return error; diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h index ca6b18af6aa..47cceb1a4a0 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cffparse.h @@ -4,7 +4,7 @@ * * CFF token stream parser (specification) * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h b/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h index da45faa7f4e..cc5aaa2867a 100644 --- a/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h +++ b/src/java.desktop/share/native/libfreetype/src/cff/cfftoken.h @@ -4,7 +4,7 @@ * * CFF token definitions (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h b/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h index c439a8c4a0b..1591979d370 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h +++ b/src/java.desktop/share/native/libfreetype/src/cid/ciderrs.h @@ -4,7 +4,7 @@ * * CID error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c index 7b571322d45..249ede5757d 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.c @@ -4,7 +4,7 @@ * * CID-keyed Type1 Glyph Loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -103,20 +103,20 @@ if ( ( cid->fd_bytes == 1 && fd_select == 0xFFU ) || ( cid->fd_bytes == 2 && fd_select == 0xFFFFU ) ) { - FT_TRACE1(( "cid_load_glyph: fail for glyph index %d:\n", + FT_TRACE1(( "cid_load_glyph: fail for glyph index %u:\n", glyph_index )); - FT_TRACE1(( " FD number %ld is the maximum\n", + FT_TRACE1(( " FD number %lu is the maximum\n", fd_select )); - FT_TRACE1(( " integer fitting into %d byte%s\n", + FT_TRACE1(( " integer fitting into %u byte%s\n", cid->fd_bytes, cid->fd_bytes == 1 ? "" : "s" )); } else { - FT_TRACE0(( "cid_load_glyph: fail for glyph index %d:\n", + FT_TRACE0(( "cid_load_glyph: fail for glyph index %u:\n", glyph_index )); - FT_TRACE0(( " FD number %ld is larger\n", + FT_TRACE0(( " FD number %lu is larger\n", fd_select )); - FT_TRACE0(( " than number of dictionaries (%d)\n", + FT_TRACE0(( " than number of dictionaries (%u)\n", cid->num_dicts )); } @@ -125,7 +125,7 @@ } else if ( off2 > stream->size ) { - FT_TRACE0(( "cid_load_glyph: fail for glyph index %d:\n", + FT_TRACE0(( "cid_load_glyph: fail for glyph index %u:\n", glyph_index )); FT_TRACE0(( " end of the glyph data\n" )); FT_TRACE0(( " is beyond the data stream\n" )); @@ -135,7 +135,7 @@ } else if ( off1 > off2 ) { - FT_TRACE0(( "cid_load_glyph: fail for glyph index %d:\n", + FT_TRACE0(( "cid_load_glyph: fail for glyph index %u:\n", glyph_index )); FT_TRACE0(( " the end position of glyph data\n" )); FT_TRACE0(( " is set before the start position\n" )); @@ -252,8 +252,8 @@ cs_offset = decoder->lenIV >= 0 ? (FT_UInt)decoder->lenIV : 0; if ( cs_offset > glyph_length ) { - FT_TRACE0(( "cid_load_glyph: fail for glyph_index=%d, " - "offset to the charstring is beyond glyph length\n", + FT_TRACE0(( "cid_load_glyph: fail for glyph_index=%u," + " offset to the charstring is beyond glyph length\n", glyph_index )); error = FT_THROW( Invalid_Offset ); goto Exit; @@ -452,16 +452,12 @@ glyph->x_scale = cidsize->metrics.x_scale; glyph->y_scale = cidsize->metrics.y_scale; - cidglyph->outline.n_points = 0; - cidglyph->outline.n_contours = 0; - hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 && ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); scaled = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); glyph->hint = hinting; glyph->scaled = scaled; - cidglyph->format = FT_GLYPH_FORMAT_OUTLINE; error = psaux->t1_decoder_funcs->init( &decoder, cidglyph->face, @@ -501,12 +497,8 @@ /* now set the metrics -- this is rather simple, as */ /* the left side bearing is the xMin, and the top side */ - /* bearing the yMax */ - cidglyph->outline.flags &= FT_OUTLINE_OWNER; - cidglyph->outline.flags |= FT_OUTLINE_REVERSE_FILL; - - /* for composite glyphs, return only left side bearing and */ - /* advance width */ + /* bearing the yMax; for composite glyphs, return only */ + /* left side bearing and advance width */ if ( load_flags & FT_LOAD_NO_RECURSE ) { FT_Slot_Internal internal = cidglyph->internal; @@ -527,6 +519,13 @@ FT_Glyph_Metrics* metrics = &cidglyph->metrics; + cidglyph->format = FT_GLYPH_FORMAT_OUTLINE; + + cidglyph->outline.flags &= FT_OUTLINE_OWNER; + cidglyph->outline.flags |= FT_OUTLINE_REVERSE_FILL; + if ( cidsize->metrics.y_ppem < 24 ) + cidglyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; + /* copy the _unscaled_ advance width */ metrics->horiAdvance = FIXED_TO_INT( decoder.builder.advance.x ); @@ -539,11 +538,6 @@ face->cid.font_bbox.yMin ) >> 16; cidglyph->linearVertAdvance = metrics->vertAdvance; - cidglyph->format = FT_GLYPH_FORMAT_OUTLINE; - - if ( cidsize->metrics.y_ppem < 24 ) - cidglyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; - /* apply the font matrix, if any */ if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L || font_matrix.xy != 0 || font_matrix.yx != 0 ) diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h index 9fdc9db5892..cef96073ded 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidgload.h @@ -4,7 +4,7 @@ * * OpenType Glyph Loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidload.c b/src/java.desktop/share/native/libfreetype/src/cid/cidload.c index 722f5a34ddf..bb1bf13e221 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidload.c +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidload.c @@ -4,7 +4,7 @@ * * CID-keyed Type1 font loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidload.h b/src/java.desktop/share/native/libfreetype/src/cid/cidload.h index 7f030b32df7..659dd0e378c 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidload.h +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidload.h @@ -4,7 +4,7 @@ * * CID-keyed Type1 font loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c index 8d337c41128..634bbf2f135 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.c @@ -4,7 +4,7 @@ * * CID objects manager (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h index d371cbe9954..800268efa2f 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidobjs.h @@ -4,7 +4,7 @@ * * CID objects manager (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c index 73a3ade893b..4d1ba335960 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.c @@ -4,7 +4,7 @@ * * CID-keyed Type1 parser (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h index 0f5baddcb92..6ae2e542394 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidparse.h @@ -4,7 +4,7 @@ * * CID-keyed Type1 parser (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c index 4be8a5c00d5..a3a587c57bf 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.c @@ -4,7 +4,7 @@ * * CID driver interface (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h index 7ddce431c5b..55d0b8a0d9b 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidriver.h @@ -4,7 +4,7 @@ * * High-level CID driver interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h b/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h index 160897d1447..25ba74fed94 100644 --- a/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h +++ b/src/java.desktop/share/native/libfreetype/src/cid/cidtoken.h @@ -4,7 +4,7 @@ * * CID token definitions (specification only). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c index e2f6a8e5adb..b813efde4eb 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.c @@ -4,7 +4,7 @@ * * AFM parser (body). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h index b7766372821..add8597717d 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/afmparse.h @@ -4,7 +4,7 @@ * * AFM parser (specification). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c index 9556e11a586..17bdd23c7d4 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.c @@ -4,7 +4,7 @@ * * PostScript CFF (Type 2) decoding routines (body). * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -2141,7 +2141,7 @@ decoder->locals_bias ); - FT_TRACE4(( " callsubr (idx %d, entering level %td)\n", + FT_TRACE4(( " callsubr (idx %u, entering level %td)\n", idx, zone - decoder->zones + 1 )); @@ -2185,7 +2185,7 @@ decoder->globals_bias ); - FT_TRACE4(( " callgsubr (idx %d, entering level %td)\n", + FT_TRACE4(( " callgsubr (idx %u, entering level %td)\n", idx, zone - decoder->zones + 1 )); diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h index 038f7235c3d..e72ec043baa 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/cffdecode.h @@ -4,7 +4,7 @@ * * PostScript CFF (Type 2) decoding routines (specification). * - * Copyright (C) 2017-2024 by + * Copyright (C) 2017-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h b/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h index 18428c40d5a..0d7fe2b6121 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psauxerr.h @@ -4,7 +4,7 @@ * * PS auxiliary module error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c index 6826f9d8d3e..942804190c5 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.c @@ -4,7 +4,7 @@ * * FreeType auxiliary PostScript module implementation (body). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h index 82d7e348af8..4a5ebc1b607 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psauxmod.h @@ -4,7 +4,7 @@ * * FreeType auxiliary PostScript module implementation (specification). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -46,9 +46,6 @@ FT_BEGIN_HEADER const CFF_Decoder_FuncsRec cff_decoder_funcs; - FT_EXPORT_VAR( const FT_Module_Class ) psaux_driver_class; - - FT_DECLARE_MODULE( psaux_module_class ) diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c index 56c0ecd1d7f..4567d3f3c06 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.c @@ -4,7 +4,7 @@ * * Some convenience conversions (body). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h index 91fcd15a1c9..63735af411f 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psconv.h @@ -4,7 +4,7 @@ * * Some convenience conversions (specification). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c b/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c index 7572e225e37..7e3475e6f58 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psintrp.c @@ -618,7 +618,7 @@ /* Our copy of it does not change that requirement. */ cf2_arrstack_setCount( &subrStack, CF2_MAX_SUBR + 1 ); - charstring = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack ); + charstring = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack ); /* catch errors so far */ if ( *error ) diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c index eca465f009e..8159fd6ef15 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.c @@ -4,7 +4,7 @@ * * Auxiliary functions for PostScript fonts (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -460,6 +460,9 @@ case '%': skip_comment( &cur, limit ); break; + + default: + break; } } @@ -1145,7 +1148,7 @@ FT_ERROR(( "ps_parser_load_field:" " expected a name or string\n" )); FT_ERROR(( " " - " but found token of type %d instead\n", + " but found token of type %u instead\n", token.type )); error = FT_THROW( Invalid_File_Format ); goto Exit; @@ -1225,7 +1228,7 @@ if ( result < 0 || (FT_UInt)result < max_objects ) { FT_ERROR(( "ps_parser_load_field:" - " expected %d integer%s in the %s subarray\n", + " expected %u integer%s in the %s subarray\n", max_objects, max_objects > 1 ? "s" : "", i == 0 ? "first" : ( i == 1 ? "second" diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h index 345fc8a7335..277aa1247c5 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/psobjs.h @@ -4,7 +4,7 @@ * * Auxiliary functions for PostScript fonts (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c index 5681c3bd0fd..66493b68123 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.c @@ -4,7 +4,7 @@ * * Type 1 character map support (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h index 445e6a2784f..114bfbb0410 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1cmap.h @@ -4,7 +4,7 @@ * * Type 1 character map support (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c index c74baa8038f..c3fb343d4c9 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c +++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.c @@ -4,7 +4,7 @@ * * PostScript Type 1 decoding routines (body). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -1633,7 +1633,7 @@ default: FT_ERROR(( "t1_decoder_parse_charstrings:" - " unhandled opcode %d\n", op )); + " unhandled opcode %u\n", op )); goto Syntax_Error; } diff --git a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h index 16203b8f734..7b913f55dff 100644 --- a/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h +++ b/src/java.desktop/share/native/libfreetype/src/psaux/t1decode.h @@ -4,7 +4,7 @@ * * PostScript Type 1 decoding routines (specification). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c index 967767b3485..ca30702321d 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.c @@ -4,7 +4,7 @@ * * PostScript hinting algorithm (body). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used @@ -100,7 +100,7 @@ if ( idx >= table->max_hints ) { - FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx )); + FT_TRACE0(( "psh_hint_table_record: invalid hint index %u\n", idx )); return; } diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h index fb362f061b6..f4aa8540559 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshalgo.h @@ -4,7 +4,7 @@ * * PostScript hinting algorithm (specification). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c index 435f45838ff..521a5ff6c5c 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.c @@ -5,7 +5,7 @@ * PostScript hinter global hinting management (body). * Inspired by the new auto-hinter module. * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h index c5a5c913168..555e99facb2 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshglob.h @@ -4,7 +4,7 @@ * * PostScript hinter global hinting management. * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c index 9965d5b16bf..c9f4a94fe98 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.c @@ -4,7 +4,7 @@ * * FreeType PostScript hinter module implementation (body). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h index 62ac0a60fdc..de9c398e9fb 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshmod.h @@ -4,7 +4,7 @@ * * PostScript hinter module interface (specification). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h index e9641340e53..7076664ddde 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshnterr.h @@ -4,7 +4,7 @@ * * PS Hinter error codes (specification only). * - * Copyright (C) 2003-2024 by + * Copyright (C) 2003-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c index 0b2b549fc29..13754313fbb 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.c @@ -4,7 +4,7 @@ * * FreeType PostScript hints recorder (body). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -467,7 +467,7 @@ table->num_masks--; } else - FT_TRACE0(( "ps_mask_table_merge: ignoring invalid indices (%d,%d)\n", + FT_TRACE0(( "ps_mask_table_merge: ignoring invalid indices (%u,%u)\n", index1, index2 )); Exit: @@ -817,7 +817,7 @@ /* limit "dimension" to 0..1 */ if ( dimension > 1 ) { - FT_TRACE0(( "ps_hints_stem: invalid dimension (%d) used\n", + FT_TRACE0(( "ps_hints_stem: invalid dimension (%u) used\n", dimension )); dimension = ( dimension != 0 ); } @@ -870,7 +870,7 @@ /* limit "dimension" to 0..1 */ if ( dimension > 1 ) { - FT_TRACE0(( "ps_hints_t1stem3: invalid dimension (%d) used\n", + FT_TRACE0(( "ps_hints_t1stem3: invalid dimension (%u) used\n", dimension )); dimension = ( dimension != 0 ); } @@ -976,7 +976,7 @@ if ( bit_count != count1 + count2 ) { FT_TRACE0(( "ps_hints_t2mask:" - " called with invalid bitcount %d (instead of %d)\n", + " called with invalid bitcount %u (instead of %u)\n", bit_count, count1 + count2 )); /* simply ignore the operator */ @@ -1022,7 +1022,7 @@ if ( bit_count != count1 + count2 ) { FT_TRACE0(( "ps_hints_t2counter:" - " called with invalid bitcount %d (instead of %d)\n", + " called with invalid bitcount %u (instead of %u)\n", bit_count, count1 + count2 )); /* simply ignore the operator */ diff --git a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h index 7e375af7ba8..a79069f98d2 100644 --- a/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h +++ b/src/java.desktop/share/native/libfreetype/src/pshinter/pshrec.h @@ -4,7 +4,7 @@ * * Postscript (Type1/Type2) hints recorder (specification). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c index 35d054d1cfb..c5d71edad88 100644 --- a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c +++ b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.c @@ -4,7 +4,7 @@ * * psnames module implementation (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h index 770458316b1..482fd0a36d1 100644 --- a/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h +++ b/src/java.desktop/share/native/libfreetype/src/psnames/psmodule.h @@ -4,7 +4,7 @@ * * High-level psnames module interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h b/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h index e123eb65e39..17987f9cd4f 100644 --- a/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h +++ b/src/java.desktop/share/native/libfreetype/src/psnames/psnamerr.h @@ -4,7 +4,7 @@ * * PS names module error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h b/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h index 2a941b04609..65ce6c0b47f 100644 --- a/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h +++ b/src/java.desktop/share/native/libfreetype/src/psnames/pstables.h @@ -4,7 +4,7 @@ * * PostScript glyph names. * - * Copyright (C) 2005-2024 by + * Copyright (C) 2005-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h b/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h index 943f2aa0a50..9d97223e94e 100644 --- a/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h +++ b/src/java.desktop/share/native/libfreetype/src/raster/ftmisc.h @@ -5,7 +5,7 @@ * Miscellaneous macros for stand-alone rasterizer (specification * only). * - * Copyright (C) 2005-2024 by + * Copyright (C) 2005-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c index e4b7b937d5a..807d444e7aa 100644 --- a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c +++ b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.c @@ -4,7 +4,7 @@ * * The FreeType glyph rasterizer (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -251,7 +251,11 @@ /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */ /* for clipping computations. It simply uses the FT_MulDiv() function */ /* defined in `ftcalc.h'. */ -#define SMulDiv_No_Round FT_MulDiv_No_Round +#ifdef FT_INT64 +#define SMulDiv( a, b, c ) (Long)( (FT_Int64)(a) * (b) / (c) ) +#else +#define SMulDiv FT_MulDiv_No_Round +#endif /* The rasterizer is a very general purpose component; please leave */ /* the following redefinitions there (you never know your target */ @@ -653,7 +657,7 @@ ras.cProfile->height = 0; } - ras.cProfile->flags = ras.dropOutControl; + ras.cProfile->flags = ras.dropOutControl; switch ( aState ) { @@ -967,14 +971,14 @@ goto Fin; } - Ix = SMulDiv_No_Round( e - y1, Dx, Dy ); + Ix = SMulDiv( e - y1, Dx, Dy ); x1 += Ix; *top++ = x1; if ( --size ) { Ax = Dx * ( e - y1 ) - Dy * Ix; /* remainder */ - Ix = FMulDiv( ras.precision, Dx, Dy ); + Ix = SMulDiv( ras.precision, Dx, Dy ); Rx = Dx * ras.precision - Dy * Ix; /* remainder */ Dx = 1; @@ -1090,8 +1094,8 @@ PLong top; - y1 = arc[degree].y; - y2 = arc[0].y; + y1 = arc[degree].y; + y2 = arc[0].y; if ( y2 < miny || y1 > maxy ) return SUCCESS; diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h index ad9cb1b9fe0..64499bf955b 100644 --- a/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h +++ b/src/java.desktop/share/native/libfreetype/src/raster/ftraster.h @@ -4,7 +4,7 @@ * * The FreeType glyph rasterizer (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c index fd9f174f2e1..3fa008704e5 100644 --- a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c +++ b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.c @@ -4,7 +4,7 @@ * * The FreeType glyph rasterizer interface (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h index cf3e73c0a24..d838a942b04 100644 --- a/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h +++ b/src/java.desktop/share/native/libfreetype/src/raster/ftrend1.h @@ -4,7 +4,7 @@ * * The FreeType glyph rasterizer interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h b/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h index 326d42e0438..39d82a8051a 100644 --- a/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h +++ b/src/java.desktop/share/native/libfreetype/src/raster/rasterrs.h @@ -4,7 +4,7 @@ * * monochrome renderer error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c index 76181568af9..9df75dc3acc 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.c @@ -4,7 +4,7 @@ * * PNG Bitmap glyph support. * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * Google, Inc. * Written by Stuart Gill and Behdad Esfahbod. * diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h index 6e7a5c08e71..c59199e60df 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/pngshim.h @@ -4,7 +4,7 @@ * * PNG Bitmap glyph support. * - * Copyright (C) 2013-2024 by + * Copyright (C) 2013-2025 by * Google, Inc. * Written by Stuart Gill and Behdad Esfahbod. * diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c index 81072207b49..ec78247aa56 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.c @@ -4,7 +4,7 @@ * * High-level SFNT driver interface (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -895,7 +895,7 @@ FT_TRACE0(( "sfnt_get_var_ps_name:" " Shortening variation PS name prefix\n" )); FT_TRACE0(( " " - " to %d characters\n", len )); + " to %u characters\n", len )); } face->var_postscript_prefix = result; diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h index 6f71489fdc1..be4e33166c1 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfdriver.h @@ -4,7 +4,7 @@ * * High-level SFNT driver interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h index d3ca1d9aa8b..2da4ac776b0 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sferrors.h @@ -4,7 +4,7 @@ * * SFNT error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c index 6ee4e5e939b..6af35787e85 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.c @@ -4,7 +4,7 @@ * * SFNT object management (base). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -579,6 +579,9 @@ if ( face_instance_index < 0 && face_index > 0 ) face_index--; + /* Note that `face_index` is also used to enumerate elements */ + /* of containers like a Mac Resource; this means we must */ + /* check whether we actually have a TTC. */ if ( face_index >= face->ttc_header.count ) { if ( face_instance_index >= 0 ) @@ -1127,9 +1130,9 @@ flags |= FT_FACE_FLAG_VERTICAL; /* kerning available ? */ - if ( TT_FACE_HAS_KERNING( face ) + if ( face->kern_avail_bits #ifdef TT_CONFIG_OPTION_GPOS_KERNING - || face->gpos_kerning_available + || face->num_gpos_lookups_kerning #endif ) flags |= FT_FACE_FLAG_KERNING; diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h index 90847d95732..8c38b727950 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfobjs.h @@ -4,7 +4,7 @@ * * SFNT object management (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c index 14514bf9574..015c7b78b4d 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.c @@ -4,7 +4,7 @@ * * WOFF format management (base). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h index a04735ffe28..df7ace5c209 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff.h @@ -4,7 +4,7 @@ * * WOFFF format management (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c index 589b3e0c6b7..5acbbaa2a77 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.c @@ -4,7 +4,7 @@ * * WOFF2 format management (base). * - * Copyright (C) 2019-2024 by + * Copyright (C) 2019-2025 by * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -902,7 +902,7 @@ substreams[i].offset = pos + offset; substreams[i].size = substream_size; - FT_TRACE5(( " Substream %d: offset = %lu; size = %lu;\n", + FT_TRACE5(( " Substream %u: offset = %lu; size = %lu;\n", i, substreams[i].offset, substreams[i].size )); offset += substream_size; } @@ -1592,7 +1592,7 @@ WOFF2_TableRec table = *( indices[nn] ); - FT_TRACE3(( "Seeking to %ld with table size %ld.\n", + FT_TRACE3(( "Seeking to %lu with table size %lu.\n", table.src_offset, table.src_length )); FT_TRACE3(( "Table tag: %c%c%c%c.\n", (FT_Char)( table.Tag >> 24 ), @@ -1943,7 +1943,7 @@ src_offset += table->TransformLength; table->dst_offset = 0; - FT_TRACE2(( " %c%c%c%c %08d %08d %08ld %08ld %08ld\n", + FT_TRACE2(( " %c%c%c%c %08d %08d %08lu %08lu %08lu\n", (FT_Char)( table->Tag >> 24 ), (FT_Char)( table->Tag >> 16 ), (FT_Char)( table->Tag >> 8 ), diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h index f41140648dc..588761d0c8e 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/sfwoff2.h @@ -4,7 +4,7 @@ * * WOFFF2 format management (specification). * - * Copyright (C) 2019-2024 by + * Copyright (C) 2019-2025 by * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c index 28f4d1173c0..91b02344224 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.c @@ -4,7 +4,7 @@ * * TrueType character mapping table (cmap) support (body). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -179,7 +179,7 @@ cmap_info->format = 0; - cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + cmap_info->language = TT_PEEK_USHORT( p ); return FT_Err_Ok; } @@ -596,7 +596,7 @@ cmap_info->format = 2; - cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + cmap_info->language = TT_PEEK_USHORT( p ); return FT_Err_Ok; } @@ -1539,7 +1539,7 @@ cmap_info->format = 4; - cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + cmap_info->language = TT_PEEK_USHORT( p ); return FT_Err_Ok; } @@ -1712,7 +1712,7 @@ cmap_info->format = 6; - cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p ); + cmap_info->language = TT_PEEK_USHORT( p ); return FT_Err_Ok; } @@ -2009,7 +2009,7 @@ cmap_info->format = 8; - cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + cmap_info->language = TT_PEEK_ULONG( p ); return FT_Err_Ok; } @@ -2184,7 +2184,7 @@ cmap_info->format = 10; - cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + cmap_info->language = TT_PEEK_ULONG( p ); return FT_Err_Ok; } @@ -2528,7 +2528,7 @@ cmap_info->format = 12; - cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + cmap_info->language = TT_PEEK_ULONG( p ); return FT_Err_Ok; } @@ -2844,7 +2844,7 @@ cmap_info->format = 13; - cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p ); + cmap_info->language = TT_PEEK_ULONG( p ); return FT_Err_Ok; } @@ -3792,7 +3792,7 @@ return FT_THROW( Invalid_Table ); /* Version 1.8.3 of the OpenType specification contains the following */ - /* (https://docs.microsoft.com/en-us/typography/opentype/spec/cmap): */ + /* (https://learn.microsoft.com/typography/opentype/spec/cmap): */ /* */ /* The 'cmap' table version number remains at 0x0000 for fonts that */ /* make use of the newer subtable formats. */ @@ -3803,7 +3803,7 @@ p += 2; num_cmaps = TT_NEXT_USHORT( p ); - FT_TRACE4(( "tt_face_build_cmaps: %d cmaps\n", num_cmaps )); + FT_TRACE4(( "tt_face_build_cmaps: %u cmaps\n", num_cmaps )); limit = table + face->cmap_size; for ( ; num_cmaps > 0 && p + 8 <= limit; num_cmaps-- ) diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h index e2c5e72bf02..645e9e37e0c 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmap.h @@ -4,7 +4,7 @@ * * TrueType character mapping table (cmap) support (specification). * - * Copyright (C) 2002-2024 by + * Copyright (C) 2002-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h index 370898363f3..65807bb7378 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcmapc.h @@ -4,7 +4,7 @@ * * TT CMAP classes definitions (specification only). * - * Copyright (C) 2009-2024 by + * Copyright (C) 2009-2025 by * Oran Agra and Mickey Gabel. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c index b37658dde9e..ad9ad6f2710 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.c @@ -4,7 +4,7 @@ * * TrueType and OpenType colored glyph layer support (body). * - * Copyright (C) 2018-2024 by + * Copyright (C) 2018-2025 by * David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg. * * Originally written by Shao Yu Zhang . @@ -51,7 +51,7 @@ #define COLOR_STOP_SIZE 6U #define VAR_IDX_BASE_SIZE 4U #define LAYER_SIZE 4U -/* https://docs.microsoft.com/en-us/typography/opentype/spec/colr#colr-header */ +/* https://learn.microsoft.com/typography/opentype/spec/colr#colr-header */ /* 3 * uint16 + 2 * Offset32 */ #define COLRV0_HEADER_SIZE 14U /* COLRV0_HEADER_SIZE + 5 * Offset32 */ diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h index 30031464c73..3913acc74d5 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcolr.h @@ -4,7 +4,7 @@ * * TrueType and OpenType colored glyph layer support (specification). * - * Copyright (C) 2018-2024 by + * Copyright (C) 2018-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * Originally written by Shao Yu Zhang . diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c index 997eb869ffc..6d1208f6af2 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.c @@ -4,7 +4,7 @@ * * TrueType and OpenType color palette support (body). * - * Copyright (C) 2018-2024 by + * Copyright (C) 2018-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * Originally written by Shao Yu Zhang . diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h index bb301ae88b6..a0b4c9d927f 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttcpal.h @@ -4,7 +4,7 @@ * * TrueType and OpenType color palette support (specification). * - * Copyright (C) 2018-2024 by + * Copyright (C) 2018-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * Originally written by Shao Yu Zhang . diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c index f0411366af4..76618b0d3bb 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.c @@ -2,10 +2,9 @@ * * ttkern.c * - * Load the basic TrueType kerning table. This doesn't handle - * kerning data within the GPOS table at the moment. + * Routines to parse and access the 'kern' table for kerning (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h index a54e51df12d..e0075dce61d 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttkern.h @@ -2,10 +2,10 @@ * * ttkern.h * - * Load the basic TrueType kerning table. This doesn't handle - * kerning data within the GPOS table at the moment. + * Routines to parse and access the 'kern' table for kerning + * (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -40,8 +40,6 @@ FT_BEGIN_HEADER FT_UInt left_glyph, FT_UInt right_glyph ); -#define TT_FACE_HAS_KERNING( face ) ( (face)->kern_avail_bits != 0 ) - FT_END_HEADER diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c index c3a5fae2cb9..0c257ce4d31 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.c @@ -5,7 +5,7 @@ * Load the basic TrueType tables, i.e., tables that can be either in * TTF or OTF fonts (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -535,7 +535,8 @@ * The tag of table to load. Use the value 0 if you want * to access the whole font file, else set this parameter * to a valid TrueType table tag that you can forge with - * the MAKE_TT_TAG macro. + * the MAKE_TT_TAG macro. Use value 1 to access the table + * directory. * * offset :: * The starting offset in the table (or the file if @@ -577,7 +578,29 @@ FT_ULong size; - if ( tag != 0 ) + if ( tag == 0 ) + { + /* The whole font file. */ + size = face->root.stream->size; + } + else if ( tag == 1 ) + { + /* The currently selected font's table directory. */ + /* */ + /* Note that `face_index` is also used to enumerate elements */ + /* of containers like a Mac Resource; this means we must */ + /* check whether we actually have a TTC (with multiple table */ + /* directories). */ + FT_Long idx = face->root.face_index & 0xFFFF; + + + if ( idx >= face->ttc_header.count ) + idx = 0; + + offset += face->ttc_header.offsets[idx]; + size = 4 + 8 + 16 * face->num_tables; + } + else { /* look for tag in font directory */ table = tt_face_lookup_table( face, tag ); @@ -590,9 +613,6 @@ offset += table->Offset; size = table->Length; } - else - /* tag == 0 -- the user wants to access the font file directly */ - size = face->root.stream->size; if ( length && *length == 0 ) { diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h index 2b1d62d9bd9..e3666c901b1 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttload.h @@ -5,7 +5,7 @@ * Load the basic TrueType tables, i.e., tables that can be either in * TTF or OTF fonts (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c index 27884118563..541d8447470 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.c @@ -4,7 +4,7 @@ * * Load the metrics tables common to TTF and OTF fonts (body). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -306,7 +306,7 @@ } #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - if ( var && face->blend ) + if ( var && FT_IS_VARIATION( &face->root ) ) { FT_Face f = FT_FACE( face ); FT_Int a = (FT_Int)*aadvance; diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h index 34b3c0e18f2..1ee84507f15 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttmtx.h @@ -4,7 +4,7 @@ * * Load the metrics tables common to TTF and OTF fonts (specification). * - * Copyright (C) 2006-2024 by + * Copyright (C) 2006-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c index 5698a62c8d1..4246b6c8eff 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.c @@ -5,7 +5,7 @@ * PostScript name table processing for TrueType and OpenType fonts * (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h index 150db6c3981..a11b6696854 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttpost.h @@ -5,7 +5,7 @@ * PostScript name table processing for TrueType and OpenType fonts * (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c index cb3a8abf182..9f9013bf39b 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.c @@ -4,7 +4,7 @@ * * TrueType and OpenType embedded bitmap support (body). * - * Copyright (C) 2005-2024 by + * Copyright (C) 2005-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * Copyright 2013 by Google, Inc. @@ -342,7 +342,7 @@ FT_TRACE2(( "tt_face_load_strike_metrics:" " sanitizing invalid ascender and descender\n" )); FT_TRACE2(( " " - " values for strike %ld (%dppem, %dppem)\n", + " values for strike %lu (%dppem, %dppem)\n", strike_index, metrics->x_ppem, metrics->y_ppem )); @@ -993,7 +993,7 @@ goto Fail; } - FT_TRACE3(( "tt_sbit_decoder_load_compound: loading %d component%s\n", + FT_TRACE3(( "tt_sbit_decoder_load_compound: loading %u component%s\n", num_components, num_components == 1 ? "" : "s" )); @@ -1419,7 +1419,7 @@ image_start = image_offset + image_start; FT_TRACE3(( "tt_sbit_decoder_load_image:" - " found sbit (format %d) for glyph index %d\n", + " found sbit (format %u) for glyph index %u\n", image_format, glyph_index )); return tt_sbit_decoder_load_bitmap( decoder, @@ -1438,13 +1438,13 @@ if ( recurse_count ) { FT_TRACE4(( "tt_sbit_decoder_load_image:" - " missing subglyph sbit with glyph index %d\n", + " missing subglyph sbit with glyph index %u\n", glyph_index )); return FT_THROW( Invalid_Composite ); } FT_TRACE4(( "tt_sbit_decoder_load_image:" - " no sbit found for glyph index %d\n", glyph_index )); + " no sbit found for glyph index %u\n", glyph_index )); return FT_THROW( Missing_Bitmap ); } @@ -1462,12 +1462,13 @@ FT_Int originOffsetX, originOffsetY; FT_Tag graphicType; FT_Int recurse_depth = 0; + FT_Bool flipped = FALSE; FT_Error error; FT_Byte* p; - FT_UNUSED( map ); #ifndef FT_CONFIG_OPTION_USE_PNG + FT_UNUSED( map ); FT_UNUSED( metrics_only ); #endif @@ -1517,12 +1518,16 @@ switch ( graphicType ) { + case FT_MAKE_TAG( 'f', 'l', 'i', 'p' ): + flipped = !flipped; + FALL_THROUGH; + case FT_MAKE_TAG( 'd', 'u', 'p', 'e' ): - if ( recurse_depth < 4 ) + if ( recurse_depth++ < 4 ) { glyph_index = FT_GET_USHORT(); FT_FRAME_EXIT(); - recurse_depth++; + goto retry; } error = FT_THROW( Invalid_File_Format ); @@ -1540,6 +1545,38 @@ glyph_end - glyph_start - 8, TRUE, metrics_only ); + if ( flipped && !metrics_only && !error ) + { + FT_UInt32* curr_pos = (FT_UInt32*)map->buffer; + + /* `Load_SBit_Png` always returns a pixmap with 32 bits per pixel */ + /* and no extra pitch bytes. */ + FT_UInt width = map->width; + FT_UInt y; + + + for ( y = 0; y < map->rows; y++ ) + { + FT_UInt32* left = curr_pos; + FT_UInt32* right = curr_pos + width - 1; + + + while ( left < right ) + { + FT_UInt32 value; + + + value = *right; + *right = *left; + *left = value; + + left++; + right--; + } + + curr_pos += width; + } + } #else error = FT_THROW( Unimplemented_Feature ); #endif diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h index 96f80a58424..7427149d68f 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/ttsbit.h @@ -4,7 +4,7 @@ * * TrueType and OpenType embedded bitmap support (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c index 532ccfa1737..0f9e3889aab 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.c @@ -4,7 +4,7 @@ * * WOFF2 Font table tags (base). * - * Copyright (C) 2019-2024 by + * Copyright (C) 2019-2025 by * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h index d03b4b41bc9..e223022962e 100644 --- a/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h +++ b/src/java.desktop/share/native/libfreetype/src/sfnt/woff2tags.h @@ -4,7 +4,7 @@ * * WOFF2 Font table tags (specification). * - * Copyright (C) 2019-2024 by + * Copyright (C) 2019-2025 by * Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c index b7c0632a6fa..365fbad42ad 100644 --- a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c +++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.c @@ -4,7 +4,7 @@ * * A new `perfect' anti-aliasing renderer (body). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -157,10 +157,6 @@ #define ft_memset memset -#define ft_setjmp setjmp -#define ft_longjmp longjmp -#define ft_jmp_buf jmp_buf - typedef ptrdiff_t FT_PtrDist; @@ -170,8 +166,8 @@ typedef ptrdiff_t FT_PtrDist; #define Smooth_Err_Invalid_Argument -3 #define Smooth_Err_Raster_Overflow -4 -#define FT_BEGIN_HEADER -#define FT_END_HEADER +#define FT_BEGIN_HEADER /* nothing */ +#define FT_END_HEADER /* nothing */ #include "ftimage.h" #include "ftgrays.h" @@ -495,6 +491,7 @@ typedef ptrdiff_t FT_PtrDist; TCoord min_ey, max_ey; TCoord count_ey; /* same as (max_ey - min_ey) */ + int error; /* pool overflow exception */ PCell cell; /* current cell */ PCell cell_free; /* call allocation next free slot */ PCell cell_null; /* last cell, used as dumpster and limit */ @@ -510,8 +507,6 @@ typedef ptrdiff_t FT_PtrDist; FT_Raster_Span_Func render_span; void* render_span_data; - ft_jmp_buf jump_buffer; - } gray_TWorker, *gray_PWorker; #if defined( _MSC_VER ) @@ -613,9 +608,14 @@ typedef ptrdiff_t FT_PtrDist; } /* insert new cell */ - cell = ras.cell_free++; - if ( cell >= ras.cell_null ) - ft_longjmp( ras.jump_buffer, 1 ); + cell = ras.cell_free; + if ( cell == ras.cell_null ) + { + ras.error = FT_THROW( Raster_Overflow ); + goto Found; + } + + ras.cell_free = cell + 1; cell->x = ex; cell->area = 0; @@ -1353,7 +1353,8 @@ typedef ptrdiff_t FT_PtrDist; ras.x = x; ras.y = y; - return 0; + + return ras.error; } @@ -1365,7 +1366,8 @@ typedef ptrdiff_t FT_PtrDist; gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) ); - return 0; + + return ras.error; } @@ -1378,7 +1380,8 @@ typedef ptrdiff_t FT_PtrDist; gray_render_conic( RAS_VAR_ control, to ); - return 0; + + return ras.error; } @@ -1392,7 +1395,8 @@ typedef ptrdiff_t FT_PtrDist; gray_render_cubic( RAS_VAR_ control1, control2, to ); - return 0; + + return ras.error; } @@ -1700,30 +1704,22 @@ typedef ptrdiff_t FT_PtrDist; gray_convert_glyph_inner( RAS_ARG_ int continued ) { - volatile int error; + int error; - if ( ft_setjmp( ras.jump_buffer ) == 0 ) - { - if ( continued ) - FT_Trace_Disable(); - error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); - if ( continued ) - FT_Trace_Enable(); + if ( continued ) + FT_Trace_Disable(); + error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); + if ( continued ) + FT_Trace_Enable(); - FT_TRACE7(( "band [%d..%d]: %td cell%s remaining\n", - ras.min_ey, - ras.max_ey, - ras.cell_null - ras.cell_free, - ras.cell_null - ras.cell_free == 1 ? "" : "s" )); - } - else - { - error = FT_THROW( Raster_Overflow ); - - FT_TRACE7(( "band [%d..%d]: to be bisected\n", - ras.min_ey, ras.max_ey )); - } + FT_TRACE7(( error == Smooth_Err_Raster_Overflow + ? "band [%d..%d]: to be bisected\n" + : "band [%d..%d]: %td cell%s remaining\n", + ras.min_ey, + ras.max_ey, + ras.cell_null - ras.cell_free, + ras.cell_null - ras.cell_free == 1 ? "" : "s" )); return error; } @@ -1873,6 +1869,7 @@ typedef ptrdiff_t FT_PtrDist; TCoord* band; int continued = 0; + int error = Smooth_Err_Ok; /* Initialize the null cell at the end of the poll. */ @@ -1907,7 +1904,6 @@ typedef ptrdiff_t FT_PtrDist; do { TCoord i; - int error; ras.min_ex = band[1]; @@ -1922,6 +1918,7 @@ typedef ptrdiff_t FT_PtrDist; ras.cell_free = buffer + n; ras.cell = ras.cell_null; + ras.error = Smooth_Err_Ok; error = gray_convert_glyph_inner( RAS_VAR_ continued ); continued = 1; @@ -1936,7 +1933,7 @@ typedef ptrdiff_t FT_PtrDist; continue; } else if ( error != Smooth_Err_Raster_Overflow ) - return error; + goto Exit; /* render pool overflow; we will reduce the render band by half */ i = ( band[0] - band[1] ) >> 1; @@ -1945,7 +1942,8 @@ typedef ptrdiff_t FT_PtrDist; if ( i == 0 ) { FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" )); - return FT_THROW( Raster_Overflow ); + error = FT_THROW( Raster_Overflow ); + goto Exit; } band++; @@ -1954,7 +1952,11 @@ typedef ptrdiff_t FT_PtrDist; } while ( band >= bands ); } - return Smooth_Err_Ok; + Exit: + ras.cell = ras.cell_free = ras.cell_null = NULL; + ras.ycells = NULL; + + return error; } diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h index 940fbe8c79b..e463e5b3eb8 100644 --- a/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h +++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftgrays.h @@ -4,7 +4,7 @@ * * FreeType smooth renderer declaration * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -19,11 +19,6 @@ #ifndef FTGRAYS_H_ #define FTGRAYS_H_ -#ifdef __cplusplus - extern "C" { -#endif - - #ifdef STANDALONE_ #include "ftimage.h" #else @@ -31,6 +26,7 @@ #include #endif +FT_BEGIN_HEADER /************************************************************************** * @@ -46,10 +42,7 @@ FT_EXPORT_VAR( const FT_Raster_Funcs ) ft_grays_raster; - -#ifdef __cplusplus - } -#endif +FT_END_HEADER #endif /* FTGRAYS_H_ */ diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h index 6d41fb8e0fd..8d5068549fa 100644 --- a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h +++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmerrs.h @@ -4,7 +4,7 @@ * * smooth renderer error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c index f0acc1ea4a6..21a6eca00ba 100644 --- a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c +++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.c @@ -4,7 +4,7 @@ * * Anti-aliasing renderer interface (body). * - * Copyright (C) 2000-2024 by + * Copyright (C) 2000-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h index d7b61a9e60e..f76708ae701 100644 --- a/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h +++ b/src/java.desktop/share/native/libfreetype/src/smooth/ftsmooth.h @@ -4,7 +4,7 @@ * * Anti-aliasing renderer interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c index 4ab68eb9a12..6369d83d6d5 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.c @@ -4,7 +4,7 @@ * * TrueType font driver implementation (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -220,12 +220,12 @@ { /* Use 'kern' table if available since that can be faster; otherwise */ /* use GPOS kerning pairs if available. */ - if ( ttface->kern_avail_bits != 0 ) + if ( ttface->kern_avail_bits ) kerning->x = sfnt->get_kerning( ttface, left_glyph, right_glyph ); #ifdef TT_CONFIG_OPTION_GPOS_KERNING - else if ( ttface->gpos_kerning_available ) + else if ( ttface->num_gpos_lookups_kerning ) kerning->x = sfnt->get_gpos_kerning( ttface, left_glyph, right_glyph ); diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h index 3e1cf234fcf..943eaae3482 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttdriver.h @@ -4,7 +4,7 @@ * * High-level TrueType driver interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h b/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h index 7ad937bd04d..631dbf5a80f 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h +++ b/src/java.desktop/share/native/libfreetype/src/truetype/tterrors.h @@ -4,7 +4,7 @@ * * TrueType error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c index b656ccf04e3..e810ebfd550 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.c @@ -4,7 +4,7 @@ * * TrueType Glyph Loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -660,7 +660,7 @@ } while ( subglyph->flags & MORE_COMPONENTS ); gloader->current.num_subglyphs = num_subglyphs; - FT_TRACE5(( " %d component%s\n", + FT_TRACE5(( " %u component%s\n", num_subglyphs, num_subglyphs > 1 ? "s" : "" )); @@ -674,7 +674,7 @@ for ( i = 0; i < num_subglyphs; i++ ) { if ( num_subglyphs > 1 ) - FT_TRACE7(( " subglyph %d:\n", i )); + FT_TRACE7(( " subglyph %u:\n", i )); FT_TRACE7(( " glyph index: %d\n", subglyph->index )); @@ -777,15 +777,11 @@ TT_Hint_Glyph( TT_Loader loader, FT_Bool is_composite ) { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - TT_Face face = loader->face; - TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); -#endif - TT_GlyphZone zone = &loader->zone; #ifdef TT_USE_BYTECODE_INTERPRETER TT_ExecContext exec = loader->exec; + TT_Size size = loader->size; FT_Long n_ins = exec->glyphSize; #else FT_UNUSED( is_composite ); @@ -797,9 +793,6 @@ if ( n_ins > 0 ) FT_ARRAY_COPY( zone->org, zone->cur, zone->n_points ); - /* Reset graphics state. */ - exec->GS = loader->size->GS; - /* XXX: UNDOCUMENTED! Hinting instructions of a composite glyph */ /* completely refer to the (already) hinted subglyphs. */ if ( is_composite ) @@ -811,8 +804,8 @@ } else { - exec->metrics.x_scale = loader->size->metrics->x_scale; - exec->metrics.y_scale = loader->size->metrics->y_scale; + exec->metrics.x_scale = size->metrics->x_scale; + exec->metrics.y_scale = size->metrics->y_scale; } #endif @@ -838,7 +831,7 @@ exec->is_composite = is_composite; exec->pts = *zone; - error = TT_Run_Context( exec ); + error = TT_Run_Context( exec, size ); if ( error && exec->pedantic_hinting ) return error; @@ -854,8 +847,7 @@ /* to change bearings or advance widths. */ #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && - exec->backward_compatibility ) + if ( exec->backward_compatibility ) return FT_Err_Ok; #endif @@ -1152,30 +1144,15 @@ x = FT_MulFix( x, x_scale ); y = FT_MulFix( y, y_scale ); - if ( subglyph->flags & ROUND_XY_TO_GRID ) + if ( subglyph->flags & ROUND_XY_TO_GRID && + IS_HINTED( loader->load_flags ) ) { - TT_Face face = loader->face; - TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + if ( !loader->exec->backward_compatibility ) +#endif + x = FT_PIX_ROUND( x ); - - if ( IS_HINTED( loader->load_flags ) ) - { - /* - * We round the horizontal offset only if there is hinting along - * the x axis; this corresponds to integer advance width values. - * - * Theoretically, a glyph's bytecode can toggle ClearType's - * `backward compatibility' mode, which would allow modification - * of the advance width. In reality, however, applications - * neither allow nor expect modified advance widths if subpixel - * rendering is active. - * - */ - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_35 ) - x = FT_PIX_ROUND( x ); - - y = FT_PIX_ROUND( y ); - } + y = FT_PIX_ROUND( y ); } } } @@ -1204,8 +1181,6 @@ { FT_Error error; FT_Outline* outline = &loader->gloader->base.outline; - FT_Stream stream = loader->stream; - FT_UShort n_ins; FT_UInt i; @@ -1224,8 +1199,10 @@ #ifdef TT_USE_BYTECODE_INTERPRETER { - TT_ExecContext exec = loader->exec; + TT_ExecContext exec = loader->exec; FT_Memory memory = exec->memory; + FT_Stream stream = loader->stream; + FT_UShort n_ins; if ( exec->glyphSize ) @@ -1378,8 +1355,9 @@ if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && loader->exec && - loader->exec->subpixel_hinting_lean && - loader->exec->grayscale_cleartype ) + loader->exec->mode != FT_RENDER_MODE_MONO && + loader->exec->mode != FT_RENDER_MODE_LCD && + loader->exec->mode != FT_RENDER_MODE_LCD_V ) { loader->pp3.x = loader->advance / 2; loader->pp4.x = loader->advance / 2; @@ -1444,13 +1422,13 @@ #ifdef FT_DEBUG_LEVEL_TRACE if ( recurse_count ) - FT_TRACE5(( " nesting level: %d\n", recurse_count )); + FT_TRACE5(( " nesting level: %u\n", recurse_count )); #endif /* some fonts have an incorrect value of `maxComponentDepth' */ if ( recurse_count > face->max_profile.maxComponentDepth ) { - FT_TRACE1(( "load_truetype_glyph: maxComponentDepth set to %d\n", + FT_TRACE1(( "load_truetype_glyph: maxComponentDepth set to %u\n", recurse_count )); face->max_profile.maxComponentDepth = (FT_UShort)recurse_count; } @@ -1566,18 +1544,18 @@ if ( header_only ) goto Exit; +#ifdef FT_CONFIG_OPTION_INCREMENTAL + tt_get_metrics_incremental( loader, glyph_index ); +#endif + tt_loader_set_pp( loader ); + + /* shortcut for empty glyphs */ if ( loader->byte_len == 0 || loader->n_contours == 0 ) { -#ifdef FT_CONFIG_OPTION_INCREMENTAL - tt_get_metrics_incremental( loader, glyph_index ); -#endif - tt_loader_set_pp( loader ); - #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) || - FT_IS_VARIATION( FT_FACE( face ) ) ) + if ( !IS_DEFAULT_INSTANCE( FT_FACE( face ) ) ) { /* a small outline structure with four elements for */ /* communication with `TT_Vary_Apply_Glyph_Deltas' */ @@ -1627,11 +1605,6 @@ goto Exit; } -#ifdef FT_CONFIG_OPTION_INCREMENTAL - tt_get_metrics_incremental( loader, glyph_index ); -#endif - tt_loader_set_pp( loader ); - /***********************************************************************/ /***********************************************************************/ @@ -1735,8 +1708,7 @@ #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) || - FT_IS_VARIATION( FT_FACE( face ) ) ) + if ( !IS_DEFAULT_INSTANCE( FT_FACE( face ) ) ) { FT_UShort i, limit; FT_SubGlyph subglyph; @@ -1953,6 +1925,9 @@ #ifdef FT_CONFIG_OPTION_INCREMENTAL + /* restore the original stream */ + loader->stream = face->root.stream; + if ( glyph_data_loaded ) face->root.internal->incremental_interface->funcs->free_glyph_data( face->root.internal->incremental_interface->object, @@ -2112,7 +2087,6 @@ { TT_Face face = (TT_Face)glyph->face; SFNT_Service sfnt = (SFNT_Service)face->sfnt; - FT_Stream stream = face->root.stream; FT_Error error; TT_SBit_MetricsRec sbit_metrics; @@ -2121,14 +2095,11 @@ size->strike_index, glyph_index, (FT_UInt)load_flags, - stream, + face->root.stream, &glyph->bitmap, &sbit_metrics ); if ( !error ) { - glyph->outline.n_points = 0; - glyph->outline.n_contours = 0; - glyph->metrics.width = (FT_Pos)sbit_metrics.width * 64; glyph->metrics.height = (FT_Pos)sbit_metrics.height * 64; @@ -2153,6 +2124,50 @@ glyph->bitmap_top = sbit_metrics.horiBearingY; } } + /* a missing glyph in a bitmap-only font is assumed whitespace */ + /* that needs to be constructed using metrics data from `hmtx' */ + /* and, optionally, `vmtx' tables */ + else if ( FT_ERR_EQ( error, Missing_Bitmap ) && + !FT_IS_SCALABLE( glyph->face ) && + face->horz_metrics_size ) + { + FT_Fixed x_scale = size->root.metrics.x_scale; + FT_Fixed y_scale = size->root.metrics.y_scale; + + FT_Short left_bearing = 0; + FT_Short top_bearing = 0; + + FT_UShort advance_width = 0; + FT_UShort advance_height = 0; + + + TT_Get_HMetrics( face, glyph_index, + &left_bearing, + &advance_width ); + TT_Get_VMetrics( face, glyph_index, + 0, + &top_bearing, + &advance_height ); + + glyph->metrics.width = 0; + glyph->metrics.height = 0; + + glyph->metrics.horiBearingX = FT_MulFix( left_bearing, x_scale ); + glyph->metrics.horiBearingY = 0; + glyph->metrics.horiAdvance = FT_MulFix( advance_width, x_scale ); + + glyph->metrics.vertBearingX = 0; + glyph->metrics.vertBearingY = FT_MulFix( top_bearing, y_scale ); + glyph->metrics.vertAdvance = FT_MulFix( advance_height, y_scale ); + + glyph->format = FT_GLYPH_FORMAT_BITMAP; + glyph->bitmap.pixel_mode = FT_PIXEL_MODE_MONO; + + glyph->bitmap_left = 0; + glyph->bitmap_top = 0; + + error = FT_Err_Ok; + } return error; } @@ -2168,15 +2183,6 @@ FT_Bool glyf_table_only ) { TT_Face face = (TT_Face)glyph->face; - FT_Stream stream = face->root.stream; - -#ifdef TT_USE_BYTECODE_INTERPRETER - FT_Error error; - FT_Bool pedantic = FT_BOOL( load_flags & FT_LOAD_PEDANTIC ); -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( glyph->face ); -#endif -#endif FT_ZERO( loader ); @@ -2186,122 +2192,78 @@ /* load execution context */ if ( IS_HINTED( load_flags ) && !glyf_table_only ) { + FT_Error error; TT_ExecContext exec; - FT_Bool grayscale = TRUE; + FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags ); + FT_Bool grayscale = FT_BOOL( mode != FT_RENDER_MODE_MONO ); + FT_Bool reexecute = FALSE; #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - FT_Bool subpixel_hinting_lean; - FT_Bool grayscale_cleartype; + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( glyph->face ); #endif - FT_Bool reexecute = FALSE; - - if ( size->bytecode_ready < 0 || size->cvt_ready < 0 ) + if ( size->bytecode_ready > 0 ) + return size->bytecode_ready; + if ( size->bytecode_ready < 0 ) { - error = tt_size_ready_bytecode( size, pedantic ); + FT_Bool pedantic = FT_BOOL( load_flags & FT_LOAD_PEDANTIC ); + + + error = tt_size_init_bytecode( size, pedantic ); if ( error ) return error; } - else if ( size->bytecode_ready ) - return size->bytecode_ready; - else if ( size->cvt_ready ) - return size->cvt_ready; - /* query new execution context */ exec = size->context; - if ( !exec ) - return FT_THROW( Could_Not_Find_Context ); - - grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - FT_RENDER_MODE_MONO ); #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) { - subpixel_hinting_lean = - FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - FT_RENDER_MODE_MONO ); - grayscale_cleartype = - FT_BOOL( subpixel_hinting_lean && - !( ( load_flags & - FT_LOAD_TARGET_LCD ) || - ( load_flags & - FT_LOAD_TARGET_LCD_V ) ) ); - exec->vertical_lcd_lean = - FT_BOOL( subpixel_hinting_lean && - ( load_flags & - FT_LOAD_TARGET_LCD_V ) ); - grayscale = FT_BOOL( grayscale && !subpixel_hinting_lean ); - } - else - { - subpixel_hinting_lean = FALSE; - grayscale_cleartype = FALSE; - exec->vertical_lcd_lean = FALSE; + grayscale = FALSE; + + /* any mode change requires a re-execution of the CVT program */ + if ( mode != exec->mode ) + { + FT_TRACE4(( "tt_loader_init: render mode change," + " re-executing `prep' table\n" )); + + exec->mode = mode; + reexecute = TRUE; + } } #endif + /* a change from mono to grayscale rendering (and vice versa) */ + /* requires a re-execution of the CVT program */ + if ( grayscale != exec->grayscale ) + { + FT_TRACE4(( "tt_loader_init: grayscale hinting change," + " re-executing `prep' table\n" )); + + exec->grayscale = grayscale; + reexecute = TRUE; + } + + if ( size->cvt_ready > 0 ) + return size->cvt_ready; + if ( size->cvt_ready < 0 || reexecute ) + { + error = tt_size_run_prep( size ); + if ( error ) + return error; + } + error = TT_Load_Context( exec, face, size ); if ( error ) return error; - { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) - { - /* a change from mono to subpixel rendering (and vice versa) */ - /* requires a re-execution of the CVT program */ - if ( subpixel_hinting_lean != exec->subpixel_hinting_lean ) - { - FT_TRACE4(( "tt_loader_init: subpixel hinting change," - " re-executing `prep' table\n" )); - - exec->subpixel_hinting_lean = subpixel_hinting_lean; - reexecute = TRUE; - } - - /* a change from colored to grayscale subpixel rendering (and */ - /* vice versa) requires a re-execution of the CVT program */ - if ( grayscale_cleartype != exec->grayscale_cleartype ) - { - FT_TRACE4(( "tt_loader_init: grayscale subpixel hinting change," - " re-executing `prep' table\n" )); - - exec->grayscale_cleartype = grayscale_cleartype; - reexecute = TRUE; - } - } -#endif - - /* a change from mono to grayscale rendering (and vice versa) */ - /* requires a re-execution of the CVT program */ - if ( grayscale != exec->grayscale ) - { - FT_TRACE4(( "tt_loader_init: grayscale hinting change," - " re-executing `prep' table\n" )); - - exec->grayscale = grayscale; - reexecute = TRUE; - } - } - - if ( reexecute ) - { - error = tt_size_run_prep( size, pedantic ); - if ( error ) - return error; - error = TT_Load_Context( exec, face, size ); - if ( error ) - return error; - } - /* check whether the cvt program has disabled hinting */ - if ( exec->GS.instruct_control & 1 ) + if ( size->GS.instruct_control & 1 ) load_flags |= FT_LOAD_NO_HINTING; - /* load default graphics state -- if needed */ - if ( exec->GS.instruct_control & 2 ) - exec->GS = tt_default_graphics_state; + /* check whether GS modifications should be reverted */ + if ( size->GS.instruct_control & 2 ) + size->GS = tt_default_graphics_state; #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* @@ -2318,28 +2280,25 @@ * */ if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && - subpixel_hinting_lean && + mode != FT_RENDER_MODE_MONO && !FT_IS_TRICKY( glyph->face ) ) - exec->backward_compatibility = !( exec->GS.instruct_control & 4 ); + exec->backward_compatibility = ( size->GS.instruct_control & 4 ) ^ 4; else - exec->backward_compatibility = FALSE; + exec->backward_compatibility = 0; #endif /* TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL */ - exec->pedantic_hinting = FT_BOOL( load_flags & FT_LOAD_PEDANTIC ); loader->exec = exec; - loader->instructions = exec->glyphIns; /* Use the hdmx table if any unless FT_LOAD_COMPUTE_METRICS */ /* is set or backward compatibility mode of the v38 or v40 */ /* interpreters is active. See `ttinterp.h' for details on */ /* backward compatibility mode. */ - if ( IS_HINTED( loader->load_flags ) && - !( loader->load_flags & FT_LOAD_COMPUTE_METRICS ) && + if ( IS_HINTED( load_flags ) && + !( load_flags & FT_LOAD_COMPUTE_METRICS ) && #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - !( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && - exec->backward_compatibility ) && + !exec->backward_compatibility && #endif - !face->postscript.isFixedPitch ) + !face->postscript.isFixedPitch ) { loader->widthp = size->widthp; } @@ -2364,7 +2323,7 @@ loader->face = face; loader->size = size; loader->glyph = (FT_GlyphSlot)glyph; - loader->stream = stream; + loader->stream = face->root.stream; loader->composites.head = NULL; loader->composites.tail = NULL; @@ -2426,84 +2385,26 @@ TT_LoaderRec loader; - FT_TRACE1(( "TT_Load_Glyph: glyph index %d\n", glyph_index )); + FT_TRACE1(( "TT_Load_Glyph: glyph index %u\n", glyph_index )); #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS /* try to load embedded bitmap (if any) */ - if ( size->strike_index != 0xFFFFFFFFUL && - ( load_flags & FT_LOAD_NO_BITMAP ) == 0 && - IS_DEFAULT_INSTANCE( glyph->face ) ) + if ( size->strike_index != 0xFFFFFFFFUL && + !( load_flags & FT_LOAD_NO_BITMAP && + FT_IS_SCALABLE( glyph->face ) ) && + IS_DEFAULT_INSTANCE( glyph->face ) ) { - FT_Fixed x_scale = size->root.metrics.x_scale; - FT_Fixed y_scale = size->root.metrics.y_scale; - - error = load_sbit_image( size, glyph, glyph_index, load_flags ); - if ( FT_ERR_EQ( error, Missing_Bitmap ) ) - { - /* the bitmap strike is incomplete and misses the requested glyph; */ - /* if we have a bitmap-only font, return an empty glyph */ - if ( !FT_IS_SCALABLE( glyph->face ) ) - { - FT_Short left_bearing = 0; - FT_Short top_bearing = 0; - - FT_UShort advance_width = 0; - FT_UShort advance_height = 0; - - - /* to return an empty glyph, however, we need metrics data */ - /* from the `hmtx' (or `vmtx') table; the assumption is that */ - /* empty glyphs are missing intentionally, representing */ - /* whitespace - not having at least horizontal metrics is */ - /* thus considered an error */ - if ( !face->horz_metrics_size ) - return error; - - /* we now construct an empty bitmap glyph */ - TT_Get_HMetrics( face, glyph_index, - &left_bearing, - &advance_width ); - TT_Get_VMetrics( face, glyph_index, - 0, - &top_bearing, - &advance_height ); - - glyph->outline.n_points = 0; - glyph->outline.n_contours = 0; - - glyph->metrics.width = 0; - glyph->metrics.height = 0; - - glyph->metrics.horiBearingX = FT_MulFix( left_bearing, x_scale ); - glyph->metrics.horiBearingY = 0; - glyph->metrics.horiAdvance = FT_MulFix( advance_width, x_scale ); - - glyph->metrics.vertBearingX = 0; - glyph->metrics.vertBearingY = FT_MulFix( top_bearing, y_scale ); - glyph->metrics.vertAdvance = FT_MulFix( advance_height, y_scale ); - - glyph->format = FT_GLYPH_FORMAT_BITMAP; - glyph->bitmap.pixel_mode = FT_PIXEL_MODE_MONO; - - glyph->bitmap_left = 0; - glyph->bitmap_top = 0; - - return FT_Err_Ok; - } - } - else if ( error ) - { - /* return error if font is not scalable */ - if ( !FT_IS_SCALABLE( glyph->face ) ) - return error; - } - else + if ( !error ) { if ( FT_IS_SCALABLE( glyph->face ) || FT_HAS_SBIX( glyph->face ) ) { + FT_Fixed x_scale = size->root.metrics.x_scale; + FT_Fixed y_scale = size->root.metrics.y_scale; + + /* for the bbox we need the header only */ (void)tt_loader_init( &loader, size, glyph, load_flags, TRUE ); (void)load_truetype_glyph( &loader, glyph_index, 0, TRUE ); @@ -2550,8 +2451,10 @@ y_scale ); } - return FT_Err_Ok; + goto Exit; } + else if ( !FT_IS_SCALABLE( glyph->face ) ) + goto Exit; } if ( load_flags & FT_LOAD_SBITS_ONLY ) @@ -2563,7 +2466,7 @@ #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ /* if FT_LOAD_NO_SCALE is not set, `ttmetrics' must be valid */ - if ( !( load_flags & FT_LOAD_NO_SCALE ) && !size->ttmetrics.valid ) + if ( !( load_flags & FT_LOAD_NO_SCALE ) && !size->ttmetrics.ppem ) { error = FT_THROW( Invalid_Size_Handle ); goto Exit; @@ -2614,7 +2517,7 @@ glyph->metrics.horiAdvance = FT_MulFix( advanceX, x_scale ); glyph->metrics.vertAdvance = FT_MulFix( advanceY, y_scale ); - return error; + goto Exit; } FT_TRACE3(( "Failed to load SVG glyph\n" )); @@ -2642,10 +2545,6 @@ goto Done; } - glyph->format = FT_GLYPH_FORMAT_OUTLINE; - glyph->num_subglyphs = 0; - glyph->outline.flags = 0; - /* main loading loop */ error = load_truetype_glyph( &loader, glyph_index, 0, FALSE ); if ( !error ) @@ -2657,9 +2556,18 @@ } else { + glyph->format = FT_GLYPH_FORMAT_OUTLINE; + glyph->outline = loader.gloader->base.outline; glyph->outline.flags &= ~FT_OUTLINE_SINGLE_PASS; + /* Set the `high precision' bit flag. This is _critical_ to */ + /* get correct output for monochrome TrueType glyphs at all */ + /* sizes using the bytecode interpreter. */ + if ( !( load_flags & FT_LOAD_NO_SCALE ) && + size->metrics->y_ppem < 24 ) + glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; + /* Translate array so that (0,0) is the glyph's origin. Note */ /* that this behaviour is independent on the value of bit 1 of */ /* the `flags' field in the `head' table -- at least major */ @@ -2708,14 +2616,6 @@ error = compute_glyph_metrics( &loader, glyph_index ); } - /* Set the `high precision' bit flag. */ - /* This is _critical_ to get correct output for monochrome */ - /* TrueType glyphs at all sizes using the bytecode interpreter. */ - /* */ - if ( !( load_flags & FT_LOAD_NO_SCALE ) && - size->metrics->y_ppem < 24 ) - glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; - FT_TRACE1(( " subglyphs = %u, contours = %hu, points = %hu," " flags = 0x%.3x\n", loader.gloader->base.num_subglyphs, @@ -2727,11 +2627,8 @@ tt_loader_done( &loader ); Exit: -#ifdef FT_DEBUG_LEVEL_TRACE - if ( error ) - FT_TRACE1(( " failed (error code 0x%x)\n", - error )); -#endif + FT_TRACE1(( error ? " failed (error code 0x%x)\n" : "", + error )); return error; } diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h index 22ea967f301..39d6ae3664c 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgload.h @@ -4,7 +4,7 @@ * * TrueType Glyph Loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c index 4f0083c96b7..0732fcbae36 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.c @@ -4,7 +4,7 @@ * * TrueType GX Font Variation loader * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. * * This file is part of the FreeType project, and may only be used, @@ -489,8 +489,9 @@ FT_UShort axis_count; FT_UInt region_count; - FT_UInt i, j; - FT_Bool long_words; + FT_UInt i, j; + FT_Byte* bytes; + FT_Bool long_words; GX_Blend blend = ttface->blend; FT_ULong* dataOffsetArray = NULL; @@ -526,11 +527,15 @@ if ( FT_QNEW_ARRAY( dataOffsetArray, data_count ) ) goto Exit; + if ( FT_FRAME_ENTER( data_count * 4 ) ) + goto Exit; + + bytes = stream->cursor; + for ( i = 0; i < data_count; i++ ) - { - if ( FT_READ_ULONG( dataOffsetArray[i] ) ) - goto Exit; - } + dataOffsetArray[i] = FT_NEXT_ULONG( bytes ); + + FT_FRAME_EXIT(); /* parse array of region records (region list) */ if ( FT_STREAM_SEEK( offset + region_offset ) ) @@ -564,13 +569,26 @@ goto Exit; itemStore->regionCount = region_count; - for ( i = 0; i < itemStore->regionCount; i++ ) + if ( FT_FRAME_ENTER( (FT_Long)region_count * axis_count * 6 ) ) + { + FT_TRACE2(( "tt_var_load_item_variation_store:" + " not enough data for variation regions\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + bytes = stream->cursor; + + for ( i = 0; i < region_count; i++ ) { GX_AxisCoords axisCoords; if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, axis_count ) ) + { + FT_FRAME_EXIT(); goto Exit; + } axisCoords = itemStore->varRegionList[i].axisList; @@ -579,10 +597,9 @@ FT_Int start, peak, end; - if ( FT_READ_SHORT( start ) || - FT_READ_SHORT( peak ) || - FT_READ_SHORT( end ) ) - goto Exit; + start = FT_NEXT_SHORT( bytes ); + peak = FT_NEXT_SHORT( bytes ); + end = FT_NEXT_SHORT( bytes ); /* immediately tag invalid ranges with special peak = 0 */ if ( ( start < 0 && end > 0 ) || start > peak || peak > end ) @@ -594,6 +611,8 @@ } } + FT_FRAME_EXIT(); + /* end of region list parse */ /* use dataOffsetArray now to parse varData items */ @@ -625,7 +644,7 @@ /* check some data consistency */ if ( word_delta_count > region_idx_count ) { - FT_TRACE2(( "bad short count %d or region count %d\n", + FT_TRACE2(( "bad short count %d or region count %u\n", word_delta_count, region_idx_count )); error = FT_THROW( Invalid_Table ); @@ -634,7 +653,7 @@ if ( region_idx_count > itemStore->regionCount ) { - FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n", + FT_TRACE2(( "inconsistent regionCount %u in varData[%u]\n", region_idx_count, i )); error = FT_THROW( Invalid_Table ); @@ -648,20 +667,32 @@ varData->wordDeltaCount = word_delta_count; varData->longWords = long_words; + if ( FT_FRAME_ENTER( region_idx_count * 2 ) ) + { + FT_TRACE2(( "tt_var_load_item_variation_store:" + " not enough data for region indices\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + bytes = stream->cursor; + for ( j = 0; j < varData->regionIdxCount; j++ ) { - if ( FT_READ_USHORT( varData->regionIndices[j] ) ) - goto Exit; + varData->regionIndices[j] = FT_NEXT_USHORT( bytes ); if ( varData->regionIndices[j] >= itemStore->regionCount ) { - FT_TRACE2(( "bad region index %d\n", + FT_TRACE2(( "bad region index %u\n", varData->regionIndices[j] )); + FT_FRAME_EXIT(); error = FT_THROW( Invalid_Table ); goto Exit; } } + FT_FRAME_EXIT(); + per_region_size = word_delta_count + region_idx_count; if ( long_words ) per_region_size *= 2; @@ -706,6 +737,7 @@ FT_UInt innerIndexMask; FT_ULong i; FT_UInt j; + FT_Byte* bytes; if ( FT_STREAM_SEEK( offset ) || @@ -757,6 +789,16 @@ if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) ) goto Exit; + if ( FT_FRAME_ENTER( map->mapCount * entrySize ) ) + { + FT_TRACE2(( "tt_var_load_delta_set_index_mapping:" + " invalid number of delta-set index mappings\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + bytes = stream->cursor; + for ( i = 0; i < map->mapCount; i++ ) { FT_UInt mapData = 0; @@ -769,9 +811,7 @@ FT_Byte data; - if ( FT_READ_BYTE( data ) ) - goto Exit; - + data = FT_NEXT_BYTE( bytes ); mapData = ( mapData << 8 ) | data; } @@ -789,7 +829,7 @@ if ( outerIndex >= itemStore->dataCount ) { - FT_TRACE2(( "outerIndex[%ld] == %d out of range\n", + FT_TRACE2(( "outerIndex[%lu] == %u out of range\n", i, outerIndex )); error = FT_THROW( Invalid_Table ); @@ -802,7 +842,7 @@ if ( innerIndex >= itemStore->varData[outerIndex].itemCount ) { - FT_TRACE2(( "innerIndex[%ld] == %d out of range\n", + FT_TRACE2(( "innerIndex[%lu] == %u out of range\n", i, innerIndex )); error = FT_THROW( Invalid_Table ); @@ -812,6 +852,8 @@ map->innerIndex[i] = innerIndex; } + FT_FRAME_EXIT(); + Exit: return error; } @@ -965,28 +1007,181 @@ } + static FT_Fixed + tt_calculate_scalar( GX_AxisCoords axis, + FT_UInt axisCount, + FT_Fixed* normalizedcoords ) + { + FT_Fixed scalar = 0x10000L; + FT_UInt j; + + + /* Inner loop steps through axes in this region. */ + for ( j = 0; j < axisCount; j++, axis++ ) + { + FT_Fixed ncv = normalizedcoords[j]; + + + /* Compute the scalar contribution of this axis, */ + /* with peak of 0 used for invalid axes. */ + if ( axis->peakCoord == ncv || + axis->peakCoord == 0 ) + continue; + + /* Ignore this region if coordinates are out of range. */ + else if ( ncv <= axis->startCoord || + ncv >= axis->endCoord ) + { + scalar = 0; + break; + } + + /* Cumulative product of all the axis scalars. */ + else if ( ncv < axis->peakCoord ) + scalar = FT_MulDiv( scalar, + ncv - axis->startCoord, + axis->peakCoord - axis->startCoord ); + else /* ncv > axis->peakCoord */ + scalar = FT_MulDiv( scalar, + axis->endCoord - ncv, + axis->endCoord - axis->peakCoord ); + + } /* per-axis loop */ + + return scalar; + } + + + static FT_Int64 + ft_mul_add_delta_scalar( FT_Int64 returnValue, + FT_Int32 delta, + FT_Int32 scalar ) + { + +#ifdef FT_INT64 + + return returnValue + (FT_Int64)delta * scalar; + +#else /* !FT_INT64 */ + + if ( (FT_UInt32)( delta + 0x8000 ) <= 0x20000 ) + { + /* Fast path: multiplication result fits into 32 bits. */ + + FT_Int32 lo = delta * scalar; + + + returnValue.lo += (FT_UInt32)lo; + + if ( returnValue.lo < (FT_UInt32)lo ) + returnValue.hi += ( lo < 0 ) ? 0 : 1; + + if ( lo < 0 ) + returnValue.hi -= 1; + + return returnValue; + } + else + { + /* Slow path: full 32x32 -> 64-bit signed multiplication. */ + + FT_Int64 product; + + /* Get absolute values. */ + FT_UInt32 a = ( delta < 0 ) ? -delta : delta; + FT_UInt32 b = ( scalar < 0 ) ? -scalar : scalar; + + /* Prepare unsigned multiplication. */ + FT_UInt32 a_lo = a & 0xFFFF; + FT_UInt32 a_hi = a >> 16; + + FT_UInt32 b_lo = b & 0xFFFF; + FT_UInt32 b_hi = b >> 16; + + /* Partial products. */ + FT_UInt32 p0 = a_lo * b_lo; + FT_UInt32 p1 = a_lo * b_hi; + FT_UInt32 p2 = a_hi * b_lo; + FT_UInt32 p3 = a_hi * b_hi; + + /* Combine: result = p3 << 32 + (p1 + p2) << 16 + p0 */ + FT_UInt32 mid = p1 + p2; + FT_UInt32 mid_carry = ( mid < p1 ); + + FT_UInt32 carry; + + + product.lo = ( mid << 16 ) + ( p0 & 0xFFFF ); + carry = ( product.lo < ( p0 & 0xFFFF ) ) ? 1 : 0; + product.hi = p3 + ( mid >> 16 ) + mid_carry + carry; + + /* If result should be negative, negate. */ + if ( ( delta < 0 ) ^ ( scalar < 0 ) ) + { + product.lo = ~product.lo + 1; + product.hi = ~product.hi + ( product.lo == 0 ? 1 : 0 ); + } + + /* Add to `returnValue`. */ + returnValue.lo += product.lo; + if ( returnValue.lo < product.lo ) + returnValue.hi++; + returnValue.hi += product.hi; + + return returnValue; + } + +#endif /* !FT_INT64 */ + + } + + + static FT_ItemVarDelta + ft_round_and_shift16( FT_Int64 returnValue ) + { + +#ifdef FT_INT64 + + return (FT_ItemVarDelta)( returnValue + 0x8000L ) >> 16; + +#else /* !FT_INT64 */ + + FT_UInt hi = returnValue.hi; + FT_UInt lo = returnValue.lo; + + FT_UInt delta; + + + /* Add 0x8000 to round. */ + lo += 0x8000; + if ( lo < 0x8000 ) /* overflow occurred */ + hi += 1; + + /* Shift right by 16 bits. */ + delta = ( hi << 16 ) | ( lo >> 16 ); + + return (FT_ItemVarDelta)delta; + +#endif /* !FT_INT64 */ + + } + + FT_LOCAL_DEF( FT_ItemVarDelta ) tt_var_get_item_delta( FT_Face face, /* TT_Face */ GX_ItemVarStore itemStore, FT_UInt outerIndex, FT_UInt innerIndex ) { - TT_Face ttface = (TT_Face)face; - FT_Stream stream = FT_FACE_STREAM( face ); - FT_Memory memory = stream->memory; - FT_Error error = FT_Err_Ok; + TT_Face ttface = (TT_Face)face; - GX_ItemVarData varData; - FT_ItemVarDelta* deltaSet = NULL; - FT_ItemVarDelta deltaSetStack[16]; + GX_ItemVarData varData; - FT_Fixed* scalars = NULL; - FT_Fixed scalarsStack[16]; - - FT_UInt master, j; - FT_ItemVarDelta returnValue = 0; - FT_UInt per_region_size; - FT_Byte* bytes; + FT_UInt master; + FT_Int64 returnValue = FT_INT64_ZERO; + FT_UInt shift_base = 1; + FT_UInt per_region_size; + FT_Byte* bytes; if ( !ttface->blend || !ttface->blend->normalizedcoords ) @@ -1011,113 +1206,63 @@ if ( varData->regionIdxCount == 0 ) return 0; /* Avoid "applying zero offset to null pointer". */ - if ( varData->regionIdxCount < 16 ) - { - deltaSet = deltaSetStack; - scalars = scalarsStack; - } - else - { - if ( FT_QNEW_ARRAY( deltaSet, varData->regionIdxCount ) ) - goto Exit; - if ( FT_QNEW_ARRAY( scalars, varData->regionIdxCount ) ) - goto Exit; - } - /* Parse delta set. */ /* */ /* Deltas are (word_delta_count + region_idx_count) bytes each */ /* if `longWords` isn't set, and twice as much otherwise. */ per_region_size = varData->wordDeltaCount + varData->regionIdxCount; if ( varData->longWords ) + { + shift_base = 2; per_region_size *= 2; + } bytes = varData->deltaSet + per_region_size * innerIndex; - if ( varData->longWords ) - { - for ( master = 0; master < varData->wordDeltaCount; master++ ) - deltaSet[master] = FT_NEXT_LONG( bytes ); - for ( ; master < varData->regionIdxCount; master++ ) - deltaSet[master] = FT_NEXT_SHORT( bytes ); - } - else - { - for ( master = 0; master < varData->wordDeltaCount; master++ ) - deltaSet[master] = FT_NEXT_SHORT( bytes ); - for ( ; master < varData->regionIdxCount; master++ ) - deltaSet[master] = FT_NEXT_CHAR( bytes ); - } - /* outer loop steps through master designs to be blended */ for ( master = 0; master < varData->regionIdxCount; master++ ) { - FT_Fixed scalar = 0x10000L; - FT_UInt regionIndex = varData->regionIndices[master]; + FT_UInt regionIndex = varData->regionIndices[master]; GX_AxisCoords axis = itemStore->varRegionList[regionIndex].axisList; + FT_Fixed scalar = tt_calculate_scalar( + axis, + itemStore->axisCount, + ttface->blend->normalizedcoords ); - /* inner loop steps through axes in this region */ - for ( j = 0; j < itemStore->axisCount; j++, axis++ ) + + if ( scalar ) { - FT_Fixed ncv = ttface->blend->normalizedcoords[j]; + FT_Int delta; - /* compute the scalar contribution of this axis */ - /* with peak of 0 used for invalid axes */ - if ( axis->peakCoord == ncv || - axis->peakCoord == 0 ) - continue; - - /* ignore this region if coords are out of range */ - else if ( ncv <= axis->startCoord || - ncv >= axis->endCoord ) + if ( varData->longWords ) { - scalar = 0; - break; + if ( master < varData->wordDeltaCount ) + delta = FT_NEXT_LONG( bytes ); + else + delta = FT_NEXT_SHORT( bytes ); + } + else + { + if ( master < varData->wordDeltaCount ) + delta = FT_NEXT_SHORT( bytes ); + else + delta = FT_NEXT_CHAR( bytes ); } - /* cumulative product of all the axis scalars */ - else if ( ncv < axis->peakCoord ) - scalar = FT_MulDiv( scalar, - ncv - axis->startCoord, - axis->peakCoord - axis->startCoord ); - else /* ncv > axis->peakCoord */ - scalar = FT_MulDiv( scalar, - axis->endCoord - ncv, - axis->endCoord - axis->peakCoord ); - - } /* per-axis loop */ - - scalars[master] = scalar; + returnValue = ft_mul_add_delta_scalar( returnValue, delta, scalar ); + } + else + { + /* Branch-free, yay. */ + bytes += shift_base << ( master < varData->wordDeltaCount ); + } } /* per-region loop */ - - /* Compute the scaled delta for this region. - * - * From: https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#item-variation-store-header-and-item-variation-data-subtables: - * - * `Fixed` is a 32-bit (16.16) type and, in the general case, requires - * 32-bit deltas. As described above, the `DeltaSet` record can - * accommodate deltas that are, logically, either 16-bit or 32-bit. - * When scaled deltas are applied to `Fixed` values, the `Fixed` value - * is treated like a 32-bit integer. - * - * `FT_MulAddFix` internally uses 64-bit precision; it thus can handle - * deltas ranging from small 8-bit to large 32-bit values that are - * applied to 16.16 `FT_Fixed` / OpenType `Fixed` values. - */ - returnValue = FT_MulAddFix( scalars, deltaSet, varData->regionIdxCount ); - - Exit: - if ( scalars != scalarsStack ) - FT_FREE( scalars ); - if ( deltaSet != deltaSetStack ) - FT_FREE( deltaSet ); - - return returnValue; + return ft_round_and_shift16( returnValue ); } @@ -1643,6 +1788,7 @@ GX_Blend blend = face->blend; FT_Error error; FT_UInt i, j; + FT_Byte* bytes; FT_ULong table_len; FT_ULong gvar_start; FT_ULong offsetToData; @@ -1734,6 +1880,8 @@ if ( FT_FRAME_ENTER( offsets_len ) ) goto Exit; + bytes = stream->cursor; + /* offsets (one more offset than glyphs, to mark size of last) */ if ( FT_QNEW_ARRAY( blend->glyphoffsets, gvar_head.glyphCount + 1 ) ) goto Fail2; @@ -1744,16 +1892,24 @@ FT_ULong max_offset = 0; + if ( stream->limit - stream->cursor < gvar_head.glyphCount * 4 ) + { + FT_TRACE2(( "ft_var_load_gvar:" + " glyph variation data offset not enough\n" )); + error = FT_THROW( Invalid_Table ); + goto Fail; + } + for ( i = 0; i <= gvar_head.glyphCount; i++ ) { - blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG(); + blend->glyphoffsets[i] = offsetToData + FT_NEXT_ULONG( bytes ); if ( max_offset <= blend->glyphoffsets[i] ) max_offset = blend->glyphoffsets[i]; else { FT_TRACE2(( "ft_var_load_gvar:" - " glyph variation data offset %d not monotonic\n", + " glyph variation data offset %u not monotonic\n", i )); blend->glyphoffsets[i] = max_offset; } @@ -1762,7 +1918,7 @@ if ( limit < blend->glyphoffsets[i] ) { FT_TRACE2(( "ft_var_load_gvar:" - " glyph variation data offset %d out of range\n", + " glyph variation data offset %u out of range\n", i )); blend->glyphoffsets[i] = limit; } @@ -1774,16 +1930,24 @@ FT_ULong max_offset = 0; + if ( stream->limit - stream->cursor < gvar_head.glyphCount * 2 ) + { + FT_TRACE2(( "ft_var_load_gvar:" + " glyph variation data offset not enough\n" )); + error = FT_THROW( Invalid_Table ); + goto Fail; + } + for ( i = 0; i <= gvar_head.glyphCount; i++ ) { - blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2; + blend->glyphoffsets[i] = offsetToData + FT_NEXT_USHORT( bytes ) * 2; if ( max_offset <= blend->glyphoffsets[i] ) max_offset = blend->glyphoffsets[i]; else { FT_TRACE2(( "ft_var_load_gvar:" - " glyph variation data offset %d not monotonic\n", + " glyph variation data offset %u not monotonic\n", i )); blend->glyphoffsets[i] = max_offset; } @@ -1792,7 +1956,7 @@ if ( limit < blend->glyphoffsets[i] ) { FT_TRACE2(( "ft_var_load_gvar:" - " glyph variation data offset %d out of range\n", + " glyph variation data offset %u out of range\n", i )); blend->glyphoffsets[i] = limit; } @@ -1814,6 +1978,8 @@ goto Fail; } + bytes = stream->cursor; + if ( FT_QNEW_ARRAY( blend->tuplecoords, gvar_head.axisCount * gvar_head.globalCoordCount ) ) goto Fail2; @@ -1824,13 +1990,17 @@ for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ ) { blend->tuplecoords[i * gvar_head.axisCount + j] = - FT_fdot14ToFixed( FT_GET_SHORT() ); + FT_fdot14ToFixed( FT_NEXT_SHORT( bytes ) ); FT_TRACE5(( "%.5f ", (double)blend->tuplecoords[i * gvar_head.axisCount + j] / 65536 )); } FT_TRACE5(( "]\n" )); } + if ( FT_NEW_ARRAY( blend->tuplescalars, + gvar_head.globalCoordCount ) ) + goto Fail2; + blend->tuplecount = gvar_head.globalCoordCount; FT_TRACE5(( "\n" )); @@ -1896,15 +2066,25 @@ for ( i = 0; i < blend->num_axis; i++ ) { - FT_Fixed ncv = blend->normalizedcoords[i]; + FT_Fixed ncv; - FT_TRACE6(( " axis %d coordinate %.5f:\n", i, (double)ncv / 65536 )); + if ( tuple_coords[i] == 0 ) + { + FT_TRACE6(( " tuple coordinate is zero, ignore\n" )); + continue; + } - /* It's not clear why (for intermediate tuples) we don't need */ - /* to check against start/end -- the documentation says we don't. */ - /* Similarly, it's unclear why we don't need to scale along the */ - /* axis. */ + ncv = blend->normalizedcoords[i]; + + FT_TRACE6(( " axis %u coordinate %.5f:\n", i, (double)ncv / 65536 )); + + if ( ncv == 0 ) + { + FT_TRACE6(( " axis coordinate is zero, stop\n" )); + apply = 0; + break; + } if ( tuple_coords[i] == ncv ) { @@ -1914,12 +2094,6 @@ continue; } - if ( tuple_coords[i] == 0 ) - { - FT_TRACE6(( " tuple coordinate is zero, ignore\n" )); - continue; - } - if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) { /* not an intermediate tuple */ @@ -2001,7 +2175,7 @@ if ( num_coords > mmvar->num_axis ) { FT_TRACE2(( "ft_var_to_normalized:" - " only using first %d of %d coordinates\n", + " only using first %u of %u coordinates\n", mmvar->num_axis, num_coords )); num_coords = mmvar->num_axis; } @@ -2016,7 +2190,7 @@ FT_Fixed coord = coords[i]; - FT_TRACE5(( " %d: %.5f\n", i, (double)coord / 65536 )); + FT_TRACE5(( " %u: %.5f\n", i, (double)coord / 65536 )); if ( coord > a->maximum || coord < a->minimum ) { FT_TRACE1(( "ft_var_to_normalized: design coordinate %.5f\n", @@ -2156,7 +2330,7 @@ if ( num_coords > blend->num_axis ) { FT_TRACE2(( "ft_var_to_design:" - " only using first %d of %d coordinates\n", + " only using first %u of %u coordinates\n", blend->num_axis, num_coords )); nc = blend->num_axis; } @@ -2516,7 +2690,7 @@ " minimum default maximum flags\n" )); /* " XXXX.XXXXX XXXX.XXXXX XXXX.XXXXX 0xXXXX" */ - FT_TRACE5(( " %3d `%s'" + FT_TRACE5(( " %3u `%s'" " %10.5f %10.5f %10.5f 0x%04X%s\n", i, a->name, @@ -2608,7 +2782,7 @@ (void)FT_STREAM_SEEK( pos ); - FT_TRACE5(( " named instance %d (%s%s%s, %s%s%s)\n", + FT_TRACE5(( " named instance %u (%s%s%s, %s%s%s)\n", i, strname ? "name: `" : "", strname ? strname : "unnamed", @@ -2636,7 +2810,7 @@ FT_UInt strid = ~0U; - /* The default instance is missing in array the */ + /* The default instance is missing in the array */ /* of named instances; try to synthesize an entry. */ /* If this fails, `default_named_instance` remains */ /* at value zero, which doesn't do any harm. */ @@ -2766,10 +2940,18 @@ } manageCvt; - face->doblend = FALSE; - if ( !face->blend ) { + face->doblend = FALSE; + for ( i = 0; i < num_coords; i++ ) + if ( coords[i] ) + { + face->doblend = TRUE; + break; + } + if ( !face->doblend ) + goto Exit; + if ( FT_SET_ERROR( TT_Get_MM_Var( FT_FACE( face ), NULL ) ) ) goto Exit; } @@ -2780,7 +2962,7 @@ if ( num_coords > mmvar->num_axis ) { FT_TRACE2(( "TT_Set_MM_Blend:" - " only using first %d of %d coordinates\n", + " only using first %u of %u coordinates\n", mmvar->num_axis, num_coords )); num_coords = mmvar->num_axis; } @@ -2882,11 +3064,7 @@ /* return value -1 indicates `no change' */ if ( !have_diff ) - { - face->doblend = TRUE; - return -1; - } for ( ; i < mmvar->num_axis; i++ ) { @@ -2915,7 +3093,15 @@ blend->normalizedcoords, blend->coords ); - face->doblend = TRUE; + face->doblend = FALSE; + for ( i = 0; i < blend->num_axis; i++ ) + { + if ( blend->normalizedcoords[i] ) + { + face->doblend = TRUE; + break; + } + } if ( face->cvt ) { @@ -2941,6 +3127,9 @@ } } + for ( i = 0 ; i < blend->tuplecount ; i++ ) + blend->tuplescalars[i] = (FT_Fixed)-0x20000L; + Exit: return error; } @@ -2980,7 +3169,24 @@ FT_UInt num_coords, FT_Fixed* coords ) { - return tt_set_mm_blend( (TT_Face)face, num_coords, coords, 1 ); + FT_Error error; + + + error = tt_set_mm_blend( (TT_Face)face, num_coords, coords, 1 ); + if ( error == FT_Err_Ok ) + { + FT_UInt i; + + + for ( i = 0; i < num_coords; i++ ) + if ( coords[i] ) + { + error = -2; /* -2 means is_variable. */ + break; + } + } + + return error; } @@ -3043,7 +3249,7 @@ if ( num_coords > blend->num_axis ) { FT_TRACE2(( "TT_Get_MM_Blend:" - " only using first %d of %d coordinates\n", + " only using first %u of %u coordinates\n", blend->num_axis, num_coords )); nc = blend->num_axis; } @@ -3125,7 +3331,7 @@ if ( num_coords > mmvar->num_axis ) { FT_TRACE2(( "TT_Set_Var_Design:" - " only using first %d of %d coordinates\n", + " only using first %u of %u coordinates\n", mmvar->num_axis, num_coords )); num_coords = mmvar->num_axis; } @@ -3201,6 +3407,15 @@ if ( error ) goto Exit; + for ( i = 0; i < num_coords; i++ ) + { + if ( normalized[i] ) + { + error = -2; /* -2 means is_variable. */ + break; + } + } + Exit: FT_FREE( normalized ); return error; @@ -3237,10 +3452,12 @@ FT_UInt num_coords, FT_Fixed* coords ) { - TT_Face ttface = (TT_Face)face; - FT_Error error = FT_Err_Ok; - GX_Blend blend; - FT_UInt i, nc; + TT_Face ttface = (TT_Face)face; + FT_Error error = FT_Err_Ok; + GX_Blend blend; + FT_MM_Var* mmvar; + FT_Var_Axis* a; + FT_UInt i, nc; if ( !ttface->blend ) @@ -3263,24 +3480,26 @@ if ( num_coords > blend->num_axis ) { FT_TRACE2(( "TT_Get_Var_Design:" - " only using first %d of %d coordinates\n", + " only using first %u of %u coordinates\n", blend->num_axis, num_coords )); nc = blend->num_axis; } + mmvar = blend->mmvar; + a = mmvar->axis; if ( ttface->doblend ) { - for ( i = 0; i < nc; i++ ) + for ( i = 0; i < nc; i++, a++ ) coords[i] = blend->coords[i]; } else { - for ( i = 0; i < nc; i++ ) - coords[i] = 0; + for ( i = 0; i < nc; i++, a++ ) + coords[i] = a->def; } - for ( ; i < num_coords; i++ ) - coords[i] = 0; + for ( ; i < num_coords; i++, a++ ) + coords[i] = a->def; return FT_Err_Ok; } @@ -3373,6 +3592,9 @@ error = TT_Set_Var_Design( face, 0, NULL ); } + if ( error == -1 || error == -2 ) + error = FT_Err_Ok; + Exit: return error; } @@ -3591,7 +3813,7 @@ FT_Stream_SeekSet( stream, here ); } - FT_TRACE5(( "cvar: there %s %d tuple%s:\n", + FT_TRACE5(( "cvar: there %s %u tuple%s:\n", ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", tupleCount & GX_TC_TUPLE_COUNT_MASK, ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); @@ -3610,7 +3832,7 @@ FT_Fixed apply; - FT_TRACE6(( " tuple %d:\n", i )); + FT_TRACE6(( " tuple %u:\n", i )); tupleDataSize = FT_GET_USHORT(); tupleIndex = FT_GET_USHORT(); @@ -3676,7 +3898,7 @@ if ( !points || !deltas ) ; /* failure, ignore it */ - else if ( localpoints == ALL_POINTS ) + else if ( points == ALL_POINTS ) { #ifdef FT_DEBUG_LEVEL_TRACE int count = 0; @@ -3697,7 +3919,7 @@ #ifdef FT_DEBUG_LEVEL_TRACE if ( old_cvt_delta != cvt_deltas[j] ) { - FT_TRACE7(( " %d: %f -> %f\n", + FT_TRACE7(( " %u: %f -> %f\n", j, (double)( FT_fdot6ToFixed( face->cvt[j] ) + old_cvt_delta ) / 65536, @@ -4027,7 +4249,7 @@ FT_Outline* outline, FT_Vector* unrounded ) { - FT_Error error; + FT_Error error = FT_Err_Ok; TT_Face face = loader->face; FT_Stream stream = face->root.stream; FT_Memory memory = stream->memory; @@ -4047,6 +4269,15 @@ FT_ULong here; FT_UInt i, j; + FT_UInt peak_coords_size; + FT_UInt point_deltas_x_size; + FT_UInt points_org_size; + FT_UInt points_out_size; + FT_UInt has_delta_size; + FT_UInt pool_size; + FT_Byte* pool = NULL; + FT_Byte* p; + FT_Fixed* peak_coords = NULL; FT_Fixed* tuple_coords; FT_Fixed* im_start_coords; @@ -4067,21 +4298,24 @@ FT_Fixed* point_deltas_y = NULL; - if ( !face->doblend || !blend ) - return FT_THROW( Invalid_Argument ); - for ( i = 0; i < n_points; i++ ) { unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x ); unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y ); } + if ( !face->doblend ) + goto Exit; + + if ( !blend ) + return FT_THROW( Invalid_Argument ); + if ( glyph_index >= blend->gv_glyphcnt || blend->glyphoffsets[glyph_index] == blend->glyphoffsets[glyph_index + 1] ) { FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" - " no variation data for glyph %d\n", glyph_index )); + " no variation data for glyph %u\n", glyph_index )); return FT_Err_Ok; } @@ -4125,18 +4359,41 @@ FT_Stream_SeekSet( stream, here ); } - FT_TRACE5(( "gvar: there %s %d tuple%s:\n", + FT_TRACE5(( "gvar: there %s %u tuple%s:\n", ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", tupleCount & GX_TC_TUPLE_COUNT_MASK, ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); - if ( FT_QNEW_ARRAY( peak_coords, 3 * blend->num_axis ) || - FT_NEW_ARRAY( point_deltas_x, 2 * n_points ) || - FT_QNEW_ARRAY( points_org, n_points ) || - FT_QNEW_ARRAY( points_out, n_points ) || - FT_QNEW_ARRAY( has_delta, n_points ) ) + peak_coords_size = ALIGN_SIZE( 3 * blend->num_axis * + sizeof ( *peak_coords ) ); + point_deltas_x_size = ALIGN_SIZE( 2 * n_points * + sizeof ( *point_deltas_x ) ); + points_org_size = ALIGN_SIZE( n_points * sizeof ( *points_org ) ); + points_out_size = ALIGN_SIZE( n_points * sizeof ( *points_out ) ); + has_delta_size = ALIGN_SIZE( n_points * sizeof ( *has_delta ) ); + + pool_size = peak_coords_size + + point_deltas_x_size + + points_org_size + + points_out_size + + has_delta_size; + + if ( FT_ALLOC( pool, pool_size ) ) goto Exit; + p = pool; + peak_coords = (FT_Fixed*)p; + p += peak_coords_size; + point_deltas_x = (FT_Fixed*)p; + p += point_deltas_x_size; + points_org = (FT_Vector*)p; + p += points_org_size; + points_out = (FT_Vector*)p; + p += points_out_size; + has_delta = (FT_Bool*)p; + + FT_ARRAY_ZERO( point_deltas_x, 2 * n_points ); + im_start_coords = peak_coords + blend->num_axis; im_end_coords = im_start_coords + blend->num_axis; point_deltas_y = point_deltas_x + n_points; @@ -4147,27 +4404,70 @@ points_org[j].y = FT_intToFixed( outline->points[j].y ); } - for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) + p = stream->cursor; + + tupleCount &= GX_TC_TUPLE_COUNT_MASK; + for ( i = 0; i < tupleCount; i++ ) { - FT_UInt tupleDataSize; - FT_UInt tupleIndex; - FT_Fixed apply; + FT_UInt tupleDataSize; + FT_UInt tupleIndex; + FT_Fixed apply; + FT_Fixed* tupleScalars; - FT_TRACE6(( " tuple %d:\n", i )); + FT_TRACE6(( " tuple %u:\n", i )); - tupleDataSize = FT_GET_USHORT(); - tupleIndex = FT_GET_USHORT(); + tupleScalars = blend->tuplescalars; + + /* Enter frame for four bytes. */ + if ( 4 > stream->limit - p ) + { + FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" + " invalid glyph variation array header\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + tupleDataSize = FT_NEXT_USHORT( p ); + tupleIndex = FT_NEXT_USHORT( p ); + + if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) + tupleScalars = NULL; if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) { + if ( 2 * blend->num_axis > (FT_UInt)( stream->limit - p ) ) + { + FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" + " invalid glyph variation array header\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + for ( j = 0; j < blend->num_axis; j++ ) - peak_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); + peak_coords[j] = FT_fdot14ToFixed( FT_NEXT_SHORT( p ) ); + tuple_coords = peak_coords; + tupleScalars = NULL; } else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) < blend->tuplecount ) + { + FT_Fixed scalar = + tupleScalars + ? tupleScalars[tupleIndex & GX_TI_TUPLE_INDEX_MASK] + : (FT_Fixed)-0x20000; + + + if ( scalar != (FT_Fixed)-0x20000 ) + { + apply = scalar; + goto apply_found; + } + tuple_coords = blend->tuplecoords + - ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis; + ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * + blend->num_axis; + } else { FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" @@ -4179,10 +4479,18 @@ if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) { + if ( 4 * blend->num_axis > (FT_UInt)( stream->limit - p ) ) + { + FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" + " invalid glyph variation array header\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + for ( j = 0; j < blend->num_axis; j++ ) - im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); + im_start_coords[j] = FT_fdot14ToFixed( FT_NEXT_SHORT( p ) ); for ( j = 0; j < blend->num_axis; j++ ) - im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); + im_end_coords[j] = FT_fdot14ToFixed( FT_NEXT_SHORT( p ) ); } apply = ft_var_apply_tuple( blend, @@ -4191,6 +4499,11 @@ im_start_coords, im_end_coords ); + if ( tupleScalars ) + tupleScalars[tupleIndex & GX_TI_TUPLE_INDEX_MASK] = apply; + + apply_found: + if ( apply == 0 ) /* tuple isn't active for our blend */ { offsetToData += tupleDataSize; @@ -4247,7 +4560,7 @@ #ifdef FT_DEBUG_LEVEL_TRACE if ( point_delta_x || point_delta_y ) { - FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n", + FT_TRACE7(( " %u: (%f, %f) -> (%f, %f)\n", j, (double)( FT_intToFixed( outline->points[j].x ) + old_point_delta_x ) / 65536, @@ -4321,7 +4634,7 @@ #ifdef FT_DEBUG_LEVEL_TRACE if ( point_delta_x || point_delta_y ) { - FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n", + FT_TRACE7(( " %u: (%f, %f) -> (%f, %f)\n", j, (double)( FT_intToFixed( outline->points[j].x ) + old_point_delta_x ) / 65536, @@ -4402,11 +4715,7 @@ Exit: if ( sharedpoints != ALL_POINTS ) FT_FREE( sharedpoints ); - FT_FREE( points_org ); - FT_FREE( points_out ); - FT_FREE( has_delta ); - FT_FREE( peak_coords ); - FT_FREE( point_deltas_x ); + FT_FREE( pool ); FExit: FT_FRAME_EXIT(); @@ -4577,6 +4886,7 @@ FT_FREE( blend->mvar_table ); } + FT_FREE( blend->tuplescalars ); FT_FREE( blend->tuplecoords ); FT_FREE( blend->glyphoffsets ); FT_FREE( blend ); diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h index 9326011e3a2..568c8027bbf 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttgxvar.h @@ -4,7 +4,7 @@ * * TrueType GX Font Variation loader (specification) * - * Copyright (C) 2004-2024 by + * Copyright (C) 2004-2025 by * David Turner, Robert Wilhelm, Werner Lemberg and George Williams. * * This file is part of the FreeType project, and may only be used, @@ -255,6 +255,10 @@ FT_BEGIN_HEADER * A two-dimensional array that holds the shared tuple coordinates * in the `gvar' table. * + * tuplescalars :: + * A one-dimensional array that holds the shared tuple + * scalars in the `gvar' table for current face coordinates. + * * gv_glyphcnt :: * The number of glyphs handled in the `gvar' table. * @@ -293,6 +297,7 @@ FT_BEGIN_HEADER FT_UInt tuplecount; FT_Fixed* tuplecoords; /* tuplecoords[tuplecount][num_axis] */ + FT_Fixed* tuplescalars; /* tuplescalars[tuplecount] */ FT_UInt gv_glyphcnt; FT_ULong* glyphoffsets; /* glyphoffsets[gv_glyphcnt + 1] */ diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c index 951891dbf51..7b26c9a9df2 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.c @@ -4,7 +4,7 @@ * * TrueType bytecode interpreter (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -27,6 +27,8 @@ #include #include +#ifdef TT_USE_BYTECODE_INTERPRETER + #include "ttinterp.h" #include "tterrors.h" #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT @@ -34,9 +36,6 @@ #endif -#ifdef TT_USE_BYTECODE_INTERPRETER - - /************************************************************************** * * The macro FT_COMPONENT is used in trace mode. It is an implicit @@ -89,6 +88,32 @@ #define FAILURE 1 + /* The default value for `scan_control' is documented as FALSE in the */ + /* TrueType specification. This is confusing since it implies a */ + /* Boolean value. However, this is not the case, thus both the */ + /* default values of our `scan_type' and `scan_control' fields (which */ + /* the documentation's `scan_control' variable is split into) are */ + /* zero. */ + /* */ + /* The rounding compensation should logically belong here but poorly */ + /* described in the OpenType specs. It was probably important in the */ + /* days of dot matrix printers. The values are referenced by color */ + /* as Gray, Black, and White in order. The Apple specification says */ + /* that the Gray compensation is always zero. The fourth value is */ + /* not described at all, but Greg says that it is the same as Gray. */ + /* FreeType sets all compensation values to zero. */ + + const TT_GraphicsState tt_default_graphics_state = + { + 0, 0, 0, 1, 1, 1, + { 0x4000, 0 }, { 0x4000, 0 }, { 0x4000, 0 }, + 1, 1, { 0, 0, 0, 0 }, + + 64, 68, 0, 0, 9, 3, + TRUE, 0, FALSE, 0 + }; + + /************************************************************************** * * CODERANGE FUNCTIONS @@ -96,53 +121,6 @@ */ - /************************************************************************** - * - * @Function: - * TT_Goto_CodeRange - * - * @Description: - * Switches to a new code range (updates the code related elements in - * `exec', and `IP'). - * - * @Input: - * range :: - * The new execution code range. - * - * IP :: - * The new IP in the new code range. - * - * @InOut: - * exec :: - * The target execution context. - */ - FT_LOCAL_DEF( void ) - TT_Goto_CodeRange( TT_ExecContext exec, - FT_Int range, - FT_Long IP ) - { - TT_CodeRange* coderange; - - - FT_ASSERT( range >= 1 && range <= 3 ); - - coderange = &exec->codeRangeTable[range - 1]; - - FT_ASSERT( coderange->base ); - - /* NOTE: Because the last instruction of a program may be a CALL */ - /* which will return to the first byte *after* the code */ - /* range, we test for IP <= Size instead of IP < Size. */ - /* */ - FT_ASSERT( IP <= coderange->size ); - - exec->code = coderange->base; - exec->codeSize = coderange->size; - exec->IP = IP; - exec->curRange = range; - } - - /************************************************************************** * * @Function: @@ -168,13 +146,19 @@ FT_LOCAL_DEF( void ) TT_Set_CodeRange( TT_ExecContext exec, FT_Int range, - void* base, + FT_Byte* base, FT_Long length ) { FT_ASSERT( range >= 1 && range <= 3 ); - exec->codeRangeTable[range - 1].base = (FT_Byte*)base; + exec->codeRangeTable[range - 1].base = base; exec->codeRangeTable[range - 1].size = length; + + exec->code = base; + exec->codeSize = length; + exec->IP = 0; + exec->curRange = range; + exec->iniRange = range; } @@ -224,9 +208,6 @@ * exec :: * A handle to the target execution context. * - * memory :: - * A handle to the parent memory object. - * * @Note: * Only the glyph loader and debugger should call this function. */ @@ -240,10 +221,6 @@ exec->maxPoints = 0; exec->maxContours = 0; - /* free stack */ - FT_FREE( exec->stack ); - exec->stackSize = 0; - /* free glyf cvt working area */ FT_FREE( exec->glyfCvt ); exec->glyfCvtSize = 0; @@ -300,72 +277,26 @@ TT_Face face, TT_Size size ) { - FT_Int i; - TT_MaxProfile* maxp; - FT_Error error; - FT_Memory memory = exec->memory; + FT_Memory memory = exec->memory; exec->face = face; - maxp = &face->max_profile; exec->size = size; - if ( size ) - { - exec->numFDefs = size->num_function_defs; - exec->maxFDefs = size->max_function_defs; - exec->numIDefs = size->num_instruction_defs; - exec->maxIDefs = size->max_instruction_defs; - exec->FDefs = size->function_defs; - exec->IDefs = size->instruction_defs; - exec->pointSize = size->point_size; - exec->tt_metrics = size->ttmetrics; - exec->metrics = *size->metrics; - - exec->maxFunc = size->max_func; - exec->maxIns = size->max_ins; - - for ( i = 0; i < TT_MAX_CODE_RANGES; i++ ) - exec->codeRangeTable[i] = size->codeRangeTable[i]; - - /* set graphics state */ - exec->GS = size->GS; - - exec->cvtSize = size->cvt_size; - exec->cvt = size->cvt; - - exec->storeSize = size->storage_size; - exec->storage = size->storage; - - exec->twilight = size->twilight; - - /* In case of multi-threading it can happen that the old size object */ - /* no longer exists, thus we must clear all glyph zone references. */ - FT_ZERO( &exec->zp0 ); - exec->zp1 = exec->zp0; - exec->zp2 = exec->zp0; - } - - /* XXX: We reserve a little more elements on the stack to deal safely */ - /* with broken fonts like arialbs, courbs, timesbs, etc. */ - if ( FT_QRENEW_ARRAY( exec->stack, - exec->stackSize, - maxp->maxStackElements + 32 ) ) - return error; - exec->stackSize = maxp->maxStackElements + 32; + /* CVT and storage are not persistent in FreeType */ + /* reset them after they might have been modifief */ + exec->storage = exec->stack + exec->stackSize; + exec->cvt = exec->storage + exec->storeSize; /* free previous glyph code range */ FT_FREE( exec->glyphIns ); exec->glyphSize = 0; - exec->pts.n_points = 0; - exec->pts.n_contours = 0; + exec->pointSize = size->point_size; + exec->tt_metrics = size->ttmetrics; + exec->metrics = *size->metrics; - exec->zp1 = exec->pts; - exec->zp2 = exec->pts; - exec->zp0 = exec->pts; - - exec->instruction_trap = FALSE; + exec->twilight = size->twilight; return FT_Err_Ok; } @@ -394,89 +325,22 @@ TT_Save_Context( TT_ExecContext exec, TT_Size size ) { - FT_Int i; + /* UNDOCUMENTED! */ + /* Only these GS values can be modified by the CVT program. */ - - /* XXX: Will probably disappear soon with all the code range */ - /* management, which is now rather obsolete. */ - /* */ - size->num_function_defs = exec->numFDefs; - size->num_instruction_defs = exec->numIDefs; - - size->max_func = exec->maxFunc; - size->max_ins = exec->maxIns; - - for ( i = 0; i < TT_MAX_CODE_RANGES; i++ ) - size->codeRangeTable[i] = exec->codeRangeTable[i]; + size->GS.minimum_distance = exec->GS.minimum_distance; + size->GS.control_value_cutin = exec->GS.control_value_cutin; + size->GS.single_width_cutin = exec->GS.single_width_cutin; + size->GS.single_width_value = exec->GS.single_width_value; + size->GS.delta_base = exec->GS.delta_base; + size->GS.delta_shift = exec->GS.delta_shift; + size->GS.auto_flip = exec->GS.auto_flip; + size->GS.instruct_control = exec->GS.instruct_control; + size->GS.scan_control = exec->GS.scan_control; + size->GS.scan_type = exec->GS.scan_type; } - /************************************************************************** - * - * @Function: - * TT_Run_Context - * - * @Description: - * Executes one or more instructions in the execution context. - * - * @Input: - * exec :: - * A handle to the target execution context. - * - * @Return: - * TrueType error code. 0 means success. - */ - FT_LOCAL_DEF( FT_Error ) - TT_Run_Context( TT_ExecContext exec ) - { - TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 ); - - exec->zp0 = exec->pts; - exec->zp1 = exec->pts; - exec->zp2 = exec->pts; - - exec->GS.gep0 = 1; - exec->GS.gep1 = 1; - exec->GS.gep2 = 1; - - exec->GS.projVector.x = 0x4000; - exec->GS.projVector.y = 0x0000; - - exec->GS.freeVector = exec->GS.projVector; - exec->GS.dualVector = exec->GS.projVector; - - exec->GS.round_state = 1; - exec->GS.loop = 1; - - /* some glyphs leave something on the stack. so we clean it */ - /* before a new execution. */ - exec->top = 0; - exec->callTop = 0; - - return exec->face->interpreter( exec ); - } - - - /* The default value for `scan_control' is documented as FALSE in the */ - /* TrueType specification. This is confusing since it implies a */ - /* Boolean value. However, this is not the case, thus both the */ - /* default values of our `scan_type' and `scan_control' fields (which */ - /* the documentation's `scan_control' variable is split into) are */ - /* zero. */ - - const TT_GraphicsState tt_default_graphics_state = - { - 0, 0, 0, - { 0x4000, 0 }, - { 0x4000, 0 }, - { 0x4000, 0 }, - - 1, 64, 1, - TRUE, 68, 0, 0, 9, 3, - 0, FALSE, 0, 1, 1, 1 - }; - - /* documentation is in ttinterp.h */ FT_EXPORT_DEF( TT_ExecContext ) @@ -485,7 +349,8 @@ FT_Memory memory; FT_Error error; - TT_ExecContext exec = NULL; + TT_ExecContext exec = NULL; + FT_DebugHook_Func interp; if ( !driver ) @@ -497,6 +362,15 @@ if ( FT_NEW( exec ) ) goto Fail; + /* set `exec->interpreter' according to the debug hook present, */ + /* which is used by 'ttdebug'. */ + interp = driver->root.root.library->debug_hooks[FT_DEBUG_HOOK_TRUETYPE]; + + if ( interp ) + exec->interpreter = (TT_Interpreter)interp; + else + exec->interpreter = (TT_Interpreter)TT_RunIns; + /* create callStack here, other allocations delayed */ exec->memory = memory; exec->callSize = 32; @@ -1160,20 +1034,35 @@ #undef PACK -#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER +#ifdef FT_INT64 + +#define TT_MulFix14( a, b ) TT_MulFix14_64( a, b ) + + static inline FT_F26Dot6 + TT_MulFix14_64( FT_F26Dot6 a, + FT_F2Dot14 b ) + { + FT_Int64 ab = MUL_INT64( a, b ); + + + ab = ADD_INT64( ab, 0x2000 + ( ab >> 63 ) ); /* rounding phase */ + + return (FT_F26Dot6)( ab >> 14 ); + } + +#elif !defined( FT_CONFIG_OPTION_NO_ASSEMBLER ) #if defined( __arm__ ) && \ ( defined( __thumb2__ ) || !defined( __thumb__ ) ) #define TT_MulFix14 TT_MulFix14_arm - static FT_Int32 + static __inline FT_Int32 TT_MulFix14_arm( FT_Int32 a, - FT_Int b ) + FT_Int32 b ) { FT_Int32 t, t2; - #if defined( __CC_ARM ) || defined( __ARMCC__ ) __asm @@ -1199,8 +1088,8 @@ #endif "adds %1, %1, %0\n\t" /* %1 += %0 */ "adc %2, %2, #0\n\t" /* %2 += carry */ - "mov %0, %1, lsr #14\n\t" /* %0 = %1 >> 16 */ - "orr %0, %0, %2, lsl #18\n\t" /* %0 |= %2 << 16 */ + "mov %0, %1, lsr #14\n\t" /* %0 = %1 >> 14 */ + "orr %0, %0, %2, lsl #18\n\t" /* %0 |= %2 << 18 */ : "=r"(a), "=&r"(t2), "=&r"(t) : "r"(a), "r"(b) : "cc" ); @@ -1210,49 +1099,60 @@ return a; } -#endif /* __arm__ && ( __thumb2__ || !__thumb__ ) */ +#elif defined( __i386__ ) || defined( _M_IX86 ) -#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ +#define TT_MulFix14 TT_MulFix14_i386 + /* documentation is in freetype.h */ -#if defined( __GNUC__ ) && \ - ( defined( __i386__ ) || defined( __x86_64__ ) ) - -#define TT_MulFix14 TT_MulFix14_long_long - - /* Temporarily disable the warning that C90 doesn't support `long long'. */ -#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 -#pragma GCC diagnostic push -#endif -#pragma GCC diagnostic ignored "-Wlong-long" - - /* This is declared `noinline' because inlining the function results */ - /* in slower code. The `pure' attribute indicates that the result */ - /* only depends on the parameters. */ - static __attribute__(( noinline )) - __attribute__(( pure )) FT_Int32 - TT_MulFix14_long_long( FT_Int32 a, - FT_Int b ) + static __inline FT_Int32 + TT_MulFixi14_i386( FT_Int32 a, + FT_Int32 b ) { + FT_Int32 result; - long long ret = (long long)a * b; +#if defined( __GNUC__ ) - /* The following line assumes that right shifting of signed values */ - /* will actually preserve the sign bit. The exact behaviour is */ - /* undefined, but this is true on x86 and x86_64. */ - long long tmp = ret >> 63; + __asm__ __volatile__ ( + "imul %%edx\n" + "movl %%edx, %%ecx\n" + "sarl $31, %%ecx\n" + "addl $0x2000, %%ecx\n" + "addl %%ecx, %%eax\n" + "adcl $0, %%edx\n" + "shrl $14, %%eax\n" + "shll $18, %%edx\n" + "addl %%edx, %%eax\n" + : "=a"(result), "=d"(b) + : "a"(a), "d"(b) + : "%ecx", "cc" ); +#elif defined( _MSC_VER) - ret += 0x2000 + tmp; + __asm + { + mov eax, a + mov edx, b + imul edx + mov ecx, edx + sar ecx, 31 + add ecx, 2000h + add eax, ecx + adc edx, 0 + shr eax, 14 + shl edx, 18 + add eax, edx + mov result, eax + } - return (FT_Int32)( ret >> 14 ); +#endif + + return result; } -#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 -#pragma GCC diagnostic pop -#endif +#endif /* __i386__ || _M_IX86 */ -#endif /* __GNUC__ && ( __i386__ || __x86_64__ ) */ +#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ #ifndef TT_MulFix14 @@ -1262,92 +1162,59 @@ /* for platforms where sizeof(int) == 2. */ static FT_Int32 TT_MulFix14( FT_Int32 a, - FT_Int b ) + FT_Int16 b ) { - FT_Int32 sign; - FT_UInt32 ah, al, mid, lo, hi; + FT_Int32 m, hi; + FT_UInt32 l, lo; - sign = a ^ b; + /* compute a*b as 64-bit (hi_lo) value */ + l = (FT_UInt32)( ( a & 0xFFFFU ) * b ); + m = ( a >> 16 ) * b; - if ( a < 0 ) - a = -a; - if ( b < 0 ) - b = -b; + lo = l + ( (FT_UInt32)m << 16 ); + hi = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo < l ); - ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU ); - al = (FT_UInt32)( a & 0xFFFFU ); + /* divide the result by 2^14 with rounding */ + l = lo + 0x2000U + (FT_UInt32)( hi >> 31 ); /* rounding phase */ + hi += ( l < lo ); - lo = al * b; - mid = ah * b; - hi = mid >> 16; - mid = ( mid << 16 ) + ( 1 << 13 ); /* rounding */ - lo += mid; - if ( lo < mid ) - hi += 1; - - mid = ( lo >> 14 ) | ( hi << 18 ); - - return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid; + return (FT_F26Dot6)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) ); } #endif /* !TT_MulFix14 */ -#if defined( __GNUC__ ) && \ - ( defined( __i386__ ) || \ - defined( __x86_64__ ) || \ - defined( __arm__ ) ) - -#define TT_DotFix14 TT_DotFix14_long_long - -#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 -#pragma GCC diagnostic push -#endif -#pragma GCC diagnostic ignored "-Wlong-long" - - static __attribute__(( pure )) FT_Int32 - TT_DotFix14_long_long( FT_Int32 ax, - FT_Int32 ay, - FT_Int bx, - FT_Int by ) - { - /* Temporarily disable the warning that C90 doesn't support */ - /* `long long'. */ - - long long temp1 = (long long)ax * bx; - long long temp2 = (long long)ay * by; - - - temp1 += temp2; - temp2 = temp1 >> 63; - temp1 += 0x2000 + temp2; - - return (FT_Int32)( temp1 >> 14 ); - - } - -#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 -#pragma GCC diagnostic pop -#endif - -#endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */ - - -#ifndef TT_DotFix14 +#ifdef FT_INT64 /* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */ - static FT_Int32 - TT_DotFix14( FT_Int32 ax, - FT_Int32 ay, - FT_Int bx, - FT_Int by ) + static inline FT_F26Dot6 + TT_DotFix14( FT_F26Dot6 ax, + FT_F26Dot6 ay, + FT_F2Dot14 bx, + FT_F2Dot14 by ) { - FT_Int32 m, s, hi1, hi2, hi; + FT_Int64 c = ADD_INT64( MUL_INT64( ax, bx ), MUL_INT64( ay, by ) ); + + + c = ADD_INT64( c, 0x2000 + ( c >> 63 ) ); /* rounding phase */ + + return (FT_F26Dot6)( c >> 14 ); + } + +#else + + static inline FT_F26Dot6 + TT_DotFix14( FT_F26Dot6 ax, + FT_F26Dot6 ay, + FT_F2Dot14 bx, + FT_F2Dot14 by ) + { + FT_Int32 m, hi1, hi2, hi; FT_UInt32 l, lo1, lo2, lo; - /* compute ax*bx as 64-bit value */ + /* compute ax*bx as 64-bit (hi_lo) value */ l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx ); m = ( ax >> 16 ) * bx; @@ -1366,18 +1233,13 @@ hi = hi1 + hi2 + ( lo < lo1 ); /* divide the result by 2^14 with rounding */ - s = hi >> 31; - l = lo + (FT_UInt32)s; - hi += s + ( l < lo ); - lo = l; - - l = lo + 0x2000U; + l = lo + 0x2000U + (FT_UInt32)( hi >> 31 ); /* rounding phase */ hi += ( l < lo ); - return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) ); + return (FT_F26Dot6)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) ); } -#endif /* TT_DotFix14 */ +#endif /* !FT_INT64 */ /************************************************************************** @@ -1531,31 +1393,6 @@ } - /************************************************************************** - * - * @Function: - * GetShortIns - * - * @Description: - * Returns a short integer taken from the instruction stream at - * address IP. - * - * @Return: - * Short read at code[IP]. - * - * @Note: - * This one could become a macro. - */ - static FT_Short - GetShortIns( TT_ExecContext exc ) - { - /* Reading a byte stream so there is no endianness (DaveP) */ - exc->IP += 2; - return (FT_Short)( ( exc->code[exc->IP - 2] << 8 ) + - exc->code[exc->IP - 1] ); - } - - /************************************************************************** * * @Function: @@ -1609,6 +1446,7 @@ exc->code = range->base; exc->codeSize = range->size; exc->IP = aIP; + exc->length = 0; exc->curRange = aRange; return SUCCESS; @@ -1671,48 +1509,33 @@ FT_UShort point, FT_F26Dot6 distance ) { - FT_F26Dot6 v; + FT_Fixed v; - v = exc->GS.freeVector.x; - + v = exc->moveVector.x; if ( v != 0 ) { #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* Exception to the post-IUP curfew: Allow the x component of */ /* diagonal moves, but only post-IUP. DejaVu tries to adjust */ /* diagonal stems like on `Z' and `z' post-IUP. */ - if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility ) - zone->cur[point].x = ADD_LONG( zone->cur[point].x, - FT_MulDiv( distance, - v, - exc->F_dot_P ) ); - else + if ( !exc->backward_compatibility ) #endif - - if ( NO_SUBPIXEL_HINTING ) zone->cur[point].x = ADD_LONG( zone->cur[point].x, - FT_MulDiv( distance, - v, - exc->F_dot_P ) ); + FT_MulFix( distance, v ) ); zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; } - v = exc->GS.freeVector.y; - + v = exc->moveVector.y; if ( v != 0 ) { #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( !( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility && - exc->iupx_called && - exc->iupy_called ) ) + /* See `ttinterp.h' for details on backward compatibility mode. */ + if ( exc->backward_compatibility != 0x7 ) #endif zone->cur[point].y = ADD_LONG( zone->cur[point].y, - FT_MulDiv( distance, - v, - exc->F_dot_P ) ); + FT_MulFix( distance, v ) ); zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; } @@ -1745,24 +1568,20 @@ FT_UShort point, FT_F26Dot6 distance ) { - FT_F26Dot6 v; + FT_Fixed v; - v = exc->GS.freeVector.x; + v = exc->moveVector.x; if ( v != 0 ) zone->org[point].x = ADD_LONG( zone->org[point].x, - FT_MulDiv( distance, - v, - exc->F_dot_P ) ); + FT_MulFix( distance, v ) ); - v = exc->GS.freeVector.y; + v = exc->moveVector.y; if ( v != 0 ) zone->org[point].y = ADD_LONG( zone->org[point].y, - FT_MulDiv( distance, - v, - exc->F_dot_P ) ); + FT_MulFix( distance, v ) ); } @@ -1784,12 +1603,8 @@ FT_F26Dot6 distance ) { #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility ) - zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance ); - else + if ( !exc->backward_compatibility ) #endif - - if ( NO_SUBPIXEL_HINTING ) zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance ); zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; @@ -1805,9 +1620,8 @@ FT_UNUSED( exc ); #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( !( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility && - exc->iupx_called && exc->iupy_called ) ) + /* See `ttinterp.h' for details on backward compatibility mode. */ + if ( exc->backward_compatibility != 0x7 ) #endif zone->cur[point].y = ADD_LONG( zone->cur[point].y, distance ); @@ -1860,8 +1674,8 @@ * distance :: * The distance (not) to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * The compensated distance. @@ -1869,10 +1683,10 @@ static FT_F26Dot6 Round_None( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; + FT_UNUSED( exc ); if ( distance >= 0 ) @@ -1903,8 +1717,8 @@ * distance :: * The distance to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * Rounded distance. @@ -1912,10 +1726,10 @@ static FT_F26Dot6 Round_To_Grid( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; + FT_UNUSED( exc ); if ( distance >= 0 ) @@ -1948,8 +1762,8 @@ * distance :: * The distance to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * Rounded distance. @@ -1957,10 +1771,10 @@ static FT_F26Dot6 Round_To_Half_Grid( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; + FT_UNUSED( exc ); if ( distance >= 0 ) @@ -1995,8 +1809,8 @@ * distance :: * The distance to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * Rounded distance. @@ -2004,10 +1818,10 @@ static FT_F26Dot6 Round_Down_To_Grid( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; + FT_UNUSED( exc ); if ( distance >= 0 ) @@ -2039,8 +1853,8 @@ * distance :: * The distance to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * Rounded distance. @@ -2048,10 +1862,10 @@ static FT_F26Dot6 Round_Up_To_Grid( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; + FT_UNUSED( exc ); if ( distance >= 0 ) @@ -2084,8 +1898,8 @@ * distance :: * The distance to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * Rounded distance. @@ -2093,10 +1907,10 @@ static FT_F26Dot6 Round_To_Double_Grid( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; + FT_UNUSED( exc ); if ( distance >= 0 ) @@ -2129,8 +1943,8 @@ * distance :: * The distance to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * Rounded distance. @@ -2144,9 +1958,8 @@ static FT_F26Dot6 Round_Super( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; @@ -2185,8 +1998,8 @@ * distance :: * The distance to round. * - * color :: - * The engine compensation color. + * compensation :: + * The engine compensation. * * @Return: * Rounded distance. @@ -2198,9 +2011,8 @@ static FT_F26Dot6 Round_Super_45( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ) + FT_F26Dot6 compensation ) { - FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; FT_F26Dot6 val; @@ -2227,59 +2039,6 @@ } - /************************************************************************** - * - * @Function: - * Compute_Round - * - * @Description: - * Sets the rounding mode. - * - * @Input: - * round_mode :: - * The rounding mode to be used. - */ - static void - Compute_Round( TT_ExecContext exc, - FT_Byte round_mode ) - { - switch ( round_mode ) - { - case TT_Round_Off: - exc->func_round = (TT_Round_Func)Round_None; - break; - - case TT_Round_To_Grid: - exc->func_round = (TT_Round_Func)Round_To_Grid; - break; - - case TT_Round_Up_To_Grid: - exc->func_round = (TT_Round_Func)Round_Up_To_Grid; - break; - - case TT_Round_Down_To_Grid: - exc->func_round = (TT_Round_Func)Round_Down_To_Grid; - break; - - case TT_Round_To_Half_Grid: - exc->func_round = (TT_Round_Func)Round_To_Half_Grid; - break; - - case TT_Round_To_Double_Grid: - exc->func_round = (TT_Round_Func)Round_To_Double_Grid; - break; - - case TT_Round_Super: - exc->func_round = (TT_Round_Func)Round_Super; - break; - - case TT_Round_Super_45: - exc->func_round = (TT_Round_Func)Round_Super_45; - break; - } - } - - /************************************************************************** * * @Function: @@ -2481,14 +2240,45 @@ static void Compute_Funcs( TT_ExecContext exc ) { - if ( exc->GS.freeVector.x == 0x4000 ) - exc->F_dot_P = exc->GS.projVector.x; - else if ( exc->GS.freeVector.y == 0x4000 ) - exc->F_dot_P = exc->GS.projVector.y; + FT_Long F_dot_P = + ( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x + + (FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y + + 0x2000L ) >> 14; + + + if ( F_dot_P >= 0x3FFEL ) + { + /* commonly collinear */ + exc->moveVector.x = exc->GS.freeVector.x * 4; + exc->moveVector.y = exc->GS.freeVector.y * 4; + } + else if ( -0x400L < F_dot_P && F_dot_P < 0x400L ) + { + /* prohibitively orthogonal */ + exc->moveVector.x = 0; + exc->moveVector.y = 0; + } else - exc->F_dot_P = - ( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x + - (FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y ) >> 14; + { + exc->moveVector.x = exc->GS.freeVector.x * 0x10000L / F_dot_P; + exc->moveVector.y = exc->GS.freeVector.y * 0x10000L / F_dot_P; + } + + if ( F_dot_P >= 0x3FFEL && exc->GS.freeVector.x == 0x4000 ) + { + exc->func_move = (TT_Move_Func)Direct_Move_X; + exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X; + } + else if ( F_dot_P >= 0x3FFEL && exc->GS.freeVector.y == 0x4000 ) + { + exc->func_move = (TT_Move_Func)Direct_Move_Y; + exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y; + } + else + { + exc->func_move = (TT_Move_Func)Direct_Move; + exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig; + } if ( exc->GS.projVector.x == 0x4000 ) exc->func_project = (TT_Project_Func)Project_x; @@ -2504,29 +2294,6 @@ else exc->func_dualproj = (TT_Project_Func)Dual_Project; - exc->func_move = (TT_Move_Func)Direct_Move; - exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig; - - if ( exc->F_dot_P == 0x4000L ) - { - if ( exc->GS.freeVector.x == 0x4000 ) - { - exc->func_move = (TT_Move_Func)Direct_Move_X; - exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X; - } - else if ( exc->GS.freeVector.y == 0x4000 ) - { - exc->func_move = (TT_Move_Func)Direct_Move_Y; - exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y; - } - } - - /* at small sizes, F_dot_P can become too small, resulting */ - /* in overflows and `spikes' in a number of glyphs like `w'. */ - - if ( FT_ABS( exc->F_dot_P ) < 0x400L ) - exc->F_dot_P = 0x4000L; - /* Disable cached aspect ratio */ exc->tt_metrics.ratio = 0; } @@ -2799,7 +2566,7 @@ Ins_ODD( TT_ExecContext exc, FT_Long* args ) { - args[0] = ( ( exc->func_round( exc, args[0], 3 ) & 127 ) == 64 ); + args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 64 ) == 64 ); } @@ -2813,7 +2580,7 @@ Ins_EVEN( TT_ExecContext exc, FT_Long* args ) { - args[0] = ( ( exc->func_round( exc, args[0], 3 ) & 127 ) == 0 ); + args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 64 ) == 0 ); } @@ -3020,7 +2787,7 @@ FT_MEM_QRENEW_ARRAY( exc->glyfStorage, exc->glyfStoreSize, exc->storeSize ); - exc->error = error; + exc->error = error; if ( error ) return; @@ -3143,7 +2910,8 @@ Ins_ROUND( TT_ExecContext exc, FT_Long* args ) { - args[0] = exc->func_round( exc, args[0], exc->opcode & 3 ); + args[0] = exc->func_round( exc, args[0], + exc->GS.compensation[exc->opcode & 3] ); } @@ -3157,7 +2925,8 @@ Ins_NROUND( TT_ExecContext exc, FT_Long* args ) { - args[0] = Round_None( exc, args[0], exc->opcode & 3 ); + args[0] = Round_None( exc, args[0], + exc->GS.compensation[exc->opcode & 3] ); } @@ -3211,13 +2980,11 @@ } else { - K = exc->stack[exc->args - L]; + K = args[-L]; - FT_ARRAY_MOVE( &exc->stack[exc->args - L ], - &exc->stack[exc->args - L + 1], - ( L - 1 ) ); + FT_ARRAY_MOVE( args - L, args - L + 1, L - 1 ); - exc->stack[exc->args - 1] = K; + args[-1] = K; } } @@ -3244,7 +3011,7 @@ args[0] = 0; } else - args[0] = exc->stack[exc->args - L]; + args[0] = args[-L]; } @@ -3314,8 +3081,7 @@ exc->length = 2 - exc->length * exc->code[exc->IP + 1]; } - if ( exc->IP + exc->length <= exc->codeSize ) - return SUCCESS; + return SUCCESS; } Fail_Overflow: @@ -3363,6 +3129,9 @@ nIfs--; Out = FT_BOOL( nIfs == 0 ); break; + + default: + break; } } while ( Out == 0 ); } @@ -3396,6 +3165,9 @@ case 0x59: /* EIF */ nIfs--; break; + + default: + break; } } while ( nIfs != 0 ); } @@ -3439,7 +3211,7 @@ return; } - exc->step_ins = FALSE; + exc->length = 0; if ( args[0] < 0 ) { @@ -3540,10 +3312,10 @@ return; } - rec->range = exc->curRange; - rec->opc = (FT_UInt16)n; - rec->start = exc->IP + 1; - rec->active = TRUE; + rec->range = exc->curRange; + rec->opc = (FT_UInt16)n; + rec->start = exc->IP + 1; + rec->active = TRUE; if ( n > exc->maxFunc ) exc->maxFunc = (FT_UInt16)n; @@ -3555,14 +3327,17 @@ { switch ( exc->opcode ) { - case 0x89: /* IDEF */ - case 0x2C: /* FDEF */ + case 0x89: /* IDEF */ + case 0x2C: /* FDEF */ exc->error = FT_THROW( Nested_DEFS ); return; case 0x2D: /* ENDF */ rec->end = exc->IP; return; + + default: + break; } } } @@ -3592,12 +3367,11 @@ pRec->Cur_Count--; - exc->step_ins = FALSE; - if ( pRec->Cur_Count > 0 ) { exc->callTop++; - exc->IP = pRec->Def->start; + exc->IP = pRec->Def->start; + exc->length = 0; } else /* Loop through the current function */ @@ -3685,8 +3459,6 @@ Ins_Goto_CodeRange( exc, def->range, def->start ); - exc->step_ins = FALSE; - return; Fail: @@ -3764,8 +3536,6 @@ Ins_Goto_CodeRange( exc, def->range, def->start ); - exc->step_ins = FALSE; - exc->loopcall_counter += (FT_ULong)args[0]; if ( exc->loopcall_counter > exc->loopcall_counter_max ) exc->error = FT_THROW( Execution_Too_Long ); @@ -3845,9 +3615,13 @@ case 0x2C: /* FDEF */ exc->error = FT_THROW( Nested_DEFS ); return; + case 0x2D: /* ENDF */ def->end = exc->IP; return; + + default: + break; } } } @@ -3870,10 +3644,23 @@ Ins_NPUSHB( TT_ExecContext exc, FT_Long* args ) { - FT_UShort L, K; + FT_Long IP = exc->IP; + FT_Int L, K; - L = (FT_UShort)exc->code[exc->IP + 1]; + if ( ++IP >= exc->codeSize ) + { + exc->error = FT_THROW( Code_Overflow ); + return; + } + + L = exc->code[IP]; + + if ( IP + L >= exc->codeSize ) + { + exc->error = FT_THROW( Code_Overflow ); + return; + } if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) { @@ -3881,10 +3668,11 @@ return; } - for ( K = 1; K <= L; K++ ) - args[K - 1] = exc->code[exc->IP + K + 1]; + for ( K = 0; K < L; K++ ) + args[K] = exc->code[++IP]; exc->new_top += L; + exc->IP = IP; } @@ -3898,10 +3686,23 @@ Ins_NPUSHW( TT_ExecContext exc, FT_Long* args ) { - FT_UShort L, K; + FT_Long IP = exc->IP; + FT_Int L, K; - L = (FT_UShort)exc->code[exc->IP + 1]; + if ( ++IP >= exc->codeSize ) + { + exc->error = FT_THROW( Code_Overflow ); + return; + } + + L = exc->code[IP]; + + if ( IP + 2 * L >= exc->codeSize ) + { + exc->error = FT_THROW( Code_Overflow ); + return; + } if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) { @@ -3909,13 +3710,12 @@ return; } - exc->IP += 2; + /* note casting for sign-extension */ + for ( K = 0; K < L; K++, IP += 2 ) + args[K] = (FT_Short)( exc->code[IP + 1] << 8 ) | exc->code[IP + 2]; - for ( K = 0; K < L; K++ ) - args[K] = GetShortIns( exc ); - - exc->step_ins = FALSE; exc->new_top += L; + exc->IP = IP; } @@ -3929,10 +3729,17 @@ Ins_PUSHB( TT_ExecContext exc, FT_Long* args ) { - FT_UShort L, K; + FT_Long IP = exc->IP; + FT_Int L, K; - L = (FT_UShort)( exc->opcode - 0xB0 + 1 ); + L = exc->opcode - 0xB0 + 1; + + if ( IP + L >= exc->codeSize ) + { + exc->error = FT_THROW( Code_Overflow ); + return; + } if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) { @@ -3940,8 +3747,10 @@ return; } - for ( K = 1; K <= L; K++ ) - args[K - 1] = exc->code[exc->IP + K]; + for ( K = 0; K < L; K++ ) + args[K] = exc->code[++IP]; + + exc->IP = IP; } @@ -3955,10 +3764,17 @@ Ins_PUSHW( TT_ExecContext exc, FT_Long* args ) { - FT_UShort L, K; + FT_Long IP = exc->IP; + FT_Int L, K; - L = (FT_UShort)( exc->opcode - 0xB8 + 1 ); + L = exc->opcode - 0xB8 + 1; + + if ( IP + 2 * L >= exc->codeSize ) + { + exc->error = FT_THROW( Code_Overflow ); + return; + } if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) { @@ -3966,12 +3782,11 @@ return; } - exc->IP++; + /* note casting for sign-extension */ + for ( K = 0; K < L; K++, IP += 2 ) + args[K] = (FT_Short)( exc->code[IP + 1] << 8 ) | exc->code[IP + 2]; - for ( K = 0; K < L; K++ ) - args[K] = GetShortIns( exc ); - - exc->step_ins = FALSE; + exc->IP = IP; } @@ -4142,15 +3957,12 @@ Ins_SPVFS( TT_ExecContext exc, FT_Long* args ) { - FT_Short S; FT_Long X, Y; /* Only use low 16bits, then sign extend */ - S = (FT_Short)args[1]; - Y = (FT_Long)S; - S = (FT_Short)args[0]; - X = (FT_Long)S; + Y = (FT_Short)args[1]; + X = (FT_Short)args[0]; Normalize( X, Y, &exc->GS.projVector ); @@ -4169,15 +3981,12 @@ Ins_SFVFS( TT_ExecContext exc, FT_Long* args ) { - FT_Short S; FT_Long X, Y; /* Only use low 16bits, then sign extend */ - S = (FT_Short)args[1]; - Y = (FT_Long)S; - S = (FT_Short)args[0]; - X = S; + Y = (FT_Short)args[1]; + X = (FT_Short)args[0]; Normalize( X, Y, &exc->GS.freeVector ); Compute_Funcs( exc ); @@ -4915,7 +4724,7 @@ /* compatibility hacks and lets them program points to the grid like */ /* it's 1996. They might sign a waiver for just one glyph, though. */ if ( SUBPIXEL_HINTING_MINIMAL ) - exc->backward_compatibility = !FT_BOOL( L == 4 ); + exc->backward_compatibility = ( L & 4 ) ^ 4; #endif } else if ( exc->pedantic_hinting ) @@ -4999,32 +4808,31 @@ * Stack: uint32... --> */ static void - Ins_FLIPPT( TT_ExecContext exc ) + Ins_FLIPPT( TT_ExecContext exc, + FT_Long* args ) { + FT_Long loop = exc->GS.loop; FT_UShort point; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - /* See `ttinterp.h' for details on backward compatibility mode. */ - if ( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility && - exc->iupx_called && - exc->iupy_called ) - goto Fail; -#endif - - if ( exc->top < exc->GS.loop ) + if ( exc->new_top < loop ) { if ( exc->pedantic_hinting ) exc->error = FT_THROW( Too_Few_Arguments ); goto Fail; } - while ( exc->GS.loop > 0 ) - { - exc->args--; + exc->new_top -= loop; - point = (FT_UShort)exc->stack[exc->args]; +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + /* See `ttinterp.h' for details on backward compatibility mode. */ + if ( exc->backward_compatibility == 0x7 ) + goto Fail; +#endif + + while ( loop-- ) + { + point = (FT_UShort)*(--args); if ( BOUNDS( point, exc->pts.n_points ) ) { @@ -5036,13 +4844,10 @@ } else exc->pts.tags[point] ^= FT_CURVE_TAG_ON; - - exc->GS.loop--; } Fail: exc->GS.loop = 1; - exc->new_top = exc->args; } @@ -5061,10 +4866,7 @@ #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* See `ttinterp.h' for details on backward compatibility mode. */ - if ( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility && - exc->iupx_called && - exc->iupy_called ) + if ( exc->backward_compatibility == 0x7 ) return; #endif @@ -5099,10 +4901,7 @@ #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* See `ttinterp.h' for details on backward compatibility mode. */ - if ( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility && - exc->iupx_called && - exc->iupy_called ) + if ( exc->backward_compatibility == 0x7 ) return; #endif @@ -5158,8 +4957,8 @@ d = PROJECT( zp.cur + p, zp.org + p ); - *x = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.x, exc->F_dot_P ); - *y = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.y, exc->F_dot_P ); + *x = FT_MulFix( d, exc->moveVector.x ); + *y = FT_MulFix( d, exc->moveVector.y ); return SUCCESS; } @@ -5176,8 +4975,8 @@ if ( exc->GS.freeVector.x != 0 ) { #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( !( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility ) ) + /* See `ttinterp.h' for details on backward compatibility mode. */ + if ( !exc->backward_compatibility ) #endif exc->zp2.cur[point].x = ADD_LONG( exc->zp2.cur[point].x, dx ); @@ -5188,10 +4987,8 @@ if ( exc->GS.freeVector.y != 0 ) { #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( !( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility && - exc->iupx_called && - exc->iupy_called ) ) + /* See `ttinterp.h' for details on backward compatibility mode. */ + if ( exc->backward_compatibility != 0x7 ) #endif exc->zp2.cur[point].y = ADD_LONG( exc->zp2.cur[point].y, dy ); @@ -5208,8 +5005,10 @@ * Stack: uint32... --> */ static void - Ins_SHP( TT_ExecContext exc ) + Ins_SHP( TT_ExecContext exc, + FT_Long* args ) { + FT_Long loop = exc->GS.loop; TT_GlyphZoneRec zp; FT_UShort refp; @@ -5217,20 +5016,21 @@ FT_UShort point; - if ( exc->top < exc->GS.loop ) + if ( exc->new_top < loop ) { if ( exc->pedantic_hinting ) - exc->error = FT_THROW( Invalid_Reference ); + exc->error = FT_THROW( Too_Few_Arguments ); goto Fail; } + exc->new_top -= loop; + if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) ) return; - while ( exc->GS.loop > 0 ) + while ( loop-- ) { - exc->args--; - point = (FT_UShort)exc->stack[exc->args]; + point = (FT_UShort)*(--args); if ( BOUNDS( point, exc->zp2.n_points ) ) { @@ -5242,13 +5042,10 @@ } else Move_Zp2_Point( exc, point, dx, dy, TRUE ); - - exc->GS.loop--; } Fail: exc->GS.loop = 1; - exc->new_top = exc->args; } @@ -5364,6 +5161,7 @@ Ins_SHPIX( TT_ExecContext exc, FT_Long* args ) { + FT_Long loop = exc->GS.loop; FT_F26Dot6 dx, dy; FT_UShort point; #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL @@ -5373,22 +5171,21 @@ #endif - - if ( exc->top < exc->GS.loop + 1 ) + if ( exc->new_top < loop ) { if ( exc->pedantic_hinting ) - exc->error = FT_THROW( Invalid_Reference ); + exc->error = FT_THROW( Too_Few_Arguments ); goto Fail; } + exc->new_top -= loop; + dx = TT_MulFix14( args[0], exc->GS.freeVector.x ); dy = TT_MulFix14( args[0], exc->GS.freeVector.y ); - while ( exc->GS.loop > 0 ) + while ( loop-- ) { - exc->args--; - - point = (FT_UShort)exc->stack[exc->args]; + point = (FT_UShort)*(--args); if ( BOUNDS( point, exc->zp2.n_points ) ) { @@ -5400,8 +5197,7 @@ } else #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility ) + if ( exc->backward_compatibility ) { /* Special case: allow SHPIX to move points in the twilight zone. */ /* Otherwise, treat SHPIX the same as DELTAP. Unbreaks various */ @@ -5409,7 +5205,7 @@ /* that would glitch severely after calling ALIGNRP after a */ /* blocked SHPIX. */ if ( in_twilight || - ( !( exc->iupx_called && exc->iupy_called ) && + ( exc->backward_compatibility != 0x7 && ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y ) ) ) ) Move_Zp2_Point( exc, point, 0, dy, TRUE ); @@ -5417,13 +5213,10 @@ else #endif Move_Zp2_Point( exc, point, dx, dy, TRUE ); - - exc->GS.loop--; } Fail: exc->GS.loop = 1; - exc->new_top = exc->args; } @@ -5502,7 +5295,7 @@ if ( ( exc->opcode & 1 ) != 0 ) { cur_dist = FAST_PROJECT( &exc->zp0.cur[point] ); - distance = SUB_LONG( exc->func_round( exc, cur_dist, 3 ), cur_dist ); + distance = SUB_LONG( exc->func_round( exc, cur_dist, 0 ), cur_dist ); } else distance = 0; @@ -5566,7 +5359,7 @@ if ( exc->GS.gep0 == 0 ) /* If in twilight zone */ { exc->zp0.org[point].x = TT_MulFix14( distance, - exc->GS.freeVector.x ); + exc->GS.freeVector.x ); exc->zp0.org[point].y = TT_MulFix14( distance, exc->GS.freeVector.y ); exc->zp0.cur[point] = exc->zp0.org[point]; @@ -5587,7 +5380,7 @@ if ( delta > control_value_cutin ) distance = org_dist; - distance = exc->func_round( exc, distance, 3 ); + distance = exc->func_round( exc, distance, 0 ); } exc->func_move( exc, &exc->zp0, point, SUB_LONG( distance, org_dist ) ); @@ -5609,7 +5402,7 @@ FT_Long* args ) { FT_UShort point = 0; - FT_F26Dot6 org_dist, distance; + FT_F26Dot6 org_dist, distance, compensation; point = (FT_UShort)args[0]; @@ -5678,12 +5471,12 @@ /* round flag */ + compensation = exc->GS.compensation[exc->opcode & 3]; + if ( ( exc->opcode & 4 ) != 0 ) - { - distance = exc->func_round( exc, org_dist, exc->opcode & 3 ); - } + distance = exc->func_round( exc, org_dist, compensation ); else - distance = Round_None( exc, org_dist, exc->opcode & 3 ); + distance = Round_None( exc, org_dist, compensation ); /* minimum distance flag */ @@ -5735,7 +5528,8 @@ FT_F26Dot6 cvt_dist, distance, cur_dist, - org_dist; + org_dist, + compensation; FT_F26Dot6 delta; @@ -5801,6 +5595,8 @@ /* control value cut-in and round */ + compensation = exc->GS.compensation[exc->opcode & 3]; + if ( ( exc->opcode & 4 ) != 0 ) { /* XXX: UNDOCUMENTED! Only perform cut-in test when both points */ @@ -5831,16 +5627,16 @@ cvt_dist = org_dist; } - distance = exc->func_round( exc, cvt_dist, exc->opcode & 3 ); + distance = exc->func_round( exc, cvt_dist, compensation ); } else - distance = Round_None( exc, cvt_dist, exc->opcode & 3 ); + distance = Round_None( exc, cvt_dist, compensation ); /* minimum distance test */ if ( ( exc->opcode & 8 ) != 0 ) { - FT_F26Dot6 minimum_distance = exc->GS.minimum_distance; + FT_F26Dot6 minimum_distance = exc->GS.minimum_distance; if ( org_dist >= 0 ) @@ -5862,11 +5658,10 @@ Fail: exc->GS.rp1 = exc->GS.rp0; + exc->GS.rp2 = point; if ( ( exc->opcode & 16 ) != 0 ) exc->GS.rp0 = point; - - exc->GS.rp2 = point; } @@ -5877,25 +5672,33 @@ * Stack: uint32 uint32... --> */ static void - Ins_ALIGNRP( TT_ExecContext exc ) + Ins_ALIGNRP( TT_ExecContext exc, + FT_Long* args ) { + FT_Long loop = exc->GS.loop; FT_UShort point; FT_F26Dot6 distance; - if ( exc->top < exc->GS.loop || - BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) + if ( exc->new_top < loop ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Too_Few_Arguments ); + goto Fail; + } + + exc->new_top -= loop; + + if ( BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) { if ( exc->pedantic_hinting ) exc->error = FT_THROW( Invalid_Reference ); goto Fail; } - while ( exc->GS.loop > 0 ) + while ( loop-- ) { - exc->args--; - - point = (FT_UShort)exc->stack[exc->args]; + point = (FT_UShort)*(--args); if ( BOUNDS( point, exc->zp1.n_points ) ) { @@ -5912,13 +5715,10 @@ exc->func_move( exc, &exc->zp1, point, NEG_LONG( distance ) ); } - - exc->GS.loop--; } Fail: exc->GS.loop = 1; - exc->new_top = exc->args; } @@ -6060,15 +5860,26 @@ /* SOMETIMES, DUMBER CODE IS BETTER CODE */ static void - Ins_IP( TT_ExecContext exc ) + Ins_IP( TT_ExecContext exc, + FT_Long* args ) { + FT_Long loop = exc->GS.loop; FT_F26Dot6 old_range, cur_range; FT_Vector* orus_base; FT_Vector* cur_base; FT_Int twilight; - if ( exc->top < exc->GS.loop ) + if ( exc->new_top < loop ) + { + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Too_Few_Arguments ); + goto Fail; + } + + exc->new_top -= loop; + + if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) ) { if ( exc->pedantic_hinting ) exc->error = FT_THROW( Invalid_Reference ); @@ -6084,13 +5895,6 @@ exc->GS.gep1 == 0 || exc->GS.gep2 == 0 ); - if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) ) - { - if ( exc->pedantic_hinting ) - exc->error = FT_THROW( Invalid_Reference ); - goto Fail; - } - if ( twilight ) orus_base = &exc->zp0.org[exc->GS.rp1]; else @@ -6102,8 +5906,7 @@ /* fonts out there (e.g. [aeu]grave in monotype.ttf) */ /* calling IP[] with bad values of rp[12]. */ /* Do something sane when this odd thing happens. */ - if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) || - BOUNDS( exc->GS.rp2, exc->zp1.n_points ) ) + if ( BOUNDS( exc->GS.rp2, exc->zp1.n_points ) ) { old_range = 0; cur_range = 0; @@ -6132,9 +5935,9 @@ cur_range = PROJECT( &exc->zp1.cur[exc->GS.rp2], cur_base ); } - for ( ; exc->GS.loop > 0; exc->GS.loop-- ) + while ( loop-- ) { - FT_UInt point = (FT_UInt)exc->stack[--exc->args]; + FT_UInt point = (FT_UInt)*(--args); FT_F26Dot6 org_dist, cur_dist, new_dist; @@ -6206,7 +6009,6 @@ Fail: exc->GS.loop = 1; - exc->new_top = exc->args; } @@ -6405,17 +6207,10 @@ /* See `ttinterp.h' for details on backward compatibility mode. */ /* Allow IUP until it has been called on both axes. Immediately */ /* return on subsequent ones. */ - if ( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility ) - { - if ( exc->iupx_called && exc->iupy_called ) - return; - - if ( exc->opcode & 1 ) - exc->iupx_called = TRUE; - else - exc->iupy_called = TRUE; - } + if ( exc->backward_compatibility == 0x7 ) + return; + else if ( exc->backward_compatibility ) + exc->backward_compatibility |= 1 << ( exc->opcode & 1 ); #endif /* ignore empty outlines */ @@ -6507,30 +6302,50 @@ Ins_DELTAP( TT_ExecContext exc, FT_Long* args ) { - FT_ULong nump, k; + FT_Long nump; FT_UShort A; - FT_ULong C, P; - FT_Long B; + FT_Long B, P, F; - P = (FT_ULong)exc->func_cur_ppem( exc ); - nump = (FT_ULong)args[0]; /* some points theoretically may occur more - than once, thus UShort isn't enough */ + nump = args[0]; /* signed value for convenience */ - for ( k = 1; k <= nump; k++ ) + if ( nump < 0 || nump > exc->new_top / 2 ) { - if ( exc->args < 2 ) - { - if ( exc->pedantic_hinting ) - exc->error = FT_THROW( Too_Few_Arguments ); - exc->args = 0; - goto Fail; - } + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Too_Few_Arguments ); - exc->args -= 2; + nump = exc->new_top / 2; + } - A = (FT_UShort)exc->stack[exc->args + 1]; - B = exc->stack[exc->args]; + exc->new_top -= 2 * nump; + + P = exc->func_cur_ppem( exc ) - exc->GS.delta_base; + + switch ( exc->opcode ) + { + case 0x5D: + break; + + case 0x71: + P -= 16; + break; + + case 0x72: + P -= 32; + break; + } + + /* check applicable range of adjusted ppem */ + if ( P & ~0xF ) /* P < 0 || P > 15 */ + return; + + P <<= 4; + F = 1L << ( 6 - exc->GS.delta_shift ); + + while ( nump-- ) + { + A = (FT_UShort)*(--args); + B = *(--args); /* XXX: Because some popular fonts contain some invalid DeltaP */ /* instructions, we simply ignore them when the stacked */ @@ -6538,41 +6353,28 @@ /* error. As a delta instruction doesn't change a glyph */ /* in great ways, this shouldn't be a problem. */ - if ( !BOUNDS( A, exc->zp0.n_points ) ) + if ( BOUNDS( A, exc->zp0.n_points ) ) { - C = ( (FT_ULong)B & 0xF0 ) >> 4; - - switch ( exc->opcode ) + if ( exc->pedantic_hinting ) { - case 0x5D: - break; - - case 0x71: - C += 16; - break; - - case 0x72: - C += 32; - break; + exc->error = FT_THROW( Invalid_Reference ); + return; } - - C += exc->GS.delta_base; - - if ( P == C ) + } + else + { + if ( ( B & 0xF0 ) == P ) { - B = ( (FT_ULong)B & 0xF ) - 8; + B = ( B & 0xF ) - 8; if ( B >= 0 ) B++; - B *= 1L << ( 6 - exc->GS.delta_shift ); - + B *= F; #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - /* See `ttinterp.h' for details on backward compatibility */ - /* mode. */ - if ( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility ) + /* See `ttinterp.h' for details on backward compatibility mode. */ + if ( exc->backward_compatibility ) { - if ( !( exc->iupx_called && exc->iupy_called ) && + if ( exc->backward_compatibility != 0x7 && ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) ) exc->func_move( exc, &exc->zp0, A, B ); @@ -6582,13 +6384,7 @@ exc->func_move( exc, &exc->zp0, A, B ); } } - else - if ( exc->pedantic_hinting ) - exc->error = FT_THROW( Invalid_Reference ); } - - Fail: - exc->new_top = exc->args; } @@ -6602,28 +6398,50 @@ Ins_DELTAC( TT_ExecContext exc, FT_Long* args ) { - FT_ULong nump, k; - FT_ULong A, C, P; - FT_Long B; + FT_Long nump; + FT_ULong A; + FT_Long B, P, F; - P = (FT_ULong)exc->func_cur_ppem( exc ); - nump = (FT_ULong)args[0]; + nump = args[0]; /* signed value for convenience */ - for ( k = 1; k <= nump; k++ ) + if ( nump < 0 || nump > exc->new_top / 2 ) { - if ( exc->args < 2 ) - { - if ( exc->pedantic_hinting ) - exc->error = FT_THROW( Too_Few_Arguments ); - exc->args = 0; - goto Fail; - } + if ( exc->pedantic_hinting ) + exc->error = FT_THROW( Too_Few_Arguments ); - exc->args -= 2; + nump = exc->new_top / 2; + } - A = (FT_ULong)exc->stack[exc->args + 1]; - B = exc->stack[exc->args]; + exc->new_top -= 2 * nump; + + P = exc->func_cur_ppem( exc ) - exc->GS.delta_base; + + switch ( exc->opcode ) + { + case 0x73: + break; + + case 0x74: + P -= 16; + break; + + case 0x75: + P -= 32; + break; + } + + /* check applicable range of adjusted ppem */ + if ( P & ~0xF ) /* P < 0 || P > 15 */ + return; + + P <<= 4; + F = 1L << ( 6 - exc->GS.delta_shift ); + + while ( nump-- ) + { + A = (FT_ULong)*(--args); + B = *(--args); if ( BOUNDSL( A, exc->cvtSize ) ) { @@ -6635,38 +6453,17 @@ } else { - C = ( (FT_ULong)B & 0xF0 ) >> 4; - - switch ( exc->opcode ) + if ( ( B & 0xF0 ) == P ) { - case 0x73: - break; - - case 0x74: - C += 16; - break; - - case 0x75: - C += 32; - break; - } - - C += exc->GS.delta_base; - - if ( P == C ) - { - B = ( (FT_ULong)B & 0xF ) - 8; + B = ( B & 0xF ) - 8; if ( B >= 0 ) B++; - B *= 1L << ( 6 - exc->GS.delta_shift ); + B *= F; exc->func_move_cvt( exc, A, B ); } } } - - Fail: - exc->new_top = exc->args; } @@ -6736,7 +6533,7 @@ /* Otherwise, instructions may behave weirdly and rendering results */ /* may differ between v35 and v40 mode, e.g., in `Times New Roman */ /* Bold Italic'. */ - if ( SUBPIXEL_HINTING_MINIMAL && exc->subpixel_hinting_lean ) + if ( SUBPIXEL_HINTING_MINIMAL && exc->mode != FT_RENDER_MODE_MONO ) { /********************************* * HINTING FOR SUBPIXEL @@ -6753,7 +6550,7 @@ * Selector Bit: 8 * Return Bit(s): 15 */ - if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd_lean ) + if ( ( args[0] & 256 ) != 0 && exc->mode == FT_RENDER_MODE_LCD_V ) K |= 1 << 15; /********************************* @@ -6774,7 +6571,7 @@ * The only smoothing method FreeType supports unless someone sets * FT_LOAD_TARGET_MONO. */ - if ( ( args[0] & 2048 ) != 0 && exc->subpixel_hinting_lean ) + if ( ( args[0] & 2048 ) != 0 && exc->mode != FT_RENDER_MODE_MONO ) K |= 1 << 18; /********************************* @@ -6786,7 +6583,10 @@ * Grayscale rendering is what FreeType does anyway unless someone * sets FT_LOAD_TARGET_MONO or FT_LOAD_TARGET_LCD(_V) */ - if ( ( args[0] & 4096 ) != 0 && exc->grayscale_cleartype ) + if ( ( args[0] & 4096 ) != 0 && + exc->mode != FT_RENDER_MODE_MONO && + exc->mode != FT_RENDER_MODE_LCD && + exc->mode != FT_RENDER_MODE_LCD_V ) K |= 1 << 19; } #endif @@ -6833,6 +6633,8 @@ for ( i = 0; i < num_axes; i++ ) args[i] = 0; } + + exc->new_top += num_axes; } @@ -6883,7 +6685,6 @@ Ins_Goto_CodeRange( exc, def->range, def->start ); - exc->step_ins = FALSE; return; } } @@ -6928,96 +6729,22 @@ TT_RunIns( void* exec ) { TT_ExecContext exc = (TT_ExecContext)exec; + FT_ULong ins_counter = 0; - FT_ULong ins_counter = 0; /* executed instructions counter */ - FT_ULong num_twilight_points; - FT_UShort i; - - - /* We restrict the number of twilight points to a reasonable, */ - /* heuristic value to avoid slow execution of malformed bytecode. */ - num_twilight_points = FT_MAX( 30, - 2 * ( exc->pts.n_points + exc->cvtSize ) ); - if ( exc->twilight.n_points > num_twilight_points ) - { - if ( num_twilight_points > 0xFFFFU ) - num_twilight_points = 0xFFFFU; - - FT_TRACE5(( "TT_RunIns: Resetting number of twilight points\n" )); - FT_TRACE5(( " from %d to the more reasonable value %ld\n", - exc->twilight.n_points, - num_twilight_points )); - exc->twilight.n_points = (FT_UShort)num_twilight_points; - } - - /* Set up loop detectors. We restrict the number of LOOPCALL loops */ - /* and the number of JMPR, JROT, and JROF calls with a negative */ - /* argument to values that depend on various parameters like the */ - /* size of the CVT table or the number of points in the current */ - /* glyph (if applicable). */ - /* */ - /* The idea is that in real-world bytecode you either iterate over */ - /* all CVT entries (in the `prep' table), or over all points (or */ - /* contours, in the `glyf' table) of a glyph, and such iterations */ - /* don't happen very often. */ - exc->loopcall_counter = 0; - exc->neg_jump_counter = 0; - - /* The maximum values are heuristic. */ - if ( exc->pts.n_points ) - exc->loopcall_counter_max = FT_MAX( 50, - 10 * exc->pts.n_points ) + - FT_MAX( 50, - exc->cvtSize / 10 ); - else - exc->loopcall_counter_max = 300 + 22 * exc->cvtSize; - - /* as a protection against an unreasonable number of CVT entries */ - /* we assume at most 100 control values per glyph for the counter */ - if ( exc->loopcall_counter_max > - 100 * (FT_ULong)exc->face->root.num_glyphs ) - exc->loopcall_counter_max = 100 * (FT_ULong)exc->face->root.num_glyphs; - - FT_TRACE5(( "TT_RunIns: Limiting total number of loops in LOOPCALL" - " to %ld\n", exc->loopcall_counter_max )); - - exc->neg_jump_counter_max = exc->loopcall_counter_max; - FT_TRACE5(( "TT_RunIns: Limiting total number of backward jumps" - " to %ld\n", exc->neg_jump_counter_max )); - - /* set PPEM and CVT functions */ - exc->tt_metrics.ratio = 0; - if ( exc->metrics.x_ppem != exc->metrics.y_ppem ) - { - /* non-square pixels, use the stretched routines */ - exc->func_cur_ppem = Current_Ppem_Stretched; - exc->func_read_cvt = Read_CVT_Stretched; - exc->func_write_cvt = Write_CVT_Stretched; - exc->func_move_cvt = Move_CVT_Stretched; - } - else - { - /* square pixels, use normal routines */ - exc->func_cur_ppem = Current_Ppem; - exc->func_read_cvt = Read_CVT; - exc->func_write_cvt = Write_CVT; - exc->func_move_cvt = Move_CVT; - } - - exc->iniRange = exc->curRange; - - Compute_Funcs( exc ); - Compute_Round( exc, (FT_Byte)exc->GS.round_state ); - - /* These flags cancel execution of some opcodes after IUP is called */ -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - exc->iupx_called = FALSE; - exc->iupy_called = FALSE; -#endif do { + /* increment instruction counter and check if we didn't */ + /* run this program for too long (e.g. infinite loops). */ + if ( ++ins_counter > TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES ) + { + exc->error = FT_THROW( Execution_Too_Long ); + goto LErrorLabel_; + } + + exc->error = FT_Err_Ok; exc->opcode = exc->code[exc->IP]; + exc->length = 1; #ifdef FT_DEBUG_LEVEL_TRACE if ( ft_trace_levels[trace_ttinterp] >= 6 ) @@ -7041,17 +6768,6 @@ } #endif /* FT_DEBUG_LEVEL_TRACE */ - if ( ( exc->length = opcode_length[exc->opcode] ) < 0 ) - { - if ( exc->IP + 1 >= exc->codeSize ) - goto LErrorCodeOverflow_; - - exc->length = 2 - exc->length * exc->code[exc->IP + 1]; - } - - if ( exc->IP + exc->length > exc->codeSize ) - goto LErrorCodeOverflow_; - /* First, let's check for empty stack and overflow */ exc->args = exc->top - ( Pop_Push_Count[exc->opcode] >> 4 ); @@ -7059,6 +6775,9 @@ /* One can also interpret it as the index of the last argument. */ if ( exc->args < 0 ) { + FT_UShort i; + + if ( exc->pedantic_hinting ) { exc->error = FT_THROW( Too_Few_Arguments ); @@ -7071,21 +6790,7 @@ exc->args = 0; } -#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - if ( exc->opcode == 0x91 ) - { - /* this is very special: GETVARIATION returns */ - /* a variable number of arguments */ - - /* it is the job of the application to `activate' GX handling, */ - /* that is, calling any of the GX API functions on the current */ - /* font to select a variation instance */ - if ( exc->face->blend ) - exc->new_top = exc->args + exc->face->blend->num_axis; - } - else -#endif - exc->new_top = exc->args + ( Pop_Push_Count[exc->opcode] & 15 ); + exc->new_top = exc->args + ( Pop_Push_Count[exc->opcode] & 15 ); /* `new_top' is the new top of the stack, after the instruction's */ /* execution. `top' will be set to `new_top' after the `switch' */ @@ -7096,9 +6801,6 @@ goto LErrorLabel_; } - exc->step_ins = TRUE; - exc->error = FT_Err_Ok; - { FT_Long* args = exc->stack + exc->args; FT_Byte opcode = exc->opcode; @@ -7281,7 +6983,7 @@ case 0x32: /* SHP */ case 0x33: /* SHP */ - Ins_SHP( exc ); + Ins_SHP( exc, args ); break; case 0x34: /* SHC */ @@ -7299,7 +7001,7 @@ break; case 0x39: /* IP */ - Ins_IP( exc ); + Ins_IP( exc, args ); break; case 0x3A: /* MSIRP */ @@ -7308,7 +7010,7 @@ break; case 0x3C: /* AlignRP */ - Ins_ALIGNRP( exc ); + Ins_ALIGNRP( exc, args ); break; case 0x3D: /* RTDG */ @@ -7544,7 +7246,7 @@ break; case 0x80: /* FLIPPT */ - Ins_FLIPPT( exc ); + Ins_FLIPPT( exc, args ); break; case 0x81: /* FLIPRGON */ @@ -7642,13 +7344,13 @@ { switch ( exc->error ) { - /* looking for redefined instructions */ case FT_ERR( Invalid_Opcode ): { TT_DefRecord* def = exc->IDefs; TT_DefRecord* limit = FT_OFFSET( def, exc->numIDefs ); + /* looking for redefined instructions */ for ( ; def < limit; def++ ) { if ( def->active && exc->opcode == (FT_Byte)def->opc ) @@ -7678,37 +7380,15 @@ } } } - - exc->error = FT_THROW( Invalid_Opcode ); - goto LErrorLabel_; - -#if 0 - break; /* Unreachable code warning suppression. */ - /* Leave to remind in case a later change the editor */ - /* to consider break; */ -#endif + FALL_THROUGH; default: goto LErrorLabel_; - -#if 0 - break; -#endif } } exc->top = exc->new_top; - - if ( exc->step_ins ) - exc->IP += exc->length; - - /* increment instruction counter and check if we didn't */ - /* run this program for too long (e.g. infinite loops). */ - if ( ++ins_counter > TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES ) - { - exc->error = FT_THROW( Execution_Too_Long ); - goto LErrorLabel_; - } + exc->IP += exc->length; LSuiteLabel_: if ( exc->IP >= exc->codeSize ) @@ -7724,15 +7404,12 @@ } while ( !exc->instruction_trap ); LNo_Error_: - FT_TRACE4(( " %ld instruction%s executed\n", + FT_TRACE4(( " %lu instruction%s executed\n", ins_counter, ins_counter == 1 ? "" : "s" )); return FT_Err_Ok; - LErrorCodeOverflow_: - exc->error = FT_THROW( Code_Overflow ); - LErrorLabel_: if ( exc->error && !exc->instruction_trap ) FT_TRACE1(( " The interpreter returned error 0x%x\n", exc->error )); @@ -7740,6 +7417,126 @@ return exc->error; } + + /************************************************************************** + * + * @Function: + * TT_Run_Context + * + * @Description: + * Executes one or more instructions in the execution context. + * + * @Input: + * exec :: + * A handle to the target execution context. + * + * @Return: + * TrueType error code. 0 means success. + */ + FT_LOCAL_DEF( FT_Error ) + TT_Run_Context( TT_ExecContext exec, + TT_Size size ) + { + FT_ULong num_twilight_points; + + + exec->zp0 = exec->pts; + exec->zp1 = exec->pts; + exec->zp2 = exec->pts; + + /* We restrict the number of twilight points to a reasonable, */ + /* heuristic value to avoid slow execution of malformed bytecode. */ + /* The selected value is large enough to support fonts hinted */ + /* with `ttfautohint`, which uses twilight points to store */ + /* vertical coordinates of (auto-hinter) segments. */ + num_twilight_points = FT_MAX( 30, + 2 * ( exec->pts.n_points + exec->cvtSize ) ); + if ( exec->twilight.n_points > num_twilight_points ) + { + if ( num_twilight_points > 0xFFFFU ) + num_twilight_points = 0xFFFFU; + + FT_TRACE5(( "TT_RunIns: Resetting number of twilight points\n" )); + FT_TRACE5(( " from %d to the more reasonable value %lu\n", + exec->twilight.n_points, + num_twilight_points )); + exec->twilight.n_points = (FT_UShort)num_twilight_points; + } + + /* Set up loop detectors. We restrict the number of LOOPCALL loops */ + /* and the number of JMPR, JROT, and JROF calls with a negative */ + /* argument to values that depend on various parameters like the */ + /* size of the CVT table or the number of points in the current */ + /* glyph (if applicable). */ + /* */ + /* The idea is that in real-world bytecode you either iterate over */ + /* all CVT entries (in the `prep' table), or over all points (or */ + /* contours, in the `glyf' table) of a glyph, and such iterations */ + /* don't happen very often. */ + exec->loopcall_counter = 0; + exec->neg_jump_counter = 0; + + /* The maximum values are heuristic. */ + if ( exec->pts.n_points ) + exec->loopcall_counter_max = FT_MAX( 50, + 10 * exec->pts.n_points ) + + FT_MAX( 50, + exec->cvtSize / 10 ); + else + exec->loopcall_counter_max = 300 + 22 * exec->cvtSize; + + /* as a protection against an unreasonable number of CVT entries */ + /* we assume at most 100 control values per glyph for the counter */ + if ( exec->loopcall_counter_max > + 100 * (FT_ULong)exec->face->root.num_glyphs ) + exec->loopcall_counter_max = 100 * (FT_ULong)exec->face->root.num_glyphs; + + FT_TRACE5(( "TT_RunIns: Limiting total number of loops in LOOPCALL" + " to %lu\n", exec->loopcall_counter_max )); + + exec->neg_jump_counter_max = exec->loopcall_counter_max; + FT_TRACE5(( "TT_RunIns: Limiting total number of backward jumps" + " to %lu\n", exec->neg_jump_counter_max )); + + /* set PPEM and CVT functions */ + if ( exec->metrics.x_ppem != exec->metrics.y_ppem ) + { + /* non-square pixels, use the stretched routines */ + exec->func_cur_ppem = Current_Ppem_Stretched; + exec->func_read_cvt = Read_CVT_Stretched; + exec->func_write_cvt = Write_CVT_Stretched; + exec->func_move_cvt = Move_CVT_Stretched; + } + else + { + /* square pixels, use normal routines */ + exec->func_cur_ppem = Current_Ppem; + exec->func_read_cvt = Read_CVT; + exec->func_write_cvt = Write_CVT; + exec->func_move_cvt = Move_CVT; + } + + /* reset graphics state */ + exec->GS = size->GS; + exec->func_round = (TT_Round_Func)Round_To_Grid; + Compute_Funcs( exec ); + +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + /* Reset IUP tracking bits in the backward compatibility mode. */ + /* See `ttinterp.h' for details. */ + exec->backward_compatibility &= ~0x3; +#endif + + /* some glyphs leave something on the stack, */ + /* so we clean it before a new execution. */ + exec->top = 0; + exec->callTop = 0; + + exec->instruction_trap = FALSE; + + return exec->interpreter( exec ); + } + #else /* !TT_USE_BYTECODE_INTERPRETER */ /* ANSI C doesn't like empty source files */ diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h index 4f1a9bbc679..2e69d191890 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttinterp.h @@ -4,7 +4,7 @@ * * TrueType bytecode interpreter (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -39,6 +39,60 @@ FT_BEGIN_HEADER #define TT_Round_Super_45 7 + /************************************************************************** + * + * EXECUTION SUBTABLES + * + * These sub-tables relate to instruction execution. + * + */ + + +#define TT_MAX_CODE_RANGES 3 + + + /************************************************************************** + * + * There can only be 3 active code ranges at once: + * - the Font Program + * - the CVT Program + * - a glyph's instructions set + */ + typedef enum TT_CodeRange_Tag_ + { + tt_coderange_none = 0, + tt_coderange_font, + tt_coderange_cvt, + tt_coderange_glyph + + } TT_CodeRange_Tag; + + + typedef struct TT_CodeRange_ + { + FT_Byte* base; + FT_Long size; + + } TT_CodeRange; + + typedef TT_CodeRange TT_CodeRangeTable[TT_MAX_CODE_RANGES]; + + + /************************************************************************** + * + * Defines a function/instruction definition record. + */ + typedef struct TT_DefRecord_ + { + FT_Int range; /* in which code range is it located? */ + FT_Long start; /* where does it start? */ + FT_Long end; /* where does it end? */ + FT_UInt opc; /* function #, or instruction code */ + FT_Bool active; /* is it active? */ + + } TT_DefRecord, *TT_DefArray; + + /************************************************************************** * * Function types used by the interpreter, depending on various modes @@ -51,7 +105,7 @@ FT_BEGIN_HEADER typedef FT_F26Dot6 (*TT_Round_Func)( TT_ExecContext exc, FT_F26Dot6 distance, - FT_Int color ); + FT_F26Dot6 compensation ); /* Point displacement along the freedom vector routine */ typedef void @@ -111,12 +165,13 @@ FT_BEGIN_HEADER TT_Face face; /* ! */ TT_Size size; /* ! */ FT_Memory memory; + TT_Interpreter interpreter; /* instructions state */ FT_Error error; /* last execution error */ - FT_Long top; /* @ top of exec. stack */ + FT_Long top; /* @! top of exec. stack */ FT_Long stackSize; /* ! size of exec. stack */ FT_Long* stack; /* ! current exec. stack */ @@ -142,11 +197,9 @@ FT_BEGIN_HEADER FT_Long IP; /* current instruction pointer */ FT_Long codeSize; /* size of current range */ - FT_Byte opcode; /* current opcode */ - FT_Int length; /* length of current opcode */ + FT_Byte opcode; /* current opcode */ + FT_Int length; /* opcode length or increment */ - FT_Bool step_ins; /* true if the interpreter must */ - /* increment IP after ins. exec */ FT_ULong cvtSize; /* ! */ FT_Long* cvt; /* ! */ FT_ULong glyfCvtSize; @@ -166,9 +219,9 @@ FT_BEGIN_HEADER FT_UInt maxFunc; /* ! maximum function index */ FT_UInt maxIns; /* ! maximum instruction index */ - FT_Int callTop, /* @ top of call stack during execution */ - callSize; /* size of call stack */ - TT_CallStack callStack; /* call stack */ + FT_Int callTop, /* @! top of call stack during execution */ + callSize; /* size of call stack */ + TT_CallStack callStack; /* call stack */ FT_UShort maxPoints; /* capacity of this context's `pts' */ FT_Short maxContours; /* record, expressed in points and */ @@ -189,16 +242,14 @@ FT_BEGIN_HEADER FT_Bool instruction_trap; /* ! If `True', the interpreter */ /* exits after each instruction */ - TT_GraphicsState default_GS; /* graphics state resulting from */ - /* the prep program */ FT_Bool is_composite; /* true if the glyph is composite */ FT_Bool pedantic_hinting; /* true if pedantic interpretation */ /* latest interpreter additions */ - FT_Long F_dot_P; /* dot product of freedom and projection */ - /* vectors */ - TT_Round_Func func_round; /* current rounding function */ + TT_Round_Func func_round; /* current rounding function */ + + FT_Vector moveVector; /* "projected" freedom vector */ TT_Project_Func func_project, /* current projection function */ func_dualproj, /* current dual proj. function */ @@ -327,34 +378,13 @@ FT_BEGIN_HEADER * */ - /* Using v40 implies subpixel hinting, unless FT_RENDER_MODE_MONO has been - * requested. Used to detect interpreter */ - /* version switches. `_lean' to differentiate from the Infinality */ - /* `subpixel_hinting', which is managed differently. */ - FT_Bool subpixel_hinting_lean; + /* Activate backward compatibility (bit 2) and track IUP (bits 0-1). */ + /* If this is zero, it means that the interpreter is either in v35 */ + /* or in native ClearType mode. */ + FT_Int backward_compatibility; - /* Long side of a LCD subpixel is vertical (e.g., screen is rotated). */ - /* `_lean' to differentiate from the Infinality `vertical_lcd', which */ - /* is managed differently. */ - FT_Bool vertical_lcd_lean; + FT_Render_Mode mode; /* target render mode */ - /* Default to backward compatibility mode in v40 interpreter. If */ - /* this is false, it implies the interpreter is in v35 or in native */ - /* ClearType mode. */ - FT_Bool backward_compatibility; - - /* Useful for detecting and denying post-IUP trickery that is usually */ - /* used to fix pixel patterns (`superhinting'). */ - FT_Bool iupx_called; - FT_Bool iupy_called; - - /* ClearType hinting and grayscale rendering, as used by Universal */ - /* Windows Platform apps (Windows 8 and above). Like the standard */ - /* colorful ClearType mode, it utilizes a vastly increased virtual */ - /* resolution on the x axis. Different from bi-level hinting and */ - /* grayscale rendering, the old mode from Win9x days that roughly */ - /* adheres to the physical pixel grid on both axes. */ - FT_Bool grayscale_cleartype; #endif /* TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL */ /* We maintain two counters (in addition to the instruction counter) */ @@ -371,22 +401,15 @@ FT_BEGIN_HEADER extern const TT_GraphicsState tt_default_graphics_state; -#ifdef TT_USE_BYTECODE_INTERPRETER - FT_LOCAL( void ) - TT_Goto_CodeRange( TT_ExecContext exec, - FT_Int range, - FT_Long IP ); - FT_LOCAL( void ) TT_Set_CodeRange( TT_ExecContext exec, FT_Int range, - void* base, + FT_Byte* base, FT_Long length ); FT_LOCAL( void ) TT_Clear_CodeRange( TT_ExecContext exec, FT_Int range ); -#endif /* TT_USE_BYTECODE_INTERPRETER */ /************************************************************************** @@ -413,7 +436,6 @@ FT_BEGIN_HEADER TT_New_Context( TT_Driver driver ); -#ifdef TT_USE_BYTECODE_INTERPRETER FT_LOCAL( void ) TT_Done_Context( TT_ExecContext exec ); @@ -424,11 +446,11 @@ FT_BEGIN_HEADER FT_LOCAL( void ) TT_Save_Context( TT_ExecContext exec, - TT_Size ins ); + TT_Size size ); FT_LOCAL( FT_Error ) - TT_Run_Context( TT_ExecContext exec ); -#endif /* TT_USE_BYTECODE_INTERPRETER */ + TT_Run_Context( TT_ExecContext exec, + TT_Size size ); /************************************************************************** diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c index d0ac3181204..fd0c0ad14f6 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.c @@ -4,7 +4,7 @@ * * Objects manager (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -67,23 +67,13 @@ * A pointer to the target glyph zone. */ FT_LOCAL_DEF( void ) - tt_glyphzone_done( TT_GlyphZone zone ) + tt_glyphzone_done( FT_Memory memory, + TT_GlyphZone zone ) { - FT_Memory memory = zone->memory; + FT_FREE( zone->org ); - - if ( memory ) - { - FT_FREE( zone->contours ); - FT_FREE( zone->tags ); - FT_FREE( zone->cur ); - FT_FREE( zone->org ); - FT_FREE( zone->orus ); - - zone->max_points = zone->n_points = 0; - zone->max_contours = zone->n_contours = 0; - zone->memory = NULL; - } + zone->n_points = 0; + zone->n_contours = 0; } @@ -119,23 +109,22 @@ TT_GlyphZone zone ) { FT_Error error; + FT_Long size = 3 * maxPoints * sizeof ( FT_Vector ) + + maxContours * sizeof ( FT_UShort ) + + maxPoints * sizeof ( FT_Byte ); - FT_ZERO( zone ); - zone->memory = memory; - - if ( FT_NEW_ARRAY( zone->org, maxPoints ) || - FT_NEW_ARRAY( zone->cur, maxPoints ) || - FT_NEW_ARRAY( zone->orus, maxPoints ) || - FT_NEW_ARRAY( zone->tags, maxPoints ) || - FT_NEW_ARRAY( zone->contours, maxContours ) ) + if ( !FT_ALLOC( zone->org, size ) ) { - tt_glyphzone_done( zone ); - } - else - { - zone->max_points = maxPoints; - zone->max_contours = maxContours; + zone->n_points = maxPoints; + zone->n_contours = maxContours; + + zone->cur = zone->org + maxPoints; + zone->orus = zone->cur + maxPoints; + zone->contours = (FT_UShort*)( zone->orus + maxPoints ); + zone->tags = (FT_Byte*)( zone->contours + maxContours ); + + zone->first_point = 0; } return error; @@ -488,8 +477,7 @@ int j, k; - FT_MEM_SET( num_matched_ids, 0, - sizeof ( int ) * TRICK_SFNT_IDS_NUM_FACES ); + FT_ARRAY_ZERO( num_matched_ids, TRICK_SFNT_IDS_NUM_FACES ); has_cvt = FALSE; has_fpgm = FALSE; has_prep = FALSE; @@ -787,7 +775,7 @@ FT_UInt instance_index = (FT_UInt)face_index >> 16; - if ( FT_HAS_MULTIPLE_MASTERS( ttface ) ) + if ( instance_index && FT_HAS_MULTIPLE_MASTERS( ttface ) ) { error = FT_Set_Named_Instance( ttface, instance_index ); if ( error ) @@ -885,75 +873,40 @@ * size :: * A handle to the size object. * - * pedantic :: - * Set if bytecode execution should be pedantic. - * * @Return: * FreeType error code. 0 means success. */ FT_LOCAL_DEF( FT_Error ) - tt_size_run_fpgm( TT_Size size, - FT_Bool pedantic ) + tt_size_run_fpgm( TT_Size size ) { TT_Face face = (TT_Face)size->root.face; - TT_ExecContext exec; + TT_ExecContext exec = size->context; FT_Error error; - exec = size->context; - error = TT_Load_Context( exec, face, size ); if ( error ) return error; - exec->callTop = 0; - exec->top = 0; - - exec->period = 64; - exec->phase = 0; - exec->threshold = 0; - - exec->instruction_trap = FALSE; - exec->F_dot_P = 0x4000L; - - exec->pedantic_hinting = pedantic; - - { - FT_Size_Metrics* size_metrics = &exec->metrics; - TT_Size_Metrics* tt_metrics = &exec->tt_metrics; - - - size_metrics->x_ppem = 0; - size_metrics->y_ppem = 0; - size_metrics->x_scale = 0; - size_metrics->y_scale = 0; - - tt_metrics->ppem = 0; - tt_metrics->scale = 0; - tt_metrics->ratio = 0x10000L; - } - - /* allow font program execution */ - TT_Set_CodeRange( exec, - tt_coderange_font, - face->font_program, - (FT_Long)face->font_program_size ); - /* disable CVT and glyph programs coderange */ TT_Clear_CodeRange( exec, tt_coderange_cvt ); TT_Clear_CodeRange( exec, tt_coderange_glyph ); if ( face->font_program_size > 0 ) { - TT_Goto_CodeRange( exec, tt_coderange_font, 0 ); + /* allow font program execution */ + TT_Set_CodeRange( exec, + tt_coderange_font, + face->font_program, + (FT_Long)face->font_program_size ); + + exec->pts.n_points = 0; + exec->pts.n_contours = 0; FT_TRACE4(( "Executing `fpgm' table.\n" )); - error = face->interpreter( exec ); -#ifdef FT_DEBUG_LEVEL_TRACE - if ( error ) - FT_TRACE4(( " interpretation failed with error code 0x%x\n", - error )); -#endif + error = TT_Run_Context( exec, size ); + FT_TRACE4(( error ? " failed (error code 0x%x)\n" : "", + error )); } else error = FT_Err_Ok; @@ -979,212 +932,146 @@ * size :: * A handle to the size object. * - * pedantic :: - * Set if bytecode execution should be pedantic. - * * @Return: * FreeType error code. 0 means success. */ FT_LOCAL_DEF( FT_Error ) - tt_size_run_prep( TT_Size size, - FT_Bool pedantic ) + tt_size_run_prep( TT_Size size ) { TT_Face face = (TT_Face)size->root.face; - TT_ExecContext exec; + TT_ExecContext exec = size->context; FT_Error error; FT_UInt i; - /* Scale the cvt values to the new ppem. */ - /* By default, we use the y ppem value for scaling. */ - FT_TRACE6(( "CVT values:\n" )); - for ( i = 0; i < size->cvt_size; i++ ) - { - /* Unscaled CVT values are already stored in 26.6 format. */ - /* Note that this scaling operation is very sensitive to rounding; */ - /* the integer division by 64 must be applied to the first argument. */ - size->cvt[i] = FT_MulFix( face->cvt[i] / 64, size->ttmetrics.scale ); - FT_TRACE6(( " %3d: %f (%f)\n", - i, (double)face->cvt[i] / 64, (double)size->cvt[i] / 64 )); - } - FT_TRACE6(( "\n" )); + /* set default GS, twilight points, and storage */ + /* before CV program can modify them. */ + size->GS = tt_default_graphics_state; - exec = size->context; + /* all twilight points are originally zero */ + FT_ARRAY_ZERO( size->twilight.org, size->twilight.n_points ); + FT_ARRAY_ZERO( size->twilight.cur, size->twilight.n_points ); error = TT_Load_Context( exec, face, size ); if ( error ) return error; - exec->callTop = 0; - exec->top = 0; + /* clear storage area */ + FT_ARRAY_ZERO( exec->storage, exec->storeSize ); - exec->instruction_trap = FALSE; - - exec->pedantic_hinting = pedantic; - - TT_Set_CodeRange( exec, - tt_coderange_cvt, - face->cvt_program, - (FT_Long)face->cvt_program_size ); + /* Scale the cvt values to the new ppem. */ + /* By default, we use the y ppem value for scaling. */ + FT_TRACE6(( "CVT values:\n" )); + for ( i = 0; i < exec->cvtSize; i++ ) + { + /* Unscaled CVT values are already stored in 26.6 format. */ + /* Note that this scaling operation is very sensitive to rounding; */ + /* the integer division by 64 must be applied to the first argument. */ + exec->cvt[i] = FT_MulFix( face->cvt[i] / 64, size->ttmetrics.scale ); + FT_TRACE6(( " %3u: %f (%f)\n", + i, (double)face->cvt[i] / 64, (double)exec->cvt[i] / 64 )); + } + FT_TRACE6(( "\n" )); TT_Clear_CodeRange( exec, tt_coderange_glyph ); if ( face->cvt_program_size > 0 ) { - TT_Goto_CodeRange( exec, tt_coderange_cvt, 0 ); + /* allow CV program execution */ + TT_Set_CodeRange( exec, + tt_coderange_cvt, + face->cvt_program, + (FT_Long)face->cvt_program_size ); + + exec->pts.n_points = 0; + exec->pts.n_contours = 0; FT_TRACE4(( "Executing `prep' table.\n" )); - error = face->interpreter( exec ); -#ifdef FT_DEBUG_LEVEL_TRACE - if ( error ) - FT_TRACE4(( " interpretation failed with error code 0x%x\n", - error )); -#endif + error = TT_Run_Context( exec, size ); + FT_TRACE4(( error ? " failed (error code 0x%x)\n" : "", + error )); } else error = FT_Err_Ok; size->cvt_ready = error; - /* UNDOCUMENTED! The MS rasterizer doesn't allow the following */ - /* graphics state variables to be modified by the CVT program. */ - - exec->GS.dualVector.x = 0x4000; - exec->GS.dualVector.y = 0; - exec->GS.projVector.x = 0x4000; - exec->GS.projVector.y = 0x0; - exec->GS.freeVector.x = 0x4000; - exec->GS.freeVector.y = 0x0; - - exec->GS.rp0 = 0; - exec->GS.rp1 = 0; - exec->GS.rp2 = 0; - - exec->GS.gep0 = 1; - exec->GS.gep1 = 1; - exec->GS.gep2 = 1; - - exec->GS.loop = 1; - - /* save as default graphics state */ - size->GS = exec->GS; - - TT_Save_Context( exec, size ); + if ( !error ) + TT_Save_Context( exec, size ); return error; } static void - tt_size_done_bytecode( FT_Size ftsize ) + tt_size_done_bytecode( TT_Size size ) { - TT_Size size = (TT_Size)ftsize; - TT_Face face = (TT_Face)ftsize->face; - FT_Memory memory = face->root.memory; + FT_Memory memory = size->root.face->memory; + TT_ExecContext exec = size->context; - if ( size->context ) + + if ( exec ) { - TT_Done_Context( size->context ); + FT_FREE( exec->stack ); + FT_FREE( exec->FDefs ); + + TT_Done_Context( exec ); size->context = NULL; } - FT_FREE( size->cvt ); - size->cvt_size = 0; - - /* free storage area */ - FT_FREE( size->storage ); - size->storage_size = 0; - /* twilight zone */ - tt_glyphzone_done( &size->twilight ); - - FT_FREE( size->function_defs ); - FT_FREE( size->instruction_defs ); - - size->num_function_defs = 0; - size->max_function_defs = 0; - size->num_instruction_defs = 0; - size->max_instruction_defs = 0; - - size->max_func = 0; - size->max_ins = 0; - - size->bytecode_ready = -1; - size->cvt_ready = -1; + tt_glyphzone_done( memory, &size->twilight ); } /* Initialize bytecode-related fields in the size object. */ /* We do this only if bytecode interpretation is really needed. */ - static FT_Error - tt_size_init_bytecode( FT_Size ftsize, + FT_LOCAL_DEF( FT_Error ) + tt_size_init_bytecode( TT_Size size, FT_Bool pedantic ) { FT_Error error; - TT_Size size = (TT_Size)ftsize; - TT_Face face = (TT_Face)ftsize->face; - FT_Memory memory = face->root.memory; + TT_Face face = (TT_Face)size->root.face; + FT_Memory memory = size->root.face->memory; FT_UShort n_twilight; TT_MaxProfile* maxp = &face->max_profile; + TT_ExecContext exec; - /* clean up bytecode related data */ - FT_FREE( size->function_defs ); - FT_FREE( size->instruction_defs ); - FT_FREE( size->cvt ); - FT_FREE( size->storage ); + exec = TT_New_Context( (TT_Driver)face->root.driver ); + if ( !exec ) + return FT_THROW( Could_Not_Find_Context ); - if ( size->context ) - TT_Done_Context( size->context ); - tt_glyphzone_done( &size->twilight ); + exec->pedantic_hinting = pedantic; - size->bytecode_ready = -1; - size->cvt_ready = -1; + exec->maxFDefs = maxp->maxFunctionDefs; + exec->maxIDefs = maxp->maxInstructionDefs; - size->context = TT_New_Context( (TT_Driver)face->root.driver ); - - size->max_function_defs = maxp->maxFunctionDefs; - size->max_instruction_defs = maxp->maxInstructionDefs; - - size->num_function_defs = 0; - size->num_instruction_defs = 0; - - size->max_func = 0; - size->max_ins = 0; - - size->cvt_size = face->cvt_size; - size->storage_size = maxp->maxStorage; - - /* Set default metrics */ - { - TT_Size_Metrics* tt_metrics = &size->ttmetrics; - - - tt_metrics->rotated = FALSE; - tt_metrics->stretched = FALSE; - - /* Set default engine compensation. Value 3 is not described */ - /* in the OpenType specification (as of Mai 2019), but Greg */ - /* says that MS handles it the same as `gray'. */ - /* */ - /* The Apple specification says that the compensation for */ - /* `gray' is always zero. FreeType doesn't do any */ - /* compensation at all. */ - tt_metrics->compensations[0] = 0; /* gray */ - tt_metrics->compensations[1] = 0; /* black */ - tt_metrics->compensations[2] = 0; /* white */ - tt_metrics->compensations[3] = 0; /* zero */ - } - - /* allocate function defs, instruction defs, cvt, and storage area */ - if ( FT_NEW_ARRAY( size->function_defs, size->max_function_defs ) || - FT_NEW_ARRAY( size->instruction_defs, size->max_instruction_defs ) || - FT_NEW_ARRAY( size->cvt, size->cvt_size ) || - FT_NEW_ARRAY( size->storage, size->storage_size ) ) + if ( FT_NEW_ARRAY( exec->FDefs, exec->maxFDefs + exec->maxIDefs ) ) goto Exit; - /* reserve twilight zone */ + exec->IDefs = exec->FDefs + exec->maxFDefs; + + exec->numFDefs = 0; + exec->numIDefs = 0; + + exec->maxFunc = 0; + exec->maxIns = 0; + + /* XXX: We reserve a little more elements on the stack to deal */ + /* with broken fonts like arialbs, courbs, timesbs, etc. */ + exec->stackSize = maxp->maxStackElements + 32; + exec->storeSize = maxp->maxStorage; + exec->cvtSize = face->cvt_size; + + if ( FT_NEW_ARRAY( exec->stack, + exec->stackSize + + (FT_Long)( exec->storeSize + exec->cvtSize ) ) ) + goto Exit; + + /* reserve twilight zone and set GS before fpgm is executed, */ + /* just in case, even though fpgm should not touch them */ n_twilight = maxp->maxTwilightPoints; /* there are 4 phantom points (do we need this?) */ @@ -1194,20 +1081,12 @@ if ( error ) goto Exit; - size->twilight.n_points = n_twilight; + size->GS = tt_default_graphics_state; + size->cvt_ready = -1; + size->context = exec; - size->GS = tt_default_graphics_state; - - /* set `face->interpreter' according to the debug hook present */ - { - FT_Library library = face->root.driver->root.library; - - - face->interpreter = (TT_Interpreter) - library->debug_hooks[FT_DEBUG_HOOK_TRUETYPE]; - if ( !face->interpreter ) - face->interpreter = (TT_Interpreter)TT_RunIns; - } + size->ttmetrics.rotated = FALSE; + size->ttmetrics.stretched = FALSE; /* Fine, now run the font program! */ @@ -1217,62 +1096,16 @@ /* to be executed just once; calling it again is completely useless */ /* and might even lead to extremely slow behaviour if it is malformed */ /* (containing an infinite loop, for example). */ - error = tt_size_run_fpgm( size, pedantic ); + error = tt_size_run_fpgm( size ); return error; Exit: if ( error ) - tt_size_done_bytecode( ftsize ); + tt_size_done_bytecode( size ); return error; } - - FT_LOCAL_DEF( FT_Error ) - tt_size_ready_bytecode( TT_Size size, - FT_Bool pedantic ) - { - FT_Error error = FT_Err_Ok; - - - if ( size->bytecode_ready < 0 ) - error = tt_size_init_bytecode( (FT_Size)size, pedantic ); - else - error = size->bytecode_ready; - - if ( error ) - goto Exit; - - /* rescale CVT when needed */ - if ( size->cvt_ready < 0 ) - { - FT_UShort i; - - - /* all twilight points are originally zero */ - for ( i = 0; i < size->twilight.n_points; i++ ) - { - size->twilight.org[i].x = 0; - size->twilight.org[i].y = 0; - size->twilight.cur[i].x = 0; - size->twilight.cur[i].y = 0; - } - - /* clear storage area */ - for ( i = 0; i < size->storage_size; i++ ) - size->storage[i] = 0; - - size->GS = tt_default_graphics_state; - - error = tt_size_run_prep( size, pedantic ); - } - else - error = size->cvt_ready; - - Exit: - return error; - } - #endif /* TT_USE_BYTECODE_INTERPRETER */ @@ -1300,11 +1133,9 @@ #ifdef TT_USE_BYTECODE_INTERPRETER size->bytecode_ready = -1; - size->cvt_ready = -1; #endif - size->ttmetrics.valid = FALSE; - size->strike_index = 0xFFFFFFFFUL; + size->strike_index = 0xFFFFFFFFUL; return error; } @@ -1325,14 +1156,11 @@ FT_LOCAL_DEF( void ) tt_size_done( FT_Size ttsize ) /* TT_Size */ { - TT_Size size = (TT_Size)ttsize; - - #ifdef TT_USE_BYTECODE_INTERPRETER - tt_size_done_bytecode( ttsize ); + tt_size_done_bytecode( (TT_Size)ttsize ); +#else + FT_UNUSED( ttsize ); #endif - - size->ttmetrics.valid = FALSE; } @@ -1353,21 +1181,13 @@ * function must take `FT_Size` as a result. The passed `FT_Size` is * expected to point to a `TT_Size`. */ - FT_LOCAL_DEF( FT_Error ) + FT_LOCAL_DEF( void ) tt_size_reset_height( FT_Size ft_size ) { TT_Size size = (TT_Size)ft_size; - TT_Face face = (TT_Face)size->root.face; + TT_Face face = (TT_Face)ft_size->face; FT_Size_Metrics* size_metrics = &size->hinted_metrics; - size->ttmetrics.valid = FALSE; - - /* copy the result from base layer */ - *size_metrics = size->root.metrics; - - if ( size_metrics->x_ppem < 1 || size_metrics->y_ppem < 1 ) - return FT_THROW( Invalid_PPem ); - /* This bit flag, if set, indicates that the ppems must be */ /* rounded to integers. Nearly all TrueType fonts have this bit */ /* set, as hinting won't work really well otherwise. */ @@ -1385,10 +1205,6 @@ FT_MulFix( face->root.height, size_metrics->y_scale ) ); } - - size->ttmetrics.valid = TRUE; - - return FT_Err_Ok; } @@ -1408,14 +1224,20 @@ FT_LOCAL_DEF( FT_Error ) tt_size_reset( TT_Size size ) { - FT_Error error; TT_Face face = (TT_Face)size->root.face; FT_Size_Metrics* size_metrics = &size->hinted_metrics; - error = tt_size_reset_height( (FT_Size)size ); - if ( error ) - return error; + /* invalidate the size object first */ + size->ttmetrics.ppem = 0; + + if ( size->root.metrics.x_ppem == 0 || size->root.metrics.y_ppem == 0 ) + return FT_THROW( Invalid_PPem ); + + /* copy the result from base layer */ + *size_metrics = size->root.metrics; + + tt_size_reset_height( (FT_Size)size ); if ( face->header.Flags & 8 ) { diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h index 9c36ca78362..28d6c7d855f 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttobjs.h @@ -4,7 +4,7 @@ * * Objects manager (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -53,6 +53,8 @@ FT_BEGIN_HEADER typedef FT_GlyphSlot TT_GlyphSlot; +#ifdef TT_USE_BYTECODE_INTERPRETER + /************************************************************************** * * @Struct: @@ -67,21 +69,27 @@ FT_BEGIN_HEADER FT_UShort rp1; FT_UShort rp2; + FT_UShort gep0; + FT_UShort gep1; + FT_UShort gep2; + FT_UnitVector dualVector; FT_UnitVector projVector; FT_UnitVector freeVector; FT_Long loop; - FT_F26Dot6 minimum_distance; FT_Int round_state; + FT_F26Dot6 compensation[4]; /* device-specific compensations */ - FT_Bool auto_flip; + /* default values below can be modified by 'fpgm' and 'prep' */ + FT_F26Dot6 minimum_distance; FT_F26Dot6 control_value_cutin; FT_F26Dot6 single_width_cutin; FT_F26Dot6 single_width_value; FT_UShort delta_base; FT_UShort delta_shift; + FT_Bool auto_flip; FT_Byte instruct_control; /* According to Greg Hitchcock from Microsoft, the `scan_control' */ /* variable as documented in the TrueType specification is a 32-bit */ @@ -90,17 +98,12 @@ FT_BEGIN_HEADER FT_Bool scan_control; FT_Int scan_type; - FT_UShort gep0; - FT_UShort gep1; - FT_UShort gep2; - } TT_GraphicsState; -#ifdef TT_USE_BYTECODE_INTERPRETER - FT_LOCAL( void ) - tt_glyphzone_done( TT_GlyphZone zone ); + tt_glyphzone_done( FT_Memory memory, + TT_GlyphZone zone ); FT_LOCAL( FT_Error ) tt_glyphzone_new( FT_Memory memory, @@ -112,73 +115,6 @@ FT_BEGIN_HEADER - /************************************************************************** - * - * EXECUTION SUBTABLES - * - * These sub-tables relate to instruction execution. - * - */ - - -#define TT_MAX_CODE_RANGES 3 - - - /************************************************************************** - * - * There can only be 3 active code ranges at once: - * - the Font Program - * - the CVT Program - * - a glyph's instructions set - */ - typedef enum TT_CodeRange_Tag_ - { - tt_coderange_none = 0, - tt_coderange_font, - tt_coderange_cvt, - tt_coderange_glyph - - } TT_CodeRange_Tag; - - - typedef struct TT_CodeRange_ - { - FT_Byte* base; - FT_Long size; - - } TT_CodeRange; - - typedef TT_CodeRange TT_CodeRangeTable[TT_MAX_CODE_RANGES]; - - - /************************************************************************** - * - * Defines a function/instruction definition record. - */ - typedef struct TT_DefRecord_ - { - FT_Int range; /* in which code range is it located? */ - FT_Long start; /* where does it start? */ - FT_Long end; /* where does it end? */ - FT_UInt opc; /* function #, or instruction code */ - FT_Bool active; /* is it active? */ - - } TT_DefRecord, *TT_DefArray; - - - /************************************************************************** - * - * Subglyph transformation record. - */ - typedef struct TT_Transform_ - { - FT_Fixed xx, xy; /* transformation matrix coefficients */ - FT_Fixed yx, yy; - FT_F26Dot6 ox, oy; /* offsets */ - - } TT_Transform; - - /************************************************************************** * * A note regarding non-squared pixels: @@ -251,13 +187,9 @@ FT_BEGIN_HEADER FT_Long x_ratio; FT_Long y_ratio; - FT_UShort ppem; /* maximum ppem size */ FT_Long ratio; /* current ratio */ FT_Fixed scale; - - FT_F26Dot6 compensations[4]; /* device-specific compensations */ - - FT_Bool valid; + FT_UShort ppem; /* maximum ppem size */ FT_Bool rotated; /* `is the glyph rotated?'-flag */ FT_Bool stretched; /* `is the glyph stretched?'-flag */ @@ -288,27 +220,8 @@ FT_BEGIN_HEADER FT_Long point_size; /* for the `MPS' bytecode instruction */ - FT_UInt num_function_defs; /* number of function definitions */ - FT_UInt max_function_defs; - TT_DefArray function_defs; /* table of function definitions */ - - FT_UInt num_instruction_defs; /* number of ins. definitions */ - FT_UInt max_instruction_defs; - TT_DefArray instruction_defs; /* table of ins. definitions */ - - FT_UInt max_func; - FT_UInt max_ins; - - TT_CodeRangeTable codeRangeTable; - TT_GraphicsState GS; - FT_ULong cvt_size; /* the scaled control value table */ - FT_Long* cvt; - - FT_UShort storage_size; /* The storage area is now part of */ - FT_Long* storage; /* the instance */ - TT_GlyphZoneRec twilight; /* The instance's twilight zone */ TT_ExecContext context; @@ -375,20 +288,18 @@ FT_BEGIN_HEADER #ifdef TT_USE_BYTECODE_INTERPRETER FT_LOCAL( FT_Error ) - tt_size_run_fpgm( TT_Size size, - FT_Bool pedantic ); + tt_size_run_fpgm( TT_Size size ); FT_LOCAL( FT_Error ) - tt_size_run_prep( TT_Size size, - FT_Bool pedantic ); + tt_size_run_prep( TT_Size size ); FT_LOCAL( FT_Error ) - tt_size_ready_bytecode( TT_Size size, - FT_Bool pedantic ); + tt_size_init_bytecode( TT_Size size, + FT_Bool pedantic ); #endif /* TT_USE_BYTECODE_INTERPRETER */ - FT_LOCAL( FT_Error ) + FT_LOCAL( void ) tt_size_reset_height( FT_Size size ); FT_LOCAL( FT_Error ) diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c index 9505b5f179f..827454d8574 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.c @@ -4,7 +4,7 @@ * * TrueType-specific tables loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -110,7 +110,7 @@ if ( face->num_locations != (FT_ULong)face->root.num_glyphs + 1 ) { - FT_TRACE2(( "glyph count mismatch! loca: %ld, maxp: %ld\n", + FT_TRACE2(( "glyph count mismatch! loca: %lu, maxp: %ld\n", face->num_locations - 1, face->root.num_glyphs )); /* we only handle the case where `maxp' gives a larger value */ @@ -151,7 +151,7 @@ face->num_locations = (FT_ULong)face->root.num_glyphs + 1; table_len = new_loca_len; - FT_TRACE2(( "adjusting num_locations to %ld\n", + FT_TRACE2(( "adjusting num_locations to %lu\n", face->num_locations )); } else @@ -225,7 +225,7 @@ if ( pos1 > ttface->glyf_len ) { FT_TRACE1(( "tt_face_get_location:" - " too large offset (0x%08lx) found for glyph index %d,\n", + " too large offset (0x%08lx) found for glyph index %u,\n", pos1, gindex )); FT_TRACE1(( " " " exceeding the end of `glyf' table (0x%08lx)\n", @@ -240,17 +240,17 @@ if ( gindex == ttface->num_locations - 2 ) { FT_TRACE1(( "tt_face_get_location:" - " too large size (%ld bytes) found for glyph index %d,\n", + " too large size (%lu bytes) found for glyph index %u,\n", pos2 - pos1, gindex )); FT_TRACE1(( " " - " truncating at the end of `glyf' table to %ld bytes\n", + " truncating at the end of `glyf' table to %lu bytes\n", ttface->glyf_len - pos1 )); pos2 = ttface->glyf_len; } else { FT_TRACE1(( "tt_face_get_location:" - " too large offset (0x%08lx) found for glyph index %d,\n", + " too large offset (0x%08lx) found for glyph index %u,\n", pos2, gindex + 1 )); FT_TRACE1(( " " " exceeding the end of `glyf' table (0x%08lx)\n", @@ -419,7 +419,7 @@ if ( FT_FRAME_EXTRACT( table_len, face->font_program ) ) goto Exit; - FT_TRACE2(( "loaded, %12ld bytes\n", face->font_program_size )); + FT_TRACE2(( "loaded, %12lu bytes\n", face->font_program_size )); } Exit: @@ -482,7 +482,7 @@ if ( FT_FRAME_EXTRACT( table_len, face->cvt_program ) ) goto Exit; - FT_TRACE2(( "loaded, %12ld bytes\n", face->cvt_program_size )); + FT_TRACE2(( "loaded, %12lu bytes\n", face->cvt_program_size )); } Exit: diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h index bc32b58020c..bb4d3c9cc55 100644 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h +++ b/src/java.desktop/share/native/libfreetype/src/truetype/ttpload.h @@ -4,7 +4,7 @@ * * TrueType-specific tables loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.c b/src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.c deleted file mode 100644 index d811beef0df..00000000000 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.c +++ /dev/null @@ -1,1013 +0,0 @@ -/**************************************************************************** - * - * ttsubpix.c - * - * TrueType Subpixel Hinting. - * - * Copyright (C) 2010-2023 by - * David Turner, Robert Wilhelm, and Werner Lemberg. - * - * This file is part of the FreeType project, and may only be used, - * modified, and distributed under the terms of the FreeType project - * license, LICENSE.TXT. By continuing to use, modify, or distribute - * this file you indicate that you have read the license and - * understand and accept it fully. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "ttsubpix.h" - - -#if defined( TT_USE_BYTECODE_INTERPRETER ) && \ - defined( TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY ) - - /************************************************************************** - * - * These rules affect how the TT Interpreter does hinting, with the - * goal of doing subpixel hinting by (in general) ignoring x moves. - * Some of these rules are fixes that go above and beyond the - * stated techniques in the MS whitepaper on Cleartype, due to - * artifacts in many glyphs. So, these rules make some glyphs render - * better than they do in the MS rasterizer. - * - * "" string or 0 int/char indicates to apply to all glyphs. - * "-" used as dummy placeholders, but any non-matching string works. - * - * Some of this could arguably be implemented in fontconfig, however: - * - * - Fontconfig can't set things on a glyph-by-glyph basis. - * - The tweaks that happen here are very low-level, from an average - * user's point of view and are best implemented in the hinter. - * - * The goal is to make the subpixel hinting techniques as generalized - * as possible across all fonts to prevent the need for extra rules such - * as these. - * - * The rule structure is designed so that entirely new rules can easily - * be added when a new compatibility feature is discovered. - * - * The rule structures could also use some enhancement to handle ranges. - * - * ****************** WORK IN PROGRESS ******************* - */ - - /* These are `classes' of fonts that can be grouped together and used in */ - /* rules below. A blank entry "" is required at the end of these! */ -#define FAMILY_CLASS_RULES_SIZE 7 - - static const SPH_Font_Class FAMILY_CLASS_Rules - [FAMILY_CLASS_RULES_SIZE] = - { - { "MS Legacy Fonts", - { "Aharoni", - "Andale Mono", - "Andalus", - "Angsana New", - "AngsanaUPC", - "Arabic Transparent", - "Arial Black", - "Arial Narrow", - "Arial Unicode MS", - "Arial", - "Batang", - "Browallia New", - "BrowalliaUPC", - "Comic Sans MS", - "Cordia New", - "CordiaUPC", - "Courier New", - "DFKai-SB", - "David Transparent", - "David", - "DilleniaUPC", - "Estrangelo Edessa", - "EucrosiaUPC", - "FangSong_GB2312", - "Fixed Miriam Transparent", - "FrankRuehl", - "Franklin Gothic Medium", - "FreesiaUPC", - "Garamond", - "Gautami", - "Georgia", - "Gulim", - "Impact", - "IrisUPC", - "JasmineUPC", - "KaiTi_GB2312", - "KodchiangUPC", - "Latha", - "Levenim MT", - "LilyUPC", - "Lucida Console", - "Lucida Sans Unicode", - "MS Gothic", - "MS Mincho", - "MV Boli", - "Mangal", - "Marlett", - "Microsoft Sans Serif", - "Mingliu", - "Miriam Fixed", - "Miriam Transparent", - "Miriam", - "Narkisim", - "Palatino Linotype", - "Raavi", - "Rod Transparent", - "Rod", - "Shruti", - "SimHei", - "Simplified Arabic Fixed", - "Simplified Arabic", - "Simsun", - "Sylfaen", - "Symbol", - "Tahoma", - "Times New Roman", - "Traditional Arabic", - "Trebuchet MS", - "Tunga", - "Verdana", - "Webdings", - "Wingdings", - "", - }, - }, - { "Core MS Legacy Fonts", - { "Arial Black", - "Arial Narrow", - "Arial Unicode MS", - "Arial", - "Comic Sans MS", - "Courier New", - "Garamond", - "Georgia", - "Impact", - "Lucida Console", - "Lucida Sans Unicode", - "Microsoft Sans Serif", - "Palatino Linotype", - "Tahoma", - "Times New Roman", - "Trebuchet MS", - "Verdana", - "", - }, - }, - { "Apple Legacy Fonts", - { "Geneva", - "Times", - "Monaco", - "Century", - "Chalkboard", - "Lobster", - "Century Gothic", - "Optima", - "Lucida Grande", - "Gill Sans", - "Baskerville", - "Helvetica", - "Helvetica Neue", - "", - }, - }, - { "Legacy Sans Fonts", - { "Andale Mono", - "Arial Unicode MS", - "Arial", - "Century Gothic", - "Comic Sans MS", - "Franklin Gothic Medium", - "Geneva", - "Lucida Console", - "Lucida Grande", - "Lucida Sans Unicode", - "Lucida Sans Typewriter", - "Microsoft Sans Serif", - "Monaco", - "Tahoma", - "Trebuchet MS", - "Verdana", - "", - }, - }, - - { "Misc Legacy Fonts", - { "Dark Courier", "", }, }, - { "Verdana Clones", - { "DejaVu Sans", - "Bitstream Vera Sans", "", }, }, - { "Verdana and Clones", - { "DejaVu Sans", - "Bitstream Vera Sans", - "Verdana", "", }, }, - }; - - - /* Define this to force natural (i.e. not bitmap-compatible) widths. */ - /* The default leans strongly towards natural widths except for a few */ - /* legacy fonts where a selective combination produces nicer results. */ -/* #define FORCE_NATURAL_WIDTHS */ - - - /* Define `classes' of styles that can be grouped together and used in */ - /* rules below. A blank entry "" is required at the end of these! */ -#define STYLE_CLASS_RULES_SIZE 5 - - static const SPH_Font_Class STYLE_CLASS_Rules - [STYLE_CLASS_RULES_SIZE] = - { - { "Regular Class", - { "Regular", - "Book", - "Medium", - "Roman", - "Normal", - "", - }, - }, - { "Regular/Italic Class", - { "Regular", - "Book", - "Medium", - "Italic", - "Oblique", - "Roman", - "Normal", - "", - }, - }, - { "Bold/BoldItalic Class", - { "Bold", - "Bold Italic", - "Black", - "", - }, - }, - { "Bold/Italic/BoldItalic Class", - { "Bold", - "Bold Italic", - "Black", - "Italic", - "Oblique", - "", - }, - }, - { "Regular/Bold Class", - { "Regular", - "Book", - "Medium", - "Normal", - "Roman", - "Bold", - "Black", - "", - }, - }, - }; - - - /* Force special legacy fixes for fonts. */ -#define COMPATIBILITY_MODE_RULES_SIZE 1 - - static const SPH_TweakRule COMPATIBILITY_MODE_Rules - [COMPATIBILITY_MODE_RULES_SIZE] = - { - { "Verdana Clones", 0, "", 0 }, - }; - - - /* Don't do subpixel (ignore_x_mode) hinting; do normal hinting. */ -#define PIXEL_HINTING_RULES_SIZE 2 - - static const SPH_TweakRule PIXEL_HINTING_Rules - [PIXEL_HINTING_RULES_SIZE] = - { - /* these characters are almost always safe */ - { "Courier New", 12, "Italic", 'z' }, - { "Courier New", 11, "Italic", 'z' }, - }; - - - /* Subpixel hinting ignores SHPIX rules on X. Force SHPIX for these. */ -#define DO_SHPIX_RULES_SIZE 1 - - static const SPH_TweakRule DO_SHPIX_Rules - [DO_SHPIX_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Skip Y moves that start with a point that is not on a Y pixel */ - /* boundary and don't move that point to a Y pixel boundary. */ -#define SKIP_NONPIXEL_Y_MOVES_RULES_SIZE 4 - - static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules - [SKIP_NONPIXEL_Y_MOVES_RULES_SIZE] = - { - /* fix vwxyz thinness */ - { "Consolas", 0, "", 0 }, - /* Fix thin middle stems */ - { "Core MS Legacy Fonts", 0, "Regular", 0 }, - /* Cyrillic small letter I */ - { "Legacy Sans Fonts", 0, "", 0 }, - /* Fix artifacts with some Regular & Bold */ - { "Verdana Clones", 0, "", 0 }, - }; - - -#define SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 - - static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules_Exceptions - [SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = - { - /* Fixes < and > */ - { "Courier New", 0, "Regular", 0 }, - }; - - - /* Skip Y moves that start with a point that is not on a Y pixel */ - /* boundary and don't move that point to a Y pixel boundary. */ -#define SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE 2 - - static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_DELTAP_Rules - [SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE] = - { - /* Maintain thickness of diagonal in 'N' */ - { "Times New Roman", 0, "Regular/Bold Class", 'N' }, - { "Georgia", 0, "Regular/Bold Class", 'N' }, - }; - - - /* Skip Y moves that move a point off a Y pixel boundary. */ -#define SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE 1 - - static const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules - [SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - -#define SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 - - static const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules_Exceptions - [SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Round moves that don't move a point to a Y pixel boundary. */ -#define ROUND_NONPIXEL_Y_MOVES_RULES_SIZE 2 - - static const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules - [ROUND_NONPIXEL_Y_MOVES_RULES_SIZE] = - { - /* Droid font instructions don't snap Y to pixels */ - { "Droid Sans", 0, "Regular/Italic Class", 0 }, - { "Droid Sans Mono", 0, "", 0 }, - }; - - -#define ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 - - static const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules_Exceptions - [ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Allow a Direct_Move along X freedom vector if matched. */ -#define ALLOW_X_DMOVE_RULES_SIZE 1 - - static const SPH_TweakRule ALLOW_X_DMOVE_Rules - [ALLOW_X_DMOVE_RULES_SIZE] = - { - /* Fixes vanishing diagonal in 4 */ - { "Verdana", 0, "Regular", '4' }, - }; - - - /* Return MS rasterizer version 35 if matched. */ -#define RASTERIZER_35_RULES_SIZE 8 - - static const SPH_TweakRule RASTERIZER_35_Rules - [RASTERIZER_35_RULES_SIZE] = - { - /* This seems to be the only way to make these look good */ - { "Times New Roman", 0, "Regular", 'i' }, - { "Times New Roman", 0, "Regular", 'j' }, - { "Times New Roman", 0, "Regular", 'm' }, - { "Times New Roman", 0, "Regular", 'r' }, - { "Times New Roman", 0, "Regular", 'a' }, - { "Times New Roman", 0, "Regular", 'n' }, - { "Times New Roman", 0, "Regular", 'p' }, - { "Times", 0, "", 0 }, - }; - - - /* Don't round to the subpixel grid. Round to pixel grid. */ -#define NORMAL_ROUND_RULES_SIZE 1 - - static const SPH_TweakRule NORMAL_ROUND_Rules - [NORMAL_ROUND_RULES_SIZE] = - { - /* Fix serif thickness for certain ppems */ - /* Can probably be generalized somehow */ - { "Courier New", 0, "", 0 }, - }; - - - /* Skip IUP instructions if matched. */ -#define SKIP_IUP_RULES_SIZE 1 - - static const SPH_TweakRule SKIP_IUP_Rules - [SKIP_IUP_RULES_SIZE] = - { - { "Arial", 13, "Regular", 'a' }, - }; - - - /* Skip MIAP Twilight hack if matched. */ -#define MIAP_HACK_RULES_SIZE 1 - - static const SPH_TweakRule MIAP_HACK_Rules - [MIAP_HACK_RULES_SIZE] = - { - { "Geneva", 12, "", 0 }, - }; - - - /* Skip DELTAP instructions if matched. */ -#define ALWAYS_SKIP_DELTAP_RULES_SIZE 23 - - static const SPH_TweakRule ALWAYS_SKIP_DELTAP_Rules - [ALWAYS_SKIP_DELTAP_RULES_SIZE] = - { - { "Georgia", 0, "Regular", 'k' }, - /* fix various problems with e in different versions */ - { "Trebuchet MS", 14, "Regular", 'e' }, - { "Trebuchet MS", 13, "Regular", 'e' }, - { "Trebuchet MS", 15, "Regular", 'e' }, - { "Trebuchet MS", 0, "Italic", 'v' }, - { "Trebuchet MS", 0, "Italic", 'w' }, - { "Trebuchet MS", 0, "Regular", 'Y' }, - { "Arial", 11, "Regular", 's' }, - /* prevent problems with '3' and others */ - { "Verdana", 10, "Regular", 0 }, - { "Verdana", 9, "Regular", 0 }, - /* Cyrillic small letter short I */ - { "Legacy Sans Fonts", 0, "", 0x438 }, - { "Legacy Sans Fonts", 0, "", 0x439 }, - { "Arial", 10, "Regular", '6' }, - { "Arial", 0, "Bold/BoldItalic Class", 'a' }, - /* Make horizontal stems consistent with the rest */ - { "Arial", 24, "Bold", 'a' }, - { "Arial", 25, "Bold", 'a' }, - { "Arial", 24, "Bold", 's' }, - { "Arial", 25, "Bold", 's' }, - { "Arial", 34, "Bold", 's' }, - { "Arial", 35, "Bold", 's' }, - { "Arial", 36, "Bold", 's' }, - { "Arial", 25, "Regular", 's' }, - { "Arial", 26, "Regular", 's' }, - }; - - - /* Always do DELTAP instructions if matched. */ -#define ALWAYS_DO_DELTAP_RULES_SIZE 1 - - static const SPH_TweakRule ALWAYS_DO_DELTAP_Rules - [ALWAYS_DO_DELTAP_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Don't allow ALIGNRP after IUP. */ -#define NO_ALIGNRP_AFTER_IUP_RULES_SIZE 1 - - static const SPH_TweakRule NO_ALIGNRP_AFTER_IUP_Rules - [NO_ALIGNRP_AFTER_IUP_RULES_SIZE] = - { - /* Prevent creation of dents in outline */ - { "-", 0, "", 0 }, - }; - - - /* Don't allow DELTAP after IUP. */ -#define NO_DELTAP_AFTER_IUP_RULES_SIZE 1 - - static const SPH_TweakRule NO_DELTAP_AFTER_IUP_Rules - [NO_DELTAP_AFTER_IUP_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Don't allow CALL after IUP. */ -#define NO_CALL_AFTER_IUP_RULES_SIZE 1 - - static const SPH_TweakRule NO_CALL_AFTER_IUP_Rules - [NO_CALL_AFTER_IUP_RULES_SIZE] = - { - /* Prevent creation of dents in outline */ - { "-", 0, "", 0 }, - }; - - - /* De-embolden these glyphs slightly. */ -#define DEEMBOLDEN_RULES_SIZE 9 - - static const SPH_TweakRule DEEMBOLDEN_Rules - [DEEMBOLDEN_RULES_SIZE] = - { - { "Courier New", 0, "Bold", 'A' }, - { "Courier New", 0, "Bold", 'W' }, - { "Courier New", 0, "Bold", 'w' }, - { "Courier New", 0, "Bold", 'M' }, - { "Courier New", 0, "Bold", 'X' }, - { "Courier New", 0, "Bold", 'K' }, - { "Courier New", 0, "Bold", 'x' }, - { "Courier New", 0, "Bold", 'z' }, - { "Courier New", 0, "Bold", 'v' }, - }; - - - /* Embolden these glyphs slightly. */ -#define EMBOLDEN_RULES_SIZE 2 - - static const SPH_TweakRule EMBOLDEN_Rules - [EMBOLDEN_RULES_SIZE] = - { - { "Courier New", 0, "Regular", 0 }, - { "Courier New", 0, "Italic", 0 }, - }; - - - /* This is a CVT hack that makes thick horizontal stems on 2, 5, 7 */ - /* similar to Windows XP. */ -#define TIMES_NEW_ROMAN_HACK_RULES_SIZE 12 - - static const SPH_TweakRule TIMES_NEW_ROMAN_HACK_Rules - [TIMES_NEW_ROMAN_HACK_RULES_SIZE] = - { - { "Times New Roman", 16, "Italic", '2' }, - { "Times New Roman", 16, "Italic", '5' }, - { "Times New Roman", 16, "Italic", '7' }, - { "Times New Roman", 16, "Regular", '2' }, - { "Times New Roman", 16, "Regular", '5' }, - { "Times New Roman", 16, "Regular", '7' }, - { "Times New Roman", 17, "Italic", '2' }, - { "Times New Roman", 17, "Italic", '5' }, - { "Times New Roman", 17, "Italic", '7' }, - { "Times New Roman", 17, "Regular", '2' }, - { "Times New Roman", 17, "Regular", '5' }, - { "Times New Roman", 17, "Regular", '7' }, - }; - - - /* This fudges distance on 2 to get rid of the vanishing stem issue. */ - /* A real solution to this is certainly welcome. */ -#define COURIER_NEW_2_HACK_RULES_SIZE 15 - - static const SPH_TweakRule COURIER_NEW_2_HACK_Rules - [COURIER_NEW_2_HACK_RULES_SIZE] = - { - { "Courier New", 10, "Regular", '2' }, - { "Courier New", 11, "Regular", '2' }, - { "Courier New", 12, "Regular", '2' }, - { "Courier New", 13, "Regular", '2' }, - { "Courier New", 14, "Regular", '2' }, - { "Courier New", 15, "Regular", '2' }, - { "Courier New", 16, "Regular", '2' }, - { "Courier New", 17, "Regular", '2' }, - { "Courier New", 18, "Regular", '2' }, - { "Courier New", 19, "Regular", '2' }, - { "Courier New", 20, "Regular", '2' }, - { "Courier New", 21, "Regular", '2' }, - { "Courier New", 22, "Regular", '2' }, - { "Courier New", 23, "Regular", '2' }, - { "Courier New", 24, "Regular", '2' }, - }; - - -#ifndef FORCE_NATURAL_WIDTHS - - /* Use compatible widths with these glyphs. Compatible widths is always */ - /* on when doing B/W TrueType instructing, but is used selectively here, */ - /* typically on glyphs with 3 or more vertical stems. */ -#define COMPATIBLE_WIDTHS_RULES_SIZE 38 - - static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules - [COMPATIBLE_WIDTHS_RULES_SIZE] = - { - { "Arial Unicode MS", 12, "Regular Class", 'm' }, - { "Arial Unicode MS", 14, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Arial", 10, "Regular Class", 0x448 }, - { "Arial", 11, "Regular Class", 'm' }, - { "Arial", 12, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Arial", 12, "Regular Class", 0x448 }, - { "Arial", 13, "Regular Class", 0x448 }, - { "Arial", 14, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Arial", 14, "Regular Class", 0x448 }, - { "Arial", 15, "Regular Class", 0x448 }, - { "Arial", 17, "Regular Class", 'm' }, - { "DejaVu Sans", 15, "Regular Class", 0 }, - { "Microsoft Sans Serif", 11, "Regular Class", 0 }, - { "Microsoft Sans Serif", 12, "Regular Class", 0 }, - { "Segoe UI", 11, "Regular Class", 0 }, - { "Monaco", 0, "Regular Class", 0 }, - { "Segoe UI", 12, "Regular Class", 'm' }, - { "Segoe UI", 14, "Regular Class", 'm' }, - { "Tahoma", 11, "Regular Class", 0 }, - { "Times New Roman", 16, "Regular Class", 'c' }, - { "Times New Roman", 16, "Regular Class", 'm' }, - { "Times New Roman", 16, "Regular Class", 'o' }, - { "Times New Roman", 16, "Regular Class", 'w' }, - { "Trebuchet MS", 11, "Regular Class", 0 }, - { "Trebuchet MS", 12, "Regular Class", 0 }, - { "Trebuchet MS", 14, "Regular Class", 0 }, - { "Trebuchet MS", 15, "Regular Class", 0 }, - { "Ubuntu", 12, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Verdana", 10, "Regular Class", 0x448 }, - { "Verdana", 11, "Regular Class", 0x448 }, - { "Verdana and Clones", 12, "Regular Class", 'i' }, - { "Verdana and Clones", 12, "Regular Class", 'j' }, - { "Verdana and Clones", 12, "Regular Class", 'l' }, - { "Verdana and Clones", 12, "Regular Class", 'm' }, - { "Verdana and Clones", 13, "Regular Class", 'i' }, - { "Verdana and Clones", 13, "Regular Class", 'j' }, - { "Verdana and Clones", 13, "Regular Class", 'l' }, - { "Verdana and Clones", 14, "Regular Class", 'm' }, - }; - - - /* Scaling slightly in the x-direction prior to hinting results in */ - /* more visually pleasing glyphs in certain cases. */ - /* This sometimes needs to be coordinated with compatible width rules. */ - /* A value of 1000 corresponds to a scaled value of 1.0. */ - -#define X_SCALING_RULES_SIZE 50 - - static const SPH_ScaleRule X_SCALING_Rules[X_SCALING_RULES_SIZE] = - { - { "DejaVu Sans", 12, "Regular Class", 'm', 950 }, - { "Verdana and Clones", 12, "Regular Class", 'a', 1100 }, - { "Verdana and Clones", 13, "Regular Class", 'a', 1050 }, - { "Arial", 11, "Regular Class", 'm', 975 }, - { "Arial", 12, "Regular Class", 'm', 1050 }, - /* Cyrillic small letter el */ - { "Arial", 13, "Regular Class", 0x43B, 950 }, - { "Arial", 13, "Regular Class", 'o', 950 }, - { "Arial", 13, "Regular Class", 'e', 950 }, - { "Arial", 14, "Regular Class", 'm', 950 }, - /* Cyrillic small letter el */ - { "Arial", 15, "Regular Class", 0x43B, 925 }, - { "Bitstream Vera Sans", 10, "Regular/Italic Class", 0, 1100 }, - { "Bitstream Vera Sans", 12, "Regular/Italic Class", 0, 1050 }, - { "Bitstream Vera Sans", 16, "Regular Class", 0, 1050 }, - { "Bitstream Vera Sans", 9, "Regular/Italic Class", 0, 1050 }, - { "DejaVu Sans", 12, "Regular Class", 'l', 975 }, - { "DejaVu Sans", 12, "Regular Class", 'i', 975 }, - { "DejaVu Sans", 12, "Regular Class", 'j', 975 }, - { "DejaVu Sans", 13, "Regular Class", 'l', 950 }, - { "DejaVu Sans", 13, "Regular Class", 'i', 950 }, - { "DejaVu Sans", 13, "Regular Class", 'j', 950 }, - { "DejaVu Sans", 10, "Regular/Italic Class", 0, 1100 }, - { "DejaVu Sans", 12, "Regular/Italic Class", 0, 1050 }, - { "Georgia", 10, "", 0, 1050 }, - { "Georgia", 11, "", 0, 1100 }, - { "Georgia", 12, "", 0, 1025 }, - { "Georgia", 13, "", 0, 1050 }, - { "Georgia", 16, "", 0, 1050 }, - { "Georgia", 17, "", 0, 1030 }, - { "Liberation Sans", 12, "Regular Class", 'm', 1100 }, - { "Lucida Grande", 11, "Regular Class", 'm', 1100 }, - { "Microsoft Sans Serif", 11, "Regular Class", 'm', 950 }, - { "Microsoft Sans Serif", 12, "Regular Class", 'm', 1050 }, - { "Segoe UI", 12, "Regular Class", 'H', 1050 }, - { "Segoe UI", 12, "Regular Class", 'm', 1050 }, - { "Segoe UI", 14, "Regular Class", 'm', 1050 }, - { "Tahoma", 11, "Regular Class", 'i', 975 }, - { "Tahoma", 11, "Regular Class", 'l', 975 }, - { "Tahoma", 11, "Regular Class", 'j', 900 }, - { "Tahoma", 11, "Regular Class", 'm', 918 }, - { "Verdana", 10, "Regular/Italic Class", 0, 1100 }, - { "Verdana", 12, "Regular Class", 'm', 975 }, - { "Verdana", 12, "Regular/Italic Class", 0, 1050 }, - { "Verdana", 13, "Regular/Italic Class", 'i', 950 }, - { "Verdana", 13, "Regular/Italic Class", 'j', 950 }, - { "Verdana", 13, "Regular/Italic Class", 'l', 950 }, - { "Verdana", 16, "Regular Class", 0, 1050 }, - { "Verdana", 9, "Regular/Italic Class", 0, 1050 }, - { "Times New Roman", 16, "Regular Class", 'm', 918 }, - { "Trebuchet MS", 11, "Regular Class", 'm', 800 }, - { "Trebuchet MS", 12, "Regular Class", 'm', 800 }, - }; - -#else - -#define COMPATIBLE_WIDTHS_RULES_SIZE 1 - - static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules - [COMPATIBLE_WIDTHS_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - -#define X_SCALING_RULES_SIZE 1 - - static const SPH_ScaleRule X_SCALING_Rules - [X_SCALING_RULES_SIZE] = - { - { "-", 0, "", 0, 1000 }, - }; - -#endif /* FORCE_NATURAL_WIDTHS */ - - - static FT_Bool - is_member_of_family_class( const FT_String* detected_font_name, - const FT_String* rule_font_name ) - { - FT_UInt i, j; - - - /* Does font name match rule family? */ - if ( ft_strcmp( detected_font_name, rule_font_name ) == 0 ) - return TRUE; - - /* Is font name a wildcard ""? */ - if ( ft_strcmp( rule_font_name, "" ) == 0 ) - return TRUE; - - /* Is font name contained in a class list? */ - for ( i = 0; i < FAMILY_CLASS_RULES_SIZE; i++ ) - { - if ( ft_strcmp( FAMILY_CLASS_Rules[i].name, rule_font_name ) == 0 ) - { - for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) - { - if ( ft_strcmp( FAMILY_CLASS_Rules[i].member[j], "" ) == 0 ) - continue; - if ( ft_strcmp( FAMILY_CLASS_Rules[i].member[j], - detected_font_name ) == 0 ) - return TRUE; - } - } - } - - return FALSE; - } - - - static FT_Bool - is_member_of_style_class( const FT_String* detected_font_style, - const FT_String* rule_font_style ) - { - FT_UInt i, j; - - - /* Does font style match rule style? */ - if ( ft_strcmp( detected_font_style, rule_font_style ) == 0 ) - return TRUE; - - /* Is font style a wildcard ""? */ - if ( ft_strcmp( rule_font_style, "" ) == 0 ) - return TRUE; - - /* Is font style contained in a class list? */ - for ( i = 0; i < STYLE_CLASS_RULES_SIZE; i++ ) - { - if ( ft_strcmp( STYLE_CLASS_Rules[i].name, rule_font_style ) == 0 ) - { - for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) - { - if ( ft_strcmp( STYLE_CLASS_Rules[i].member[j], "" ) == 0 ) - continue; - if ( ft_strcmp( STYLE_CLASS_Rules[i].member[j], - detected_font_style ) == 0 ) - return TRUE; - } - } - } - - return FALSE; - } - - - FT_LOCAL_DEF( FT_Bool ) - sph_test_tweak( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index, - const SPH_TweakRule* rule, - FT_UInt num_rules ) - { - FT_UInt i; - - - /* rule checks may be able to be optimized further */ - for ( i = 0; i < num_rules; i++ ) - { - if ( family && - ( is_member_of_family_class ( family, rule[i].family ) ) ) - if ( rule[i].ppem == 0 || - rule[i].ppem == ppem ) - if ( style && - is_member_of_style_class ( style, rule[i].style ) ) - if ( rule[i].glyph == 0 || - FT_Get_Char_Index( (FT_Face)face, - rule[i].glyph ) == glyph_index ) - return TRUE; - } - - return FALSE; - } - - - static FT_UInt - scale_test_tweak( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index, - const SPH_ScaleRule* rule, - FT_UInt num_rules ) - { - FT_UInt i; - - - /* rule checks may be able to be optimized further */ - for ( i = 0; i < num_rules; i++ ) - { - if ( family && - ( is_member_of_family_class ( family, rule[i].family ) ) ) - if ( rule[i].ppem == 0 || - rule[i].ppem == ppem ) - if ( style && - is_member_of_style_class( style, rule[i].style ) ) - if ( rule[i].glyph == 0 || - FT_Get_Char_Index( (FT_Face)face, - rule[i].glyph ) == glyph_index ) - return rule[i].scale; - } - - return 1000; - } - - - FT_LOCAL_DEF( FT_UInt ) - sph_test_tweak_x_scaling( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index ) - { - return scale_test_tweak( face, family, ppem, style, glyph_index, - X_SCALING_Rules, X_SCALING_RULES_SIZE ); - } - - -#define TWEAK_RULES( x ) \ - if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ - x##_Rules, x##_RULES_SIZE ) ) \ - loader->exec->sph_tweak_flags |= SPH_TWEAK_##x - -#define TWEAK_RULES_EXCEPTIONS( x ) \ - if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ - x##_Rules_Exceptions, x##_RULES_EXCEPTIONS_SIZE ) ) \ - loader->exec->sph_tweak_flags &= ~SPH_TWEAK_##x - - - FT_LOCAL_DEF( void ) - sph_set_tweaks( TT_Loader loader, - FT_UInt glyph_index ) - { - TT_Face face = loader->face; - FT_String* family = face->root.family_name; - FT_UInt ppem = loader->size->metrics->x_ppem; - FT_String* style = face->root.style_name; - - - /* don't apply rules if style isn't set */ - if ( !face->root.style_name ) - return; - -#ifdef SPH_DEBUG_MORE_VERBOSE - printf( "%s,%d,%s,%c=%d ", - family, ppem, style, glyph_index, glyph_index ); -#endif - - TWEAK_RULES( PIXEL_HINTING ); - - if ( loader->exec->sph_tweak_flags & SPH_TWEAK_PIXEL_HINTING ) - { - loader->exec->ignore_x_mode = FALSE; - return; - } - - TWEAK_RULES( ALLOW_X_DMOVE ); - TWEAK_RULES( ALWAYS_DO_DELTAP ); - TWEAK_RULES( ALWAYS_SKIP_DELTAP ); - TWEAK_RULES( DEEMBOLDEN ); - TWEAK_RULES( DO_SHPIX ); - TWEAK_RULES( EMBOLDEN ); - TWEAK_RULES( MIAP_HACK ); - TWEAK_RULES( NORMAL_ROUND ); - TWEAK_RULES( NO_ALIGNRP_AFTER_IUP ); - TWEAK_RULES( NO_CALL_AFTER_IUP ); - TWEAK_RULES( NO_DELTAP_AFTER_IUP ); - TWEAK_RULES( RASTERIZER_35 ); - TWEAK_RULES( SKIP_IUP ); - - TWEAK_RULES( SKIP_OFFPIXEL_Y_MOVES ); - TWEAK_RULES_EXCEPTIONS( SKIP_OFFPIXEL_Y_MOVES ); - - TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES_DELTAP ); - - TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES ); - TWEAK_RULES_EXCEPTIONS( SKIP_NONPIXEL_Y_MOVES ); - - TWEAK_RULES( ROUND_NONPIXEL_Y_MOVES ); - TWEAK_RULES_EXCEPTIONS( ROUND_NONPIXEL_Y_MOVES ); - - if ( loader->exec->sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) - { - if ( loader->exec->rasterizer_version != TT_INTERPRETER_VERSION_35 ) - { - loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; - loader->exec->size->cvt_ready = -1; - - tt_size_ready_bytecode( - loader->exec->size, - FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); - } - else - loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; - } - else - { - if ( loader->exec->rasterizer_version != - SPH_OPTION_SET_RASTERIZER_VERSION ) - { - loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; - loader->exec->size->cvt_ready = -1; - - tt_size_ready_bytecode( - loader->exec->size, - FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); - } - else - loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; - } - - if ( IS_HINTED( loader->load_flags ) ) - { - TWEAK_RULES( TIMES_NEW_ROMAN_HACK ); - TWEAK_RULES( COURIER_NEW_2_HACK ); - } - - if ( sph_test_tweak( face, family, ppem, style, glyph_index, - COMPATIBILITY_MODE_Rules, COMPATIBILITY_MODE_RULES_SIZE ) ) - loader->exec->face->sph_compatibility_mode = TRUE; - - - if ( IS_HINTED( loader->load_flags ) ) - { - if ( sph_test_tweak( face, family, ppem, style, glyph_index, - COMPATIBLE_WIDTHS_Rules, COMPATIBLE_WIDTHS_RULES_SIZE ) ) - loader->exec->compatible_widths |= TRUE; - } - } - -#else /* !(TT_USE_BYTECODE_INTERPRETER && */ - /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY) */ - - /* ANSI C doesn't like empty source files */ - typedef int _tt_subpix_dummy; - -#endif /* !(TT_USE_BYTECODE_INTERPRETER && */ - /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY) */ - - -/* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.h b/src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.h deleted file mode 100644 index 62af4c272d1..00000000000 --- a/src/java.desktop/share/native/libfreetype/src/truetype/ttsubpix.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** - * - * ttsubpix.h - * - * TrueType Subpixel Hinting. - * - * Copyright (C) 2010-2023 by - * David Turner, Robert Wilhelm, and Werner Lemberg. - * - * This file is part of the FreeType project, and may only be used, - * modified, and distributed under the terms of the FreeType project - * license, LICENSE.TXT. By continuing to use, modify, or distribute - * this file you indicate that you have read the license and - * understand and accept it fully. - * - */ - - -#ifndef TTSUBPIX_H_ -#define TTSUBPIX_H_ - -#include "ttobjs.h" -#include "ttinterp.h" - - -FT_BEGIN_HEADER - - -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - /************************************************************************** - * - * ID flags to identify special functions at FDEF and runtime. - * - */ -#define SPH_FDEF_INLINE_DELTA_1 0x0000001 -#define SPH_FDEF_INLINE_DELTA_2 0x0000002 -#define SPH_FDEF_DIAGONAL_STROKE 0x0000004 -#define SPH_FDEF_VACUFORM_ROUND_1 0x0000008 -#define SPH_FDEF_TTFAUTOHINT_1 0x0000010 -#define SPH_FDEF_SPACING_1 0x0000020 -#define SPH_FDEF_SPACING_2 0x0000040 -#define SPH_FDEF_TYPEMAN_STROKES 0x0000080 -#define SPH_FDEF_TYPEMAN_DIAGENDCTRL 0x0000100 - - - /************************************************************************** - * - * Tweak flags that are set for each glyph by the below rules. - * - */ -#define SPH_TWEAK_ALLOW_X_DMOVE 0x0000001UL -#define SPH_TWEAK_ALWAYS_DO_DELTAP 0x0000002UL -#define SPH_TWEAK_ALWAYS_SKIP_DELTAP 0x0000004UL -#define SPH_TWEAK_COURIER_NEW_2_HACK 0x0000008UL -#define SPH_TWEAK_DEEMBOLDEN 0x0000010UL -#define SPH_TWEAK_DO_SHPIX 0x0000020UL -#define SPH_TWEAK_EMBOLDEN 0x0000040UL -#define SPH_TWEAK_MIAP_HACK 0x0000080UL -#define SPH_TWEAK_NORMAL_ROUND 0x0000100UL -#define SPH_TWEAK_NO_ALIGNRP_AFTER_IUP 0x0000200UL -#define SPH_TWEAK_NO_CALL_AFTER_IUP 0x0000400UL -#define SPH_TWEAK_NO_DELTAP_AFTER_IUP 0x0000800UL -#define SPH_TWEAK_PIXEL_HINTING 0x0001000UL -#define SPH_TWEAK_RASTERIZER_35 0x0002000UL -#define SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES 0x0004000UL -#define SPH_TWEAK_SKIP_IUP 0x0008000UL -#define SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES 0x0010000UL -#define SPH_TWEAK_SKIP_OFFPIXEL_Y_MOVES 0x0020000UL -#define SPH_TWEAK_TIMES_NEW_ROMAN_HACK 0x0040000UL -#define SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP 0x0080000UL - - - FT_LOCAL( FT_Bool ) - sph_test_tweak( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index, - const SPH_TweakRule* rule, - FT_UInt num_rules ); - - FT_LOCAL( FT_UInt ) - sph_test_tweak_x_scaling( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index ); - - FT_LOCAL( void ) - sph_set_tweaks( TT_Loader loader, - FT_UInt glyph_index ); - - - /* These macros are defined absent a method for setting them */ -#define SPH_OPTION_BITMAP_WIDTHS FALSE -#define SPH_OPTION_SET_SUBPIXEL TRUE -#define SPH_OPTION_SET_GRAYSCALE FALSE -#define SPH_OPTION_SET_COMPATIBLE_WIDTHS FALSE -#define SPH_OPTION_SET_RASTERIZER_VERSION 38 - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - - -FT_END_HEADER - -#endif /* TTSUBPIX_H_ */ - - -/* END */ diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c index a63cd4dc48a..b1a0d23bed6 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.c @@ -4,7 +4,7 @@ * * AFM support for Type 1 fonts (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h index 7f5cdda191f..92ff627dd0d 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1afm.h @@ -4,7 +4,7 @@ * * AFM support for Type 1 fonts (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c index 8ed01914a5a..5ded7714021 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.c @@ -4,7 +4,7 @@ * * Type 1 driver interface (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h index 5ff52b55b1a..1cc3d24e7dd 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1driver.h @@ -4,7 +4,7 @@ * * High-level Type 1 driver interface (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h b/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h index 8aeb24ae188..46bddbc30fd 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1errors.h @@ -4,7 +4,7 @@ * * Type 1 error codes (specification only). * - * Copyright (C) 2001-2024 by + * Copyright (C) 2001-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c index c29e682510c..b9bc0b56ce8 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.c @@ -4,7 +4,7 @@ * * Type 1 Glyph Loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -70,8 +70,13 @@ /* For incremental fonts get the character data using the */ /* callback function. */ if ( inc ) + { + /* So `free_glyph_data` knows whether to free it. */ + char_string->pointer = NULL; + error = inc->funcs->get_glyph_data( inc->object, glyph_index, char_string ); + } else #endif /* FT_CONFIG_OPTION_INCREMENTAL */ @@ -155,6 +160,9 @@ decoder->builder.advance.y = INT_TO_FIXED( metrics.advance_v ); } + if ( error && inc ) + inc->funcs->free_glyph_data( inc->object, char_string ); + #endif /* FT_CONFIG_OPTION_INCREMENTAL */ return error; @@ -295,7 +303,7 @@ { advances[nn] = 0; - FT_TRACE5(( " idx %d: advance height 0 font units\n", + FT_TRACE5(( " idx %u: advance height 0 font units\n", first + nn )); } @@ -333,7 +341,7 @@ else advances[nn] = 0; - FT_TRACE5(( " idx %d: advance width %ld font unit%s\n", + FT_TRACE5(( " idx %u: advance width %ld font unit%s\n", first + nn, advances[nn], advances[nn] == 1 ? "" : "s" )); @@ -380,7 +388,7 @@ goto Exit; } - FT_TRACE1(( "T1_Load_Glyph: glyph index %d\n", glyph_index )); + FT_TRACE1(( "T1_Load_Glyph: glyph index %u\n", glyph_index )); FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) ); @@ -398,16 +406,12 @@ glyph->y_scale = 0x10000L; } - t1glyph->outline.n_points = 0; - t1glyph->outline.n_contours = 0; - hinting = FT_BOOL( !( load_flags & FT_LOAD_NO_SCALE ) && !( load_flags & FT_LOAD_NO_HINTING ) ); scaled = FT_BOOL( !( load_flags & FT_LOAD_NO_SCALE ) ); glyph->hint = hinting; glyph->scaled = scaled; - t1glyph->format = FT_GLYPH_FORMAT_OUTLINE; error = decoder_funcs->init( &decoder, t1glyph->face, @@ -452,16 +456,12 @@ must_finish_decoder = FALSE; - /* now, set the metrics -- this is rather simple, as */ - /* the left side bearing is the xMin, and the top side */ - /* bearing the yMax */ if ( !error ) { - t1glyph->outline.flags &= FT_OUTLINE_OWNER; - t1glyph->outline.flags |= FT_OUTLINE_REVERSE_FILL; - - /* for composite glyphs, return only left side bearing and */ - /* advance width */ + /* now, set the metrics -- this is rather simple, as */ + /* the left side bearing is the xMin, and the top side */ + /* bearing the yMax; for composite glyphs, return only */ + /* left side bearing and advance width */ if ( load_flags & FT_LOAD_NO_RECURSE ) { FT_Slot_Internal internal = t1glyph->internal; @@ -482,6 +482,13 @@ FT_Glyph_Metrics* metrics = &t1glyph->metrics; + t1glyph->format = FT_GLYPH_FORMAT_OUTLINE; + + t1glyph->outline.flags &= FT_OUTLINE_OWNER; + t1glyph->outline.flags |= FT_OUTLINE_REVERSE_FILL; + if ( t1size && t1size->metrics.y_ppem < 24 ) + t1glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; + /* copy the _unscaled_ advance width */ metrics->horiAdvance = FIXED_TO_INT( decoder.builder.advance.x ); @@ -504,11 +511,6 @@ FIXED_TO_INT( decoder.builder.advance.y ); } - t1glyph->format = FT_GLYPH_FORMAT_OUTLINE; - - if ( t1size && t1size->metrics.y_ppem < 24 ) - t1glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION; - #if 1 /* apply the font matrix, if any */ if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L || diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h index 17a6a5941e3..6bedd132c5f 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1gload.h @@ -4,7 +4,7 @@ * * Type 1 Glyph Loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1load.c b/src/java.desktop/share/native/libfreetype/src/type1/t1load.c index ee7fb42a517..0f11445bef0 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1load.c +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1load.c @@ -4,7 +4,7 @@ * * Type 1 font loader (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, @@ -471,7 +471,7 @@ nc = num_coords; if ( num_coords > blend->num_axis ) { - FT_TRACE2(( "T1_Get_MM_Blend: only using first %d of %d coordinates\n", + FT_TRACE2(( "T1_Get_MM_Blend: only using first %u of %u coordinates\n", blend->num_axis, num_coords )); nc = blend->num_axis; } @@ -640,7 +640,7 @@ { FT_UNUSED( instance_index ); - return T1_Set_MM_Blend( face, 0, NULL ); + return T1_Set_MM_WeightVector( face, 0, NULL ); } @@ -691,7 +691,7 @@ if ( num_coords > blend->num_axis ) { FT_TRACE2(( "T1_Get_Var_Design:" - " only using first %d of %d coordinates\n", + " only using first %u of %u coordinates\n", blend->num_axis, num_coords )); nc = blend->num_axis; } diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1load.h b/src/java.desktop/share/native/libfreetype/src/type1/t1load.h index a45efa7cb7b..2cd8241968d 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1load.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1load.h @@ -4,7 +4,7 @@ * * Type 1 font loader (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c index b1b27c31fe3..7f25208f875 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.c @@ -4,7 +4,7 @@ * * Type 1 objects manager (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h index 3809370c1e0..6c71977c154 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1objs.h @@ -4,7 +4,7 @@ * * Type 1 objects manager (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c index 3717ea7c572..ef643e298f4 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.c @@ -4,7 +4,7 @@ * * Type 1 parser (body). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h index a0a2134d45c..f4ad426e9e1 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1parse.h @@ -4,7 +4,7 @@ * * Type 1 parser (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h b/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h index 5a3d2f1ef08..c3736cd42c0 100644 --- a/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h +++ b/src/java.desktop/share/native/libfreetype/src/type1/t1tokens.h @@ -4,7 +4,7 @@ * * Type 1 tokenizer (specification). * - * Copyright (C) 1996-2024 by + * Copyright (C) 1996-2025 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used, diff --git a/test/jdk/javax/swing/text/html/CSS/8231286/HtmlFontSizeTest.java b/test/jdk/javax/swing/text/html/CSS/8231286/HtmlFontSizeTest.java index 9bdf4dbfae8..eea0a969e77 100644 --- a/test/jdk/javax/swing/text/html/CSS/8231286/HtmlFontSizeTest.java +++ b/test/jdk/javax/swing/text/html/CSS/8231286/HtmlFontSizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 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 @@ -51,8 +51,8 @@ public class HtmlFontSizeTest { htmlPane.setEditorKit(kit); String htmlString = "\n" - + "\n" - + "

This should be 16 pt.

\n" + + "\n" + + "

This should be 32 pt.

\n" + "\n" + ""; @@ -71,10 +71,16 @@ public class HtmlFontSizeTest { System.out.println("size with W3C:" + w3cFrameSize); System.out.println("size without W3C:" + stdFrameSize); - float ratio = (float)w3cFrameSize.width / (float)stdFrameSize.width; - System.out.println("w3cFrameSize.width/stdFrameSize.width " + ratio); + float widthRatio = (float)w3cFrameSize.width / (float)stdFrameSize.width; + System.out.println("w3cFrameSize.width/stdFrameSize.width " + widthRatio); - if (!"1.3".equals(String.format(Locale.ENGLISH, "%.1f", ratio))) { + float heightRatio = (float)w3cFrameSize.height / (float)stdFrameSize.height; + System.out.println("w3cFrameSize.height/stdFrameSize.height " + heightRatio); + + float avgRatio = (widthRatio + heightRatio) / 2.0f; + System.out.println("Average ratio of two dimensions " + avgRatio); + + if (!"1.3".equals(String.format(Locale.ENGLISH, "%.1f", avgRatio))) { throw new RuntimeException("HTML font size too large with high-DPI scaling and W3C_LENGTH_UNITS"); } } From 667404948c1c967daf6e274c61b9d1b1bd0827d4 Mon Sep 17 00:00:00 2001 From: Oli Gillespie Date: Tue, 3 Mar 2026 10:05:14 +0000 Subject: [PATCH 130/636] 8378963: Test test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java#id4 failed Reviewed-by: kevinw, dholmes --- .../jdk/java/lang/management/RuntimeMXBean/InputArgument.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java b/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java index 0e7609b86ed..8f0ed8e48af 100644 --- a/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java +++ b/test/jdk/java/lang/management/RuntimeMXBean/InputArgument.java @@ -69,7 +69,7 @@ * * @run driver InputArgument generateFlagsFile * @run main/othervm -XX:+UseFastJNIAccessors -XX:Flags=InputArgument.flags InputArgument - * -XX:+UseFastJNIAccessors -XX:-UseG1GC -XX:+UseParallelGC -XX:MaxHeapSize=100M + * -XX:+UseFastJNIAccessors -XX:+PrintWarnings -XX:-PrintCommandLineFlags -XX:ErrorLogTimeout=100 */ import java.lang.management.*; @@ -116,6 +116,6 @@ public class InputArgument { private static void generateFlagsFile() throws Exception { // 3 types of flag; both boolean cases and 1 numeric Files.writeString(Paths.get("", "InputArgument.flags"), - String.format("-UseG1GC%n+UseParallelGC%nMaxHeapSize=100M%n")); + String.format("+PrintWarnings%n-PrintCommandLineFlags%nErrorLogTimeout=100%n")); } } From b28568f5d8308170c018651ea1c87c2b8f36acb2 Mon Sep 17 00:00:00 2001 From: Vic Wang Date: Tue, 3 Mar 2026 10:41:47 +0000 Subject: [PATCH 131/636] 8367478: Improve UseAVX setting and add cpu descriptions for zhaoxin processors Reviewed-by: jiefu, dholmes --- src/hotspot/cpu/x86/vm_version_x86.cpp | 40 ++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 78d6dec08cf..b352de77d6f 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -958,9 +958,17 @@ void VM_Version::get_processor_features() { if (UseSSE < 1) _features.clear_feature(CPU_SSE); - //since AVX instructions is slower than SSE in some ZX cpus, force USEAVX=0. - if (is_zx() && ((cpu_family() == 6) || (cpu_family() == 7))) { - UseAVX = 0; + // ZX cpus specific settings + if (is_zx() && FLAG_IS_DEFAULT(UseAVX)) { + if (cpu_family() == 7) { + if (extended_cpu_model() == 0x5B || extended_cpu_model() == 0x6B) { + UseAVX = 1; + } else if (extended_cpu_model() == 0x1B || extended_cpu_model() == 0x3B) { + UseAVX = 0; + } + } else if (cpu_family() == 6) { + UseAVX = 0; + } } // UseSSE is set to the smaller of what hardware supports and what @@ -2623,6 +2631,23 @@ const char* VM_Version::cpu_family_description(void) { return _family_id_intel[cpu_family_id]; } } + if (is_zx()) { + int cpu_model_id = extended_cpu_model(); + if (cpu_family_id == 7) { + switch (cpu_model_id) { + case 0x1B: + return "wudaokou"; + case 0x3B: + return "lujiazui"; + case 0x5B: + return "yongfeng"; + case 0x6B: + return "shijidadao"; + } + } else if (cpu_family_id == 6) { + return "zhangjiang"; + } + } if (is_hygon()) { return "Dhyana"; } @@ -2642,6 +2667,9 @@ int VM_Version::cpu_type_description(char* const buf, size_t buf_len) { } else if (is_amd()) { cpu_type = "AMD"; x64 = cpu_is_em64t() ? " AMD64" : ""; + } else if (is_zx()) { + cpu_type = "Zhaoxin"; + x64 = cpu_is_em64t() ? " x86_64" : ""; } else if (is_hygon()) { cpu_type = "Hygon"; x64 = cpu_is_em64t() ? " AMD64" : ""; @@ -3259,6 +3287,12 @@ int VM_Version::allocate_prefetch_distance(bool use_watermark_prefetch) { } else { return 128; // Athlon } + } else if (is_zx()) { + if (supports_sse2()) { + return 256; + } else { + return 128; + } } else { // Intel if (supports_sse3() && is_intel_server_family()) { if (supports_sse4_2() && supports_ht()) { // Nehalem based cpus From 7dc97af89f0965ff9e0fa38426adcfc8c69c34ea Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 3 Mar 2026 12:37:05 +0000 Subject: [PATCH 132/636] 8378905: RISC-V: fastdebug build fails after JDK-8377554 Reviewed-by: fyang, wenanjian --- src/hotspot/cpu/riscv/riscv.ad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 730dd68dd88..ed6e8db0606 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -2274,7 +2274,7 @@ encode %{ } else if (rtype == relocInfo::metadata_type) { __ mov_metadata(dst_reg, (Metadata*)con); } else { - assert(rtype == relocInfo::none, "unexpected reloc type"); + assert(rtype == relocInfo::none || rtype == relocInfo::external_word_type, "unexpected reloc type"); __ mv(dst_reg, $src$$constant); } } From 6cf8b2ea2fb34b2e63a44d74ffe0495669ea5690 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 3 Mar 2026 13:26:20 +0000 Subject: [PATCH 133/636] 8378845: Add NoSafepointVerifier to CriticalSection classes Reviewed-by: dholmes, iwalulya --- src/hotspot/share/utilities/globalCounter.inline.hpp | 7 +++++-- src/hotspot/share/utilities/singleWriterSynchronizer.hpp | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/utilities/globalCounter.inline.hpp b/src/hotspot/share/utilities/globalCounter.inline.hpp index ed37b8a878d..0d05096716a 100644 --- a/src/hotspot/share/utilities/globalCounter.inline.hpp +++ b/src/hotspot/share/utilities/globalCounter.inline.hpp @@ -29,6 +29,7 @@ #include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" +#include "runtime/safepointVerifiers.hpp" inline GlobalCounter::CSContext GlobalCounter::critical_section_begin(Thread *thread) { @@ -53,11 +54,13 @@ GlobalCounter::critical_section_end(Thread *thread, CSContext context) { } class GlobalCounter::CriticalSection { - private: + NoSafepointVerifier _nsv; Thread* _thread; CSContext _context; - public: + +public: inline CriticalSection(Thread* thread) : + _nsv(), _thread(thread), _context(GlobalCounter::critical_section_begin(_thread)) {} diff --git a/src/hotspot/share/utilities/singleWriterSynchronizer.hpp b/src/hotspot/share/utilities/singleWriterSynchronizer.hpp index 450c7e89233..c21c9d4ee5e 100644 --- a/src/hotspot/share/utilities/singleWriterSynchronizer.hpp +++ b/src/hotspot/share/utilities/singleWriterSynchronizer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "runtime/atomic.hpp" +#include "runtime/safepointVerifiers.hpp" #include "runtime/semaphore.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -101,12 +102,14 @@ inline void SingleWriterSynchronizer::exit(uint enter_value) { } class SingleWriterSynchronizer::CriticalSection : public StackObj { + NoSafepointVerifier _nsv; SingleWriterSynchronizer* _synchronizer; uint _enter_value; public: // Enter synchronizer's critical section. explicit CriticalSection(SingleWriterSynchronizer* synchronizer) : + _nsv(), _synchronizer(synchronizer), _enter_value(synchronizer->enter()) {} From 364fd0e37e05b98042db9c7c140c5ed6d78b50e0 Mon Sep 17 00:00:00 2001 From: Oli Gillespie Date: Tue, 3 Mar 2026 15:23:08 +0000 Subject: [PATCH 134/636] 8378971: Test jdk/jfr/event/runtime/TestVMInfoEvent.java fails after JDK-8378110 (RuntimeMXBean.getInputArguments()) Reviewed-by: syan, kevinw --- .../jdk/jfr/event/runtime/TestVMInfoEvent.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java b/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java index 2571530a470..8cbc6ea2bb8 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java +++ b/test/jdk/jdk/jfr/event/runtime/TestVMInfoEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, 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 @@ -28,8 +28,9 @@ import java.lang.management.RuntimeMXBean; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; -import java.util.stream.Collectors; +import java.util.Properties; +import jdk.internal.vm.VMSupport; import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; import jdk.test.lib.Asserts; @@ -38,6 +39,7 @@ import jdk.test.lib.jfr.Events; /** * @test + * @modules java.base/jdk.internal.vm * @requires vm.flagless * @requires vm.gc == "Serial" | vm.gc == null * @requires vm.hasJFR @@ -69,13 +71,12 @@ public class TestVMInfoEvent { Asserts.fail(String.format("%s does not contain %s", jvmVersion, mbean.getVmVersion())); } - String jvmArgs = Events.assertField(event, "jvmArguments").notNull().getValue(); - String jvmFlags = Events.assertField(event, "jvmFlags").notNull().getValue(); Long pid = Events.assertField(event, "pid").atLeast(0L).getValue(); Asserts.assertEquals(pid, ProcessHandle.current().pid()); - String eventArgs = (jvmFlags.trim() + " " + jvmArgs).trim(); - String beanArgs = mbean.getInputArguments().stream().collect(Collectors.joining(" ")); - Asserts.assertEquals(eventArgs, beanArgs, "Wrong inputArgs"); + + Properties agentProps = VMSupport.getAgentProperties(); + Events.assertField(event, "jvmArguments").equal(agentProps.getProperty("sun.jvm.args")); + Events.assertField(event, "jvmFlags").equal(agentProps.getProperty("sun.jvm.flags")); final String javaCommand = mbean.getSystemProperties().get("sun.java.command"); Events.assertField(event, "javaArguments").equal(javaCommand); From 0ea7d890d98eda32912e9a8340020ee405042576 Mon Sep 17 00:00:00 2001 From: Raffaello Giulietti Date: Tue, 3 Mar 2026 16:57:09 +0000 Subject: [PATCH 135/636] 8377903: ArraysSupport::mismatch should document that they return the smallest index Reviewed-by: rriggs, vyazici --- .../classes/jdk/internal/util/ArraysSupport.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java index c220455e80b..3bd2486fa39 100644 --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java @@ -478,7 +478,7 @@ public class ArraysSupport { // Bytes /** - * Find the index of a mismatch between two arrays. + * Find the smallest index of a mismatch between two arrays. * *

This method does not perform bounds checks. It is the responsibility * of the caller to perform such bounds checks before calling this method. @@ -486,9 +486,9 @@ public class ArraysSupport { * @param a the first array to be tested for a mismatch * @param b the second array to be tested for a mismatch * @param length the number of bytes from each array to check - * @return the index of a mismatch between the two arrays, otherwise -1 if - * no mismatch. The index will be within the range of (inclusive) 0 to - * (exclusive) the smaller of the two array lengths. + * @return the smallest index of a mismatch between the two arrays, + * otherwise -1 if no mismatch. The index will be within the range of + * (inclusive) 0 to (exclusive) the smaller of the two array lengths. */ public static int mismatch(byte[] a, byte[] b, @@ -520,8 +520,8 @@ public class ArraysSupport { } /** - * Find the relative index of a mismatch between two arrays starting from - * given indexes. + * Find the smallest relative index of a mismatch between two arrays + * starting from given indexes. * *

This method does not perform bounds checks. It is the responsibility * of the caller to perform such bounds checks before calling this method. @@ -533,7 +533,7 @@ public class ArraysSupport { * @param bFromIndex the index of the first element (inclusive) in the * second array to be compared * @param length the number of bytes from each array to check - * @return the relative index of a mismatch between the two arrays, + * @return the smallest relative index of a mismatch between the two arrays, * otherwise -1 if no mismatch. The index will be within the range of * (inclusive) 0 to (exclusive) the smaller of the two array bounds. */ From df43ef915ab13714c7a191c6413494f97f9db8c2 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 3 Mar 2026 17:09:14 +0000 Subject: [PATCH 136/636] 8378883: Enable more vector reductions IR matching tests for RISC-V Reviewed-by: fyang --- .../loopopts/superword/TestReductions.java | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java index 9d674950499..5c085e6a3a3 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java @@ -1340,7 +1340,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1357,7 +1357,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1374,7 +1374,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1391,7 +1391,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_REDUCTION_VI, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1408,7 +1408,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MUL_REDUCTION_VI, "> 0", IRNode.MUL_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1425,7 +1425,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1442,7 +1442,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1460,7 +1460,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1477,7 +1477,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1494,7 +1494,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1511,7 +1511,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_REDUCTION_VI, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1528,7 +1528,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MUL_REDUCTION_VI, "> 0", IRNode.MUL_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1545,7 +1545,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1562,7 +1562,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1580,7 +1580,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1597,7 +1597,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1614,7 +1614,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1631,7 +1631,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_REDUCTION_VI, "> 0", IRNode.ADD_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1648,7 +1648,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MUL_REDUCTION_VI, "> 0", IRNode.MUL_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1665,7 +1665,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1682,7 +1682,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VI, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_I, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1700,7 +1700,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.AND_REDUCTION_V, "> 0", IRNode.AND_VL, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_L, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1717,7 +1717,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.OR_REDUCTION_V, "> 0", IRNode.OR_VL, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_L, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1734,7 +1734,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.XOR_REDUCTION_V, "> 0", IRNode.XOR_VL, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_L, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1751,7 +1751,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.ADD_REDUCTION_VL, "> 0", IRNode.ADD_VL, "> 0"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_L, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -1799,7 +1799,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VL, "> 0"}, - applyIfCPUFeatureOr = {"avx512", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx512", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_L, applyIfCPUFeatureAnd = {"avx512", "false", "avx2", "true"}) @@ -1819,7 +1819,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_L, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VL, "> 0"}, - applyIfCPUFeatureOr = {"avx512", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx512", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_L, applyIfCPUFeatureAnd = {"avx512", "false", "avx2", "true"}) @@ -2207,7 +2207,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_F, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2224,7 +2224,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_F, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2284,7 +2284,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_F, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2301,7 +2301,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_F, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2361,7 +2361,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_F, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2378,7 +2378,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VF, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_F, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2444,7 +2444,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_D, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VD, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_D, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2461,7 +2461,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_D, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VD, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_D, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2521,7 +2521,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_D, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VD, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_D, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2538,7 +2538,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_D, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VD, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_D, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2598,7 +2598,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_D, "> 0", IRNode.MIN_REDUCTION_V, "> 0", IRNode.MIN_VD, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_D, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) @@ -2615,7 +2615,7 @@ public class TestReductions { @IR(counts = {IRNode.LOAD_VECTOR_D, "> 0", IRNode.MAX_REDUCTION_V, "> 0", IRNode.MAX_VD, "> 0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_D, applyIf = {"AutoVectorizationOverrideProfitability", "= 0"}) From 86800eb2b34bd6ea7a77e7a9ac2f7dbce89c11fb Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Tue, 3 Mar 2026 17:11:17 +0000 Subject: [PATCH 137/636] 8378723: Locale variant delimiter is unclear Reviewed-by: naoto --- src/java.base/share/classes/java/util/Locale.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index f45a52c14fa..682476d8082 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.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 @@ -176,7 +176,10 @@ import sun.util.locale.provider.TimeZoneNameUtility; * SUBTAG (('_'|'-') SUBTAG)*} where {@code SUBTAG = * [0-9][0-9a-zA-Z]{3} | [0-9a-zA-Z]{5,8}}. *

BCP 47 deviation: BCP 47 only - * uses hyphen ('-') as a delimiter, {@code Locale} is more lenient.
+ * uses hyphen ('-') as a delimiter and APIs provided by {@code Locale} which accept + * BCP 47 language tags expect as such. However, for backwards compatibility, + * {@link Locale.Builder#setVariant(String)} also accepts underscore ('_'). + * {@link Locale#of(String, String, String)} accepts only underscore ('_'). * *
Example: "polyton" (Polytonic Greek), "POSIX"
* From c13fdc044d188d2266b2a96c4d1803b014a00633 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 3 Mar 2026 19:09:03 +0000 Subject: [PATCH 138/636] 8378877: jpackage: improve rebranding of exe files on Windows Reviewed-by: almatvee --- .../internal/ExecutableRebrander.java | 142 +++++++++++------- .../jpackage/internal/WindowsDefender.java | 72 --------- .../jpackage/internal/WindowsRegistry.java | 130 ---------------- .../resources/WinResources.properties | 1 - 4 files changed, 87 insertions(+), 258 deletions(-) delete mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java delete mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.java index 05b87e6f449..ec2a96d4c7d 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.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 @@ -40,9 +40,11 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Properties; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Stream; import jdk.jpackage.internal.model.DottedVersion; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.WinApplication; import jdk.jpackage.internal.model.WinExePackage; import jdk.jpackage.internal.model.WinLauncher; @@ -74,11 +76,11 @@ final class ExecutableRebrander { this.props = new HashMap<>(); - validateValueAndPut(this.props, Map.entry("COMPANY_NAME", props.vendor), "vendor"); - validateValueAndPut(this.props, Map.entry("FILE_DESCRIPTION",props.description), "description"); - validateValueAndPut(this.props, Map.entry("FILE_VERSION", props.version.toString()), "version"); - validateValueAndPut(this.props, Map.entry("LEGAL_COPYRIGHT", props.copyright), "copyright"); - validateValueAndPut(this.props, Map.entry("PRODUCT_NAME", props.name), "name"); + this.props.put("COMPANY_NAME", validateSingleLine(props.vendor)); + this.props.put("FILE_DESCRIPTION", validateSingleLine(props.description)); + this.props.put("FILE_VERSION", validateSingleLine(props.version.toString())); + this.props.put("LEGAL_COPYRIGHT", validateSingleLine(props.copyright)); + this.props.put("PRODUCT_NAME", validateSingleLine(props.name)); this.props.put("FIXEDFILEINFO_FILE_VERSION", toFixedFileVersion(props.version)); this.props.put("INTERNAL_NAME", props.executableName); @@ -90,7 +92,7 @@ final class ExecutableRebrander { UpdateResourceAction versionSwapper = resourceLock -> { if (versionSwap(resourceLock, propsArray) != 0) { - throw I18N.buildException().message("error.version-swap", target).create(RuntimeException::new); + throw new JPackageException(I18N.format("error.version-swap", target)); } }; @@ -100,7 +102,7 @@ final class ExecutableRebrander { .map(absIcon -> { return resourceLock -> { if (iconSwap(resourceLock, absIcon.toString()) != 0) { - throw I18N.buildException().message("error.icon-swap", absIcon).create(RuntimeException::new); + throw new JPackageException(I18N.format("error.icon-swap", absIcon)); } }; }); @@ -118,43 +120,58 @@ final class ExecutableRebrander { private static void rebrandExecutable(BuildEnv env, final Path target, List actions) throws IOException { + + Objects.requireNonNull(env); + Objects.requireNonNull(target); Objects.requireNonNull(actions); actions.forEach(Objects::requireNonNull); - String tempDirectory = env.buildRoot().toAbsolutePath().toString(); - if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(tempDirectory)) { - Log.verbose(I18N.format("message.potential.windows.defender.issue", tempDirectory)); - } - - var shortTargetPath = ShortPathUtils.toShortPath(target); - long resourceLock = lockResource(shortTargetPath.orElse(target).toString()); - if (resourceLock == 0) { - throw I18N.buildException().message("error.lock-resource", shortTargetPath.orElse(target)).create(RuntimeException::new); - } - - final boolean resourceUnlockedSuccess; try { - for (var action : actions) { - action.editResource(resourceLock); - } - } finally { - if (resourceLock == 0) { - resourceUnlockedSuccess = true; - } else { - resourceUnlockedSuccess = unlockResource(resourceLock); - if (shortTargetPath.isPresent()) { - // Windows will rename the executable in the unlock operation. - // Should restore executable's name. - var tmpPath = target.getParent().resolve( - target.getFileName().toString() + ".restore"); - Files.move(shortTargetPath.get(), tmpPath); - Files.move(tmpPath, target); - } - } - } + Globals.instance().objectFactory().retryExecutor(RuntimeException.class).setExecutable(() -> { - if (!resourceUnlockedSuccess) { - throw I18N.buildException().message("error.unlock-resource", target).create(RuntimeException::new); + var shortTargetPath = ShortPathUtils.toShortPath(target); + long resourceLock = lockResource(shortTargetPath.orElse(target).toString()); + if (resourceLock == 0) { + throw new JPackageException(I18N.format("error.lock-resource", shortTargetPath.orElse(target))); + } + + final boolean resourceUnlockedSuccess; + try { + for (var action : actions) { + try { + action.editResource(resourceLock); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } finally { + if (resourceLock == 0) { + resourceUnlockedSuccess = true; + } else { + resourceUnlockedSuccess = unlockResource(resourceLock); + if (shortTargetPath.isPresent()) { + // Windows will rename the executable in the unlock operation. + // Should restore executable's name. + var tmpPath = target.getParent().resolve( + target.getFileName().toString() + ".restore"); + try { + Files.move(shortTargetPath.get(), tmpPath); + Files.move(tmpPath, target); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + } + + if (!resourceUnlockedSuccess) { + throw new JPackageException(I18N.format("error.unlock-resource", shortTargetPath.orElse(target))); + } + + return null; + }).setMaxAttemptsCount(5).setAttemptTimeout(3, TimeUnit.SECONDS).execute(); + } catch (UncheckedIOException ex) { + throw ex.getCause(); } } @@ -197,14 +214,13 @@ final class ExecutableRebrander { } } - private static void validateValueAndPut(Map target, - Map.Entry e, String label) { - if (e.getValue().contains("\r") || e.getValue().contains("\n")) { - Log.error("Configuration parameter " + label - + " contains multiple lines of text, ignore it"); - e = Map.entry(e.getKey(), ""); + private static String validateSingleLine(String v) { + Objects.requireNonNull(v); + if (v.contains("\r") || v.contains("\n")) { + throw new IllegalArgumentException("Configuration parameter contains multiple lines of text"); + } else { + return v; } - target.put(e.getKey(), e.getValue()); } @FunctionalInterface @@ -212,19 +228,35 @@ final class ExecutableRebrander { public void editResource(long resourceLock) throws IOException; } - private static record ExecutableProperties(String vendor, String description, + private record ExecutableProperties(String vendor, String description, DottedVersion version, String copyright, String name, String executableName) { - static ExecutableProperties create(WinApplication app, - WinLauncher launcher) { - return new ExecutableProperties(app.vendor(), launcher.description(), - app.winVersion(), app.copyright(), launcher.name(), + + ExecutableProperties { + Objects.requireNonNull(vendor); + Objects.requireNonNull(description); + Objects.requireNonNull(version); + Objects.requireNonNull(copyright); + Objects.requireNonNull(name); + Objects.requireNonNull(executableName); + } + + static ExecutableProperties create(WinApplication app, WinLauncher launcher) { + return new ExecutableProperties( + app.vendor(), + launcher.description(), + app.winVersion(), + app.copyright(), + launcher.name(), launcher.executableNameWithSuffix()); } static ExecutableProperties create(WinExePackage pkg) { - return new ExecutableProperties(pkg.app().vendor(), - pkg.description(), DottedVersion.lazy(pkg.version()), - pkg.app().copyright(), pkg.packageName(), + return new ExecutableProperties( + pkg.app().vendor(), + pkg.description(), + DottedVersion.lazy(pkg.version()), + pkg.app().copyright(), + pkg.packageName(), pkg.packageFileNameWithSuffix()); } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java deleted file mode 100644 index 075d87bcbca..00000000000 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.List; -import jdk.internal.util.OperatingSystem; -import jdk.internal.util.OSVersion; - -final class WindowsDefender { - - private WindowsDefender() {} - - static final boolean isThereAPotentialWindowsDefenderIssue(String dir) { - boolean result = false; - - if (OperatingSystem.isWindows() && - OSVersion.current().major() == 10) { - - // If DisableRealtimeMonitoring is not enabled then there - // may be a problem. - if (!WindowsRegistry.readDisableRealtimeMonitoring() && - !isDirectoryInExclusionPath(dir)) { - result = true; - } - } - - return result; - } - - private static boolean isDirectoryInExclusionPath(String dir) { - boolean result = false; - // If the user temp directory is not found in the exclusion - // list then there may be a problem. - List paths = WindowsRegistry.readExclusionsPaths(); - for (String s : paths) { - if (WindowsRegistry.comparePaths(s, dir)) { - result = true; - break; - } - } - - return result; - } - - static final String getUserTempDirectory() { - String tempDirectory = System.getProperty("java.io.tmpdir"); - return tempDirectory; - } -} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java deleted file mode 100644 index 7eb7b922667..00000000000 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.ArrayList; -import java.util.List; - -@SuppressWarnings("restricted") -final class WindowsRegistry { - - // Currently we only support HKEY_LOCAL_MACHINE. Native implementation will - // require support for additinal HKEY if needed. - private static final int HKEY_LOCAL_MACHINE = 1; - - static { - System.loadLibrary("jpackage"); - } - - private WindowsRegistry() {} - - /** - * Reads the registry value for DisableRealtimeMonitoring. - * @return true if DisableRealtimeMonitoring is set to 0x1, - * false otherwise. - */ - static final boolean readDisableRealtimeMonitoring() { - final String subKey = "Software\\Microsoft\\" - + "Windows Defender\\Real-Time Protection"; - final String value = "DisableRealtimeMonitoring"; - int result = readDwordValue(HKEY_LOCAL_MACHINE, subKey, value, 0); - return (result == 1); - } - - static final List readExclusionsPaths() { - List result = new ArrayList<>(); - final String subKey = "Software\\Microsoft\\" - + "Windows Defender\\Exclusions\\Paths"; - long lKey = openRegistryKey(HKEY_LOCAL_MACHINE, subKey); - if (lKey == 0) { - return result; - } - - String valueName; - int index = 0; - do { - valueName = enumRegistryValue(lKey, index); - if (valueName != null) { - result.add(valueName); - index++; - } - } while (valueName != null); - - closeRegistryKey(lKey); - - return result; - } - - /** - * Reads DWORD registry value. - * - * @param key one of HKEY predefine value - * @param subKey registry sub key - * @param value value to read - * @param defaultValue default value in case if subKey or value not found - * or any other errors occurred - * @return value's data only if it was read successfully, otherwise - * defaultValue - */ - private static native int readDwordValue(int key, String subKey, - String value, int defaultValue); - - /** - * Open registry key. - * - * @param key one of HKEY predefine value - * @param subKey registry sub key - * @return native handle to open key - */ - private static native long openRegistryKey(int key, String subKey); - - /** - * Enumerates the values for registry key. - * - * @param lKey native handle to open key returned by openRegistryKey - * @param index index of value starting from 0. Increment until this - * function returns NULL which means no more values. - * @return returns value or NULL if error or no more data - */ - private static native String enumRegistryValue(long lKey, int index); - - /** - * Close registry key. - * - * @param lKey native handle to open key returned by openRegistryKey - */ - private static native void closeRegistryKey(long lKey); - - /** - * Compares two Windows paths regardless case and if paths - * are short or long. - * - * @param path1 path to compare - * @param path2 path to compare - * @return true if paths point to same location - */ - public static native boolean comparePaths(String path1, String path2); -} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties index 38d0bd02bbb..13a6c989324 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties @@ -55,7 +55,6 @@ error.missing-service-installer='service-installer.exe' service installer not fo error.missing-service-installer.advice=Add 'service-installer.exe' service installer to the resource directory message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. -message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". message.tool-version=Detected [{0}] version [{1}]. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. From a6db3f870218bc016ecc67e3f5e142d6491ac080 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 3 Mar 2026 19:11:11 +0000 Subject: [PATCH 139/636] 8378873: jpackage: remove macOS-specific code from jdk.jpackage.internal.ModuleInfo Reviewed-by: almatvee --- .../jdk/jpackage/internal/FromOptions.java | 18 +++++++++++++++++- .../jdk/jpackage/internal/ModuleInfo.java | 15 ++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java index cccd05792d6..4f590850328 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java @@ -49,6 +49,7 @@ import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; @@ -149,6 +150,13 @@ final class FromOptions { ApplicationLayout appLayout, RuntimeLayout runtimeLayout, Optional predefinedRuntimeLayout) { + Objects.requireNonNull(options); + Objects.requireNonNull(launcherCtor); + Objects.requireNonNull(launcherOverrideCtor); + Objects.requireNonNull(appLayout); + Objects.requireNonNull(runtimeLayout); + Objects.requireNonNull(predefinedRuntimeLayout); + final var appBuilder = new ApplicationBuilder(); final var isRuntimeInstaller = isRuntimeInstaller(options); @@ -185,7 +193,15 @@ final class FromOptions { } else { appBuilder.appImageLayout(appLayout); - final var launchers = createLaunchers(options, launcherCtor); + // Adjust the value of the PREDEFINED_RUNTIME_IMAGE option to make it reference + // a directory with the standard Java runtime structure. + final var launcherOptions = predefinedRuntimeDirectory.filter(v -> { + return !predefinedRuntimeImage.get().equals(v); + }).map(v -> { + return Options.of(Map.of(PREDEFINED_RUNTIME_IMAGE, v)).copyWithParent(options); + }).orElse(options); + + final var launchers = createLaunchers(launcherOptions, launcherCtor); if (PREDEFINED_APP_IMAGE.containsIn(options)) { appBuilder.launchers(launchers); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java index 590321d5e86..71d1a66659e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleInfo.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 @@ -35,7 +35,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Properties; -import jdk.internal.util.OperatingSystem; record ModuleInfo(String name, Optional version, Optional mainClass, Optional location) { @@ -61,17 +60,7 @@ record ModuleInfo(String name, Optional version, Optional mainCl // is linked in the runtime by simply analysing the data // of `release` file. - final Path releaseFile; - if (!OperatingSystem.isMacOS()) { - releaseFile = cookedRuntime.resolve("release"); - } else { - // On Mac `cookedRuntime` can be runtime root or runtime home. - Path runtimeHome = cookedRuntime.resolve("Contents/Home"); - if (!Files.isDirectory(runtimeHome)) { - runtimeHome = cookedRuntime; - } - releaseFile = runtimeHome.resolve("release"); - } + final Path releaseFile = cookedRuntime.resolve("release"); try (Reader reader = Files.newBufferedReader(releaseFile)) { Properties props = new Properties(); From 73363a0cb501eb10f5be4a6a283b20da58b1950d Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 3 Mar 2026 21:53:20 +0000 Subject: [PATCH 140/636] 8378874: jpackage: remove redundant messages Reviewed-by: almatvee --- .../jdk/jpackage/internal/MacPkgPackager.java | 6 ------ .../internal/resources/MacResources.properties | 1 - .../jdk/jpackage/internal/WinMsiPackager.java | 3 --- .../internal/WixAppImageFragmentBuilder.java | 13 +------------ .../jdk/jpackage/internal/WixFragmentBuilder.java | 9 ++------- .../internal/resources/WinResources.properties | 1 - 6 files changed, 3 insertions(+), 30 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java index fc5c85c699c..369ab201611 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java @@ -51,7 +51,6 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import jdk.internal.util.Architecture; -import jdk.internal.util.OSVersion; import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.MacPkgPackage; @@ -509,11 +508,6 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic // maybe sign if (pkg.sign()) { - if (OSVersion.current().compareTo(new OSVersion(10, 12)) >= 0) { - // we need this for OS X 10.12+ - Log.verbose(I18N.getString("message.signing.pkg")); - } - final var pkgSigningConfig = pkg.signingConfig().orElseThrow(); commandLine.add("--sign"); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index 3aebdc39d41..90c4162b80e 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -61,7 +61,6 @@ message.invalid-identifier.advice=specify identifier with "--mac-package-identif message.preparing-dmg-setup=Preparing dmg setup: {0}. message.preparing-scripts=Preparing package scripts. message.preparing-distribution-dist=Preparing distribution.dist: {0}. -message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. message.setfile.dmg=Setting custom icon on DMG file skipped because 'SetFile' utility was not found. Installing Xcode with Command Line Tools should resolve this issue. message.codesign.failed.reason.app.content="codesign" failed and additional application content was supplied via the "--app-content" parameter. Probably the additional content broke the integrity of the application bundle and caused the failure. Ensure content supplied via the "--app-content" parameter does not break the integrity of the application bundle, or add it in the post-processing step. message.codesign.failed.reason.xcode.tools=Possible reason for "codesign" failure is missing Xcode with command line developer tools. Install Xcode with command line developer tools to see if it resolves the problem. diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java index c72b14a76d5..a53d083847a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java @@ -151,9 +151,6 @@ final class WinMsiPackager implements Consumer { wixFragments.forEach(wixFragment -> wixFragment.setWixVersion(wixToolset.getVersion(), wixToolset.getType())); - - wixFragments.stream().map(WixFragmentBuilder::getLoggableWixFeatures).flatMap( - List::stream).distinct().toList().forEach(Log::verbose); } WinMsiPackager(BuildEnv env, WinMsiPackage pkg, Path outputDir, WinSystemEnvironment sysEnv) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java index 0dff5c26ae2..ac7af873739 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.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 @@ -31,7 +31,6 @@ import static jdk.jpackage.internal.util.CollectionUtils.toCollection; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -142,16 +141,6 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { super.addFilesToConfigRoot(); } - @Override - List getLoggableWixFeatures() { - if (isWithWix36Features()) { - return List.of(MessageFormat.format(I18N.getString("message.use-wix36-features"), - getWixVersion())); - } else { - return List.of(); - } - } - @Override protected Collection getFragmentWriters() { return List.of( diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java index 4d180a94871..46894699d98 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.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 @@ -30,19 +30,18 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.nio.file.Path; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.stream.XMLStreamWriter; -import jdk.jpackage.internal.util.XmlConsumer; import jdk.internal.util.Architecture; import jdk.jpackage.internal.WixSourceConverter.ResourceGroup; import jdk.jpackage.internal.WixToolset.WixToolsetType; import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.WinMsiPackage; +import jdk.jpackage.internal.util.XmlConsumer; import jdk.jpackage.internal.util.XmlUtils; /** @@ -72,10 +71,6 @@ abstract class WixFragmentBuilder { fragmentResource = env.createResource(defaultResourceName).setPublicName(outputFileName); } - List getLoggableWixFeatures() { - return List.of(); - } - void configureWixPipeline(WixPipeline.Builder wixPipeline) { wixPipeline.addSource(configRoot.resolve(outputFileName), Optional.ofNullable(wixVariables).map(WixVariables::getValues).orElse( diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties index 13a6c989324..0f3dcab8260 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties @@ -57,7 +57,6 @@ error.missing-service-installer.advice=Add 'service-installer.exe' service insta message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. message.tool-version=Detected [{0}] version [{1}]. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. -message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. message.product-code=MSI ProductCode: {0}. message.upgrade-code=MSI UpgradeCode: {0}. message.preparing-msi-config=Preparing MSI config: {0}. From 0729d1d82207de856fdd8d2fe2a2ea4a0b8694a2 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Wed, 4 Mar 2026 00:36:08 +0000 Subject: [PATCH 141/636] 8379039: Build failure on vector API source generation after JDK-8378312 Reviewed-by: jbhateja, psandoz --- .../share/classes/jdk/incubator/vector/X-Vector.java.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index d6763c2c03a..95fe8ca35db 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -772,7 +772,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { @ForceInline final $abstractvectortype$ unaryMathOp(VectorOperators.Unary op) { - return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), $abstractvectortype$::unaryOperations, + return VectorMathLibrary.unaryMathOp(op, opCode(op), vspecies(), $abstractvectortype$::unaryOperations, this); } #end[FP] @@ -983,7 +983,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { @ForceInline final $abstractvectortype$ binaryMathOp(VectorOperators.Binary op, $abstractvectortype$ that) { - return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), $abstractvectortype$::binaryOperations, + return VectorMathLibrary.binaryMathOp(op, opCode(op), vspecies(), $abstractvectortype$::binaryOperations, this, that); } #end[FP] From 284d1310d059764b5fb887d4693af623c2f7e89a Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 4 Mar 2026 00:49:05 +0000 Subject: [PATCH 142/636] 8378876: jpackage: facilitate testing with mocks Reviewed-by: almatvee --- .../jpackage/internal/LibProvidersLookup.java | 2 +- .../LinuxDebSystemEnvironmentMixin.java | 3 ++ .../LinuxRpmSystemEnvironmentMixin.java | 4 +- .../internal/MacBundlingEnvironment.java | 5 +++ .../internal/MacDmgSystemEnvironment.java | 23 ++++++++--- .../jdk/jpackage/internal/AppImageFile.java | 13 ------- .../internal/DefaultBundlingEnvironment.java | 9 +++++ .../jpackage/internal/OptionsTransformer.java | 7 +++- .../jdk/jpackage/internal/PackageBuilder.java | 22 ++++++----- .../jdk/jpackage/internal/ToolValidator.java | 4 -- .../jdk/jpackage/internal/cli/Main.java | 39 +++++++++++-------- .../internal/cli/OptionsAnalyzer.java | 12 +++--- .../internal/cli/OptionsProcessor.java | 6 ++- .../jpackage/internal/cli/StandardOption.java | 6 +++ .../jdk/jpackage/test/AppImageFile.java | 16 ++++---- .../helpers/jdk/jpackage/test/JavaTool.java | 5 ++- .../helpers/jdk/jpackage/test/TKit.java | 39 +++++++++++++++---- .../test/stdmock/JPackageMockUtils.java | 2 +- .../jpackage/internal/AppImageFileTest.java | 16 +++++--- .../jdk/jpackage/internal/cli/MainTest.java | 6 ++- .../internal/cli/OptionsProcessorTest.java | 4 +- .../cli/OptionsValidationFailTest.java | 2 +- 22 files changed, 157 insertions(+), 88 deletions(-) diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java index 6faacbca528..2dccc91cf8f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java @@ -44,7 +44,7 @@ import java.util.stream.Stream; */ public final class LibProvidersLookup { static boolean supported() { - return (new ToolValidator(TOOL_LDD).validate() == null); + return (new ToolValidator(TOOL_LDD).setCommandLine("--version").validate() == null); } LibProvidersLookup setPackageLookup(PackageLookup v) { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java index 2cf3e9e36e8..4c6fd75bed8 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java @@ -40,6 +40,9 @@ interface LinuxDebSystemEnvironmentMixin { static Result create() { final var errors = Stream.of(Internal.TOOL_DPKG_DEB, Internal.TOOL_DPKG, Internal.TOOL_FAKEROOT) .map(ToolValidator::new) + .map(v -> { + return v.setCommandLine("--version"); + }) .map(ToolValidator::validate) .filter(Objects::nonNull) .toList(); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java index 4cbd3ce4a9c..8de28b79430 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java @@ -44,7 +44,9 @@ interface LinuxRpmSystemEnvironmentMixin { final var errors = Stream.of( Internal.createRpmbuildToolValidator(), new ToolValidator(Internal.TOOL_RPM) - ).map(ToolValidator::validate).filter(Objects::nonNull).toList(); + ).map(v -> { + return v.setCommandLine("--version"); + }).map(ToolValidator::validate).filter(Objects::nonNull).toList(); if (errors.isEmpty()) { return Result.ofValue(new Stub(Internal.TOOL_RPM, Internal.TOOL_RPMBUILD)); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java index 224ea20f249..1e60d8490f0 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java @@ -31,6 +31,7 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_APP import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_DMG; import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_PKG; import static jdk.jpackage.internal.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.EXIT_AFTER_CONFIGURATION_PHASE; import java.util.Optional; import jdk.jpackage.internal.cli.Options; @@ -74,6 +75,10 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { final var pkg = createSignAppImagePackage(app, env); + if (EXIT_AFTER_CONFIGURATION_PHASE.getFrom(options)) { + return; + } + buildPipeline(pkg).create().execute(env, pkg, env.appImageDir()); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java index 12d105b99b5..11cf94ca66d 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java @@ -31,6 +31,7 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.Result; record MacDmgSystemEnvironment(Path hdiutil, Path osascript, Optional setFileUtility) implements SystemEnvironment { @@ -39,12 +40,22 @@ record MacDmgSystemEnvironment(Path hdiutil, Path osascript, Optional setF } static Result create() { - final var errors = Stream.of(HDIUTIL, OSASCRIPT) - .map(ToolValidator::new) - .map(ToolValidator::checkExistsOnly) - .map(ToolValidator::validate) - .filter(Objects::nonNull) - .toList(); + + List errors; + + if (OperatingSystem.isMacOS()) { + errors = Stream.of(HDIUTIL, OSASCRIPT) + .map(ToolValidator::new) + .map(ToolValidator::checkExistsOnly) + .map(ToolValidator::validate) + .filter(Objects::nonNull) + .toList(); + } else { + // The code runs on an OS other than macOS. Presume this is mock testing. + // Don't validate the tools; checking that their executables exist will fail in this environment. + errors = List.of(); + } + if (errors.isEmpty()) { return Result.ofValue(new MacDmgSystemEnvironment(HDIUTIL, OSASCRIPT, findSetFileUtility())); } else { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java index 75ead9d08ad..7edc6cd3c11 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java @@ -134,19 +134,6 @@ final class AppImageFile { return appLayout.appDirectory().resolve(FILENAME); } - /** - * Loads application image info from the specified application layout. - *

- * It is an equivalent to calling - * {@link #load(ApplicationLayout, OperatingSystem)} method with - * {@code OperatingSystem.current()} for the second parameter. - * - * @param appLayout the application layout - */ - static ExternalApplication load(ApplicationLayout appLayout) { - return load(appLayout, OperatingSystem.current()); - } - /** * Loads application image info from the specified application layout and OS. * diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java index fc51f60aa66..ae4b1005318 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java @@ -25,6 +25,7 @@ package jdk.jpackage.internal; import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.cli.StandardOption.EXIT_AFTER_CONFIGURATION_PHASE; import static jdk.jpackage.internal.util.MemoizingSupplier.runOnce; import java.io.IOException; @@ -129,6 +130,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Objects.requireNonNull(app); Objects.requireNonNull(pipelineBuilder); + if (EXIT_AFTER_CONFIGURATION_PHASE.getFrom(options)) { + return; + } + final var outputDir = PathUtils.normalizedAbsolutePath(OptionUtils.outputDir(options).resolve(app.appImageDirName())); Log.verbose(I18N.getString("message.create-app-image")); @@ -170,6 +175,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Objects.requireNonNull(createPipelineBuilder); Objects.requireNonNull(pipelineBuilderMutatorFactory); + if (EXIT_AFTER_CONFIGURATION_PHASE.getFrom(options)) { + return; + } + var pipelineBuilder = Objects.requireNonNull(createPipelineBuilder.apply(pkg)); // Delete an old output package file (if any) before creating a new one. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionsTransformer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionsTransformer.java index a01be089a49..f7b535b06f6 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionsTransformer.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionsTransformer.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 @@ -46,7 +46,10 @@ record OptionsTransformer(Options mainOptions, Optional ext } OptionsTransformer(Options mainOptions, ApplicationLayout appLayout) { - this(mainOptions, PREDEFINED_APP_IMAGE.findIn(mainOptions).map(appLayout::resolveAt).map(AppImageFile::load)); + this(mainOptions, PREDEFINED_APP_IMAGE.findIn(mainOptions).map(appLayout::resolveAt).map(resolvedLayout -> { + var os = OptionUtils.bundlingOperation(mainOptions).os(); + return AppImageFile.load(resolvedLayout, os); + })); } Options appOptions() { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java index f537a1ea0a2..8b61ef19fa2 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.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 @@ -207,7 +207,7 @@ final class PackageBuilder { }); } - private static Path mapInstallDir(Path installDir, PackageType pkgType) { + private static Path mapInstallDir(Path installDir, PackageType type) { var ex = buildConfigException("error.invalid-install-dir", installDir).create(); if (installDir.getNameCount() == 0) { @@ -223,15 +223,17 @@ final class PackageBuilder { throw ex; } - switch (pkgType) { - case StandardPackageType.WIN_EXE, StandardPackageType.WIN_MSI -> { - if (installDir.isAbsolute()) { - throw ex; + if (type instanceof StandardPackageType stdType) { + switch (stdType) { + case WIN_EXE, WIN_MSI -> { + if (installDir.isAbsolute()) { + throw ex; + } } - } - default -> { - if (!installDir.isAbsolute()) { - throw ex; + default -> { + if (!installDir.isAbsolute()) { + throw ex; + } } } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java index 9440aff3a53..13a9e05e934 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java @@ -33,7 +33,6 @@ import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Stream; -import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.DottedVersion; @@ -46,9 +45,6 @@ final class ToolValidator { ToolValidator(Path toolPath) { this.toolPath = Objects.requireNonNull(toolPath); - if (OperatingSystem.isLinux()) { - setCommandLine("--version"); - } } ToolValidator setCommandLine(String... args) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java index d40895a7da6..5789203c3e9 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java @@ -64,14 +64,15 @@ import jdk.jpackage.internal.util.function.ExceptionBox; */ public final class Main { - public record Provider(Supplier bundlingEnvSupplier) implements ToolProvider { + public record Provider(Supplier bundlingEnvSupplier, OperatingSystem os) implements ToolProvider { public Provider { Objects.requireNonNull(bundlingEnvSupplier); + Objects.requireNonNull(os); } public Provider() { - this(DefaultBundlingEnvironmentLoader.INSTANCE); + this(DefaultBundlingEnvironmentLoader.INSTANCE, OperatingSystem.current()); } @Override @@ -81,7 +82,7 @@ public final class Main { @Override public int run(PrintWriter out, PrintWriter err, String... args) { - return Main.run(bundlingEnvSupplier, out, err, args); + return Main.run(os, bundlingEnvSupplier, out, err, args); } @Override @@ -107,25 +108,29 @@ public final class Main { public static void main(String... args) { var out = toPrintWriter(System.out); var err = toPrintWriter(System.err); - System.exit(run(out, err, args)); + System.exit(run(OperatingSystem.current(), DefaultBundlingEnvironmentLoader.INSTANCE, out, err, args)); } - static int run(PrintWriter out, PrintWriter err, String... args) { - return run(DefaultBundlingEnvironmentLoader.INSTANCE, out, err, args); - } - - static int run(Supplier bundlingEnvSupplier, PrintWriter out, PrintWriter err, String... args) { - return Globals.main(() -> { - return runWithGlobals(bundlingEnvSupplier, out, err, args); - }); - } - - private static int runWithGlobals( + static int run( + OperatingSystem os, Supplier bundlingEnvSupplier, PrintWriter out, PrintWriter err, String... args) { + return Globals.main(() -> { + return runWithGlobals(os, bundlingEnvSupplier, out, err, args); + }); + } + + private static int runWithGlobals( + OperatingSystem os, + Supplier bundlingEnvSupplier, + PrintWriter out, + PrintWriter err, + String... args) { + + Objects.requireNonNull(os); Objects.requireNonNull(bundlingEnvSupplier); Objects.requireNonNull(args); for (String arg : args) { @@ -162,7 +167,7 @@ public final class Main { final var bundlingEnv = bundlingEnvSupplier.get(); - final var parseResult = Utils.buildParser(OperatingSystem.current(), bundlingEnv).create().apply(mappedArgs.get()); + final var parseResult = Utils.buildParser(os, bundlingEnv).create().apply(mappedArgs.get()); return runner.run(() -> { final var parsedOptionsBuilder = parseResult.orElseThrow(); @@ -189,7 +194,7 @@ public final class Main { Globals.instance().loggerVerbose(); } - final var optionsProcessor = new OptionsProcessor(parsedOptionsBuilder, bundlingEnv); + final var optionsProcessor = new OptionsProcessor(parsedOptionsBuilder, os, bundlingEnv); final var validationResult = optionsProcessor.validate(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java index 6c32a3d1569..fb1dd20df92 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java @@ -63,8 +63,8 @@ import jdk.jpackage.internal.model.BundleType; */ final class OptionsAnalyzer { - OptionsAnalyzer(Options cmdline, BundlingEnvironment bundlingEnv) { - this(cmdline, getBundlingOperation(cmdline, OperatingSystem.current(), bundlingEnv), false); + OptionsAnalyzer(Options cmdline, OperatingSystem os, BundlingEnvironment bundlingEnv) { + this(cmdline, getBundlingOperation(cmdline, os, bundlingEnv), false); } OptionsAnalyzer(Options cmdline, StandardBundlingOperation bundlingOperation) { @@ -291,17 +291,17 @@ final class OptionsAnalyzer { return bundlingOperations.getFirst(); } else { // Multiple standard bundling operations produce the `bundleType` bundle type. - // Filter those that belong to the current OS + // Filter those that belong to the specified OS. bundlingOperations = bundlingOperations.stream().filter(op -> { - return op.os().equals(OperatingSystem.current()); + return op.os().equals(os); }).toList(); if (bundlingOperations.isEmpty()) { // jpackage internal error: none of the standard bundling operations produce - // bundles of the `bundleType` on the current OS. + // bundles of the `bundleType` on the specified OS. throw new AssertionError(String.format( "None of the standard bundling operations produce bundles of type [%s] on %s", - bundleType, OperatingSystem.current())); + bundleType, os)); } else if (bundlingOperations.size() == 1) { return bundlingOperations.getFirst(); } else if (StandardBundlingOperation.MACOS_APP_IMAGE.containsAll(bundlingOperations)) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.java index 3c0eab77f8a..387c36ea416 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.java @@ -62,8 +62,9 @@ import jdk.jpackage.internal.util.Result; */ final class OptionsProcessor { - OptionsProcessor(OptionsBuilder optionsBuilder, CliBundlingEnvironment bundlingEnv) { + OptionsProcessor(OptionsBuilder optionsBuilder, OperatingSystem os, CliBundlingEnvironment bundlingEnv) { this.optionsBuilder = Objects.requireNonNull(optionsBuilder); + this.os = Objects.requireNonNull(os); this.bundlingEnv = Objects.requireNonNull(bundlingEnv); } @@ -88,7 +89,7 @@ final class OptionsProcessor { final var untypedOptions = optionsBuilder.create(); // Create command line structure analyzer. - final var analyzerResult = Result.of(() -> new OptionsAnalyzer(untypedOptions, bundlingEnv)); + final var analyzerResult = Result.of(() -> new OptionsAnalyzer(untypedOptions, os, bundlingEnv)); if (analyzerResult.hasErrors()) { // Failed to derive the bundling operation from the command line. allErrors.addAll(analyzerResult.mapErrors().errors()); @@ -419,6 +420,7 @@ final class OptionsProcessor { private final JOptSimpleOptionsBuilder.OptionsBuilder optionsBuilder; + private final OperatingSystem os; private final CliBundlingEnvironment bundlingEnv; private static final OptionValue> ADD_LAUNCHER_INTERNAL = diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index eca82da99aa..f554fc12ec6 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java @@ -434,6 +434,12 @@ public final class StandardOption { public static final OptionValue BUNDLING_OPERATION_DESCRIPTOR = OptionValue.create(); + /** + * Debug option telling bundler to exit after the configuration phase is over, + * without running the packaging phase. + */ + public static final OptionValue EXIT_AFTER_CONFIGURATION_PHASE = OptionValue.build().defaultValue(false).create(); + /** * Returns options configuring a launcher. * diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java index 55b13a06620..2dcccc673a9 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java @@ -41,7 +41,6 @@ import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -195,13 +194,16 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche } private static String getPlatform() { - return PLATFORM_LABELS.get(OperatingSystem.current()); + if (TKit.isLinux()) { + return "linux"; + } else if (TKit.isWindows()) { + return "windows"; + } else if (TKit.isOSX()) { + return "macOS"; + } else { + throw new IllegalStateException(); + } } private static final String FILENAME = ".jpackage.xml"; - - private static final Map PLATFORM_LABELS = Map.of( - OperatingSystem.LINUX, "linux", - OperatingSystem.WINDOWS, "windows", - OperatingSystem.MACOS, "macOS"); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.java index 0bbf71fe0cd..4ca4b918355 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.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 @@ -27,6 +27,7 @@ package jdk.jpackage.test; import java.nio.file.Path; import java.util.spi.ToolProvider; +import jdk.internal.util.OperatingSystem; public enum JavaTool { JAVA, JAVAC, JPACKAGE, JAR, JLINK, JMOD, JSHELL; @@ -50,7 +51,7 @@ public enum JavaTool { private Path relativePathInJavaHome() { Path path = Path.of("bin", toolName()); - if (TKit.isWindows()) { + if (OperatingSystem.isWindows()) { path = path.getParent().resolve(path.getFileName().toString() + ".exe"); } return path; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index 88d11d75f45..db76309c292 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -77,6 +77,9 @@ import jdk.jpackage.internal.util.function.ThrowingUnaryOperator; public final class TKit { + private static final ScopedValue STATE = ScopedValue.newInstance(); + private static final State DEFAULT_STATE = State.build().initDefaults().mutable(false).create(); + public static final Path TEST_SRC_ROOT = Functional.identity(() -> { Path root = Path.of(System.getProperty("test.src")); @@ -128,6 +131,15 @@ public final class TKit { } } + public static void withOperatingSystem(ThrowingRunnable action, OperatingSystem os) { + Objects.requireNonNull(action); + Objects.requireNonNull(os); + + withState(action, stateBuilder -> { + stateBuilder.os(os); + }); + } + public static void withState(ThrowingRunnable action, Consumer stateBuilderMutator) { Objects.requireNonNull(action); Objects.requireNonNull(stateBuilderMutator); @@ -233,21 +245,25 @@ public final class TKit { } public static boolean isWindows() { - return OperatingSystem.isWindows(); + return TKit.state().os == OperatingSystem.WINDOWS; } public static boolean isOSX() { - return OperatingSystem.isMacOS(); + return TKit.state().os == OperatingSystem.MACOS; } public static boolean isLinux() { - return OperatingSystem.isLinux(); + return TKit.state().os == OperatingSystem.LINUX; } public static boolean isLinuxAPT() { return isLinux() && Files.exists(Path.of("/usr/bin/apt-get")); } + public static boolean isMockingOperatingSystem() { + return TKit.state().os != OperatingSystem.current(); + } + private static String addTimestamp(String msg) { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); Date time = new Date(System.currentTimeMillis()); @@ -1314,6 +1330,7 @@ public final class TKit { public static final class State { private State( + OperatingSystem os, TestInstance currentTest, PrintStream out, PrintStream err, @@ -1323,10 +1340,12 @@ public final class TKit { boolean verboseJPackage, boolean verboseTestSetup) { + Objects.requireNonNull(os); Objects.requireNonNull(out); Objects.requireNonNull(err); Objects.requireNonNull(properties); + this.os = os; this.currentTest = currentTest; this.out = out; this.err = err; @@ -1371,6 +1390,7 @@ public final class TKit { static final class Builder { Builder initDefaults() { + os = null; currentTest = null; out = System.out; err = System.err; @@ -1403,6 +1423,7 @@ public final class TKit { } Builder initFrom(State state) { + os = state.os; currentTest = state.currentTest; out = state.out; err = state.err; @@ -1418,6 +1439,11 @@ public final class TKit { return this; } + Builder os(OperatingSystem v) { + os = v; + return this; + } + Builder currentTest(TestInstance v) { currentTest = v; return this; @@ -1449,6 +1475,7 @@ public final class TKit { State create() { return new State( + Optional.ofNullable(os).orElseGet(OperatingSystem::current), currentTest, out, err, @@ -1459,6 +1486,7 @@ public final class TKit { verboseTestSetup); } + private OperatingSystem os; private TestInstance currentTest; private PrintStream out; private PrintStream err; @@ -1474,6 +1502,7 @@ public final class TKit { } + private OperatingSystem os; private final TestInstance currentTest; private final PrintStream out; private final PrintStream err; @@ -1486,8 +1515,4 @@ public final class TKit { private final boolean verboseJPackage; private final boolean verboseTestSetup; } - - - private static final ScopedValue STATE = ScopedValue.newInstance(); - private static final State DEFAULT_STATE = State.build().initDefaults().mutable(false).create(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java index 604bcd0711f..085102390ff 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java @@ -213,7 +213,7 @@ public final class JPackageMockUtils { var impl = new Main.Provider(runOnce(() -> { return createBundlingEnvironment(os); - })); + }), os); return new ToolProvider() { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java index 8fdcc96acff..85b15d77052 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java @@ -108,7 +108,9 @@ public class AppImageFileTest { @Test public void testNoSuchFile() throws IOException { - var ex = assertThrowsExactly(JPackageException.class, () -> AppImageFile.load(DUMMY_LAYOUT.resolveAt(tempFolder))); + var ex = assertThrowsExactly(JPackageException.class, () -> { + AppImageFile.load(DUMMY_LAYOUT.resolveAt(tempFolder), OperatingSystem.current()); + }); Assertions.assertEquals(I18N.format("error.missing-app-image-file", ".jpackage.xml", tempFolder), ex.getMessage()); assertNull(ex.getCause()); } @@ -117,7 +119,9 @@ public class AppImageFileTest { public void testDirectory() throws IOException { Files.createDirectory(AppImageFile.getPathInAppImage(DUMMY_LAYOUT.resolveAt(tempFolder))); - var ex = assertThrowsExactly(JPackageException.class, () -> AppImageFile.load(DUMMY_LAYOUT.resolveAt(tempFolder))); + var ex = assertThrowsExactly(JPackageException.class, () -> { + AppImageFile.load(DUMMY_LAYOUT.resolveAt(tempFolder), OperatingSystem.current()); + }); Assertions.assertEquals(I18N.format("error.reading-app-image-file", ".jpackage.xml", tempFolder), ex.getMessage()); assertNotNull(ex.getCause()); } @@ -131,7 +135,9 @@ public class AppImageFileTest { Files.writeString(appImageFile, ""); try (var out = new FileOutputStream(appImageFile.toFile()); var lock = out.getChannel().lock()) { - var ex = assertThrowsExactly(JPackageException.class, () -> AppImageFile.load(DUMMY_LAYOUT.resolveAt(tempFolder))); + var ex = assertThrowsExactly(JPackageException.class, () -> { + AppImageFile.load(DUMMY_LAYOUT.resolveAt(tempFolder), OperatingSystem.current()); + }); Assertions.assertEquals(I18N.format("error.reading-app-image-file", ".jpackage.xml", tempFolder), ex.getMessage()); assertNotNull(ex.getCause()); } @@ -237,7 +243,7 @@ public class AppImageFileTest { final var copy = toSupplier(() -> { var layout = DUMMY_LAYOUT.resolveAt(dir); new AppImageFile(app).save(layout); - return AppImageFile.load(layout); + return AppImageFile.load(layout, OperatingSystem.current()); }).get(); assertEquals(createExternalApplication(OperatingSystem.current()), copy); @@ -638,7 +644,7 @@ public class AppImageFileTest { private static final ApplicationLayout DUMMY_LAYOUT = ApplicationLayout.build().setAll("").create(); - private final static Map OPTIONS = Stream.of(AppImageFileOptionScope.values()) + private static final Map OPTIONS = Stream.of(AppImageFileOptionScope.values()) .flatMap(AppImageFileOptionScope::options) .collect(toMap(OptionValue::id, OptionValue::getName)); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java index 1c06d592006..3f933093e97 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java @@ -461,7 +461,11 @@ public class MainTest extends JUnitAdapter { var stdout = new StringWriter(); var stderr = new StringWriter(); - var exitCode = Main.run(new PrintWriter(stdout), new PrintWriter(stderr), args); + var os = OperatingSystem.current(); + var exitCode = Main.run(os, () -> { + CliBundlingEnvironment bundlingEnv = JPackageMockUtils.createBundlingEnvironment(os); + return bundlingEnv; + }, new PrintWriter(stdout), new PrintWriter(stderr), args); return new ExecutionResult(lines(stdout.toString()), lines(stderr.toString()), exitCode); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsProcessorTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsProcessorTest.java index 53b726d8f1a..499aab2f24a 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsProcessorTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsProcessorTest.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 @@ -664,7 +664,7 @@ public class OptionsProcessorTest { var optionsBuilder = Utils.buildParser(os, bundlingEnv).create().apply(stringArgs).orElseThrow(); - var op = new OptionsProcessor(optionsBuilder, bundlingEnv); + var op = new OptionsProcessor(optionsBuilder, OperatingSystem.current(), bundlingEnv); Collection> errors; if (expectedValidationErrorsOrdered) { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java index 9976a71ef3f..3381677cb61 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java @@ -105,7 +105,7 @@ public class OptionsValidationFailTest { final var firstErr = errors.stream().findFirst().orElseThrow(); errorReporter.reportError(firstErr); }).map(builder -> { - var result = new OptionsProcessor(builder, bundlingEnv).validate(); + var result = new OptionsProcessor(builder, OperatingSystem.current(), bundlingEnv).validate(); if (result.hasValue()) { return 0; } else { From 7f518deb2c6ac54867a266ce16023183e7ced053 Mon Sep 17 00:00:00 2001 From: Frederic Thevenet Date: Wed, 4 Mar 2026 05:48:30 +0000 Subject: [PATCH 143/636] 8378584: (process) Investigate and fix Alpine errors in Basic.java ProcessBuilder test, and re-enable tests Reviewed-by: stuefe --- test/jdk/java/lang/ProcessBuilder/Basic.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/lang/ProcessBuilder/Basic.java b/test/jdk/java/lang/ProcessBuilder/Basic.java index 7034174f85c..dd97de8fad8 100644 --- a/test/jdk/java/lang/ProcessBuilder/Basic.java +++ b/test/jdk/java/lang/ProcessBuilder/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -33,7 +33,6 @@ * @summary Basic tests for Process and Environment Variable code * @modules java.base/java.lang:open * java.base/java.io:open - * @requires !vm.musl * @requires vm.flagless * @library /test/lib * @run main/othervm/native/timeout=360 Basic @@ -46,7 +45,7 @@ * @modules java.base/java.lang:open * java.base/java.io:open * java.base/jdk.internal.misc - * @requires (os.family == "linux" & !vm.musl) + * @requires (os.family == "linux") * @library /test/lib * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=posix_spawn Basic */ From d9e256d3b2bb96ab079ccab51a5d3b32900aa632 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Wed, 4 Mar 2026 06:11:02 +0000 Subject: [PATCH 144/636] 8372245: GTest globalDefinitions.format_specifiers cannot run without VM Reviewed-by: kbarrett, iwalulya --- test/hotspot/gtest/utilities/test_globalDefinitions.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp index 6636efbba8e..b8b65ae8ca8 100644 --- a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp +++ b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -22,7 +22,6 @@ */ #include "cppstdlib/type_traits.hpp" -#include "memory/resourceArea.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" @@ -220,10 +219,9 @@ TEST(globalDefinitions, array_size) { #define check_format(format, value, expected) \ do { \ - ResourceMark rm; \ stringStream out; \ out.print((format), (value)); \ - const char* result = out.as_string(); \ + const char* result = out.base(); \ EXPECT_STREQ((result), (expected)) << "Failed with" \ << " format '" << (format) << "'" \ << " value '" << (value); \ From 3cd0b99990ae50579e6f99c043beb1ab3f2f5e89 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Wed, 4 Mar 2026 06:13:05 +0000 Subject: [PATCH 145/636] 8372248: GTest istream.coverage depends on istream.basic Reviewed-by: kbarrett, iwalulya --- test/hotspot/gtest/utilities/test_istream.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/hotspot/gtest/utilities/test_istream.cpp b/test/hotspot/gtest/utilities/test_istream.cpp index c07450822af..643d8cf8fcf 100644 --- a/test/hotspot/gtest/utilities/test_istream.cpp +++ b/test/hotspot/gtest/utilities/test_istream.cpp @@ -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 @@ -305,16 +305,13 @@ static void istream_test_driver(const bool VERBOSE, } TEST_VM(istream, basic) { - const bool VERBOSE = false; - istream_test_driver(VERBOSE, false, false, false); -} - -TEST_VM(istream, coverage) { - const bool VERBOSE = false; + const bool VERBOSE_TEST = false; + const bool VERBOSE_COVERAGE = false; + istream_test_driver(VERBOSE_TEST, false, false, false); #ifdef ASSERT istream_coverage_mode(0, cases, total, zeroes); if (cases == 0) return; - if (VERBOSE || zeroes != 0) + if (VERBOSE_COVERAGE || zeroes != 0) istream_coverage_mode(-1, cases, total, zeroes); EXPECT_EQ(zeroes, 0) << "zeroes: " << zeroes << "/" << cases; #endif //ASSERT From 39b1e9d839c0f0089565c55accdcab5337839fbf Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Wed, 4 Mar 2026 07:07:00 +0000 Subject: [PATCH 146/636] 8372247: OSX: Semaphore.trywait requires os::Bsd::clock_init Reviewed-by: dholmes, kbarrett --- src/hotspot/os/bsd/semaphore_bsd.cpp | 44 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/hotspot/os/bsd/semaphore_bsd.cpp b/src/hotspot/os/bsd/semaphore_bsd.cpp index 827c955677e..c35712ff2da 100644 --- a/src/hotspot/os/bsd/semaphore_bsd.cpp +++ b/src/hotspot/os/bsd/semaphore_bsd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -81,27 +81,37 @@ bool OSXSemaphore::timedwait(int64_t millis) { // kernel semaphores take a relative timeout mach_timespec_t waitspec; - int secs = millis / MILLIUNITS; - int nsecs = millis_to_nanos(millis % MILLIUNITS); - waitspec.tv_sec = secs; - waitspec.tv_nsec = nsecs; + int64_t starttime; + const bool is_trywait = millis == 0; - int64_t starttime = os::javaTimeNanos(); + if (!is_trywait) { + int secs = millis / MILLIUNITS; + int nsecs = millis_to_nanos(millis % MILLIUNITS); + waitspec.tv_sec = secs; + waitspec.tv_nsec = nsecs; + + starttime = os::javaTimeNanos(); + } else { + waitspec.tv_sec = 0; + waitspec.tv_nsec = 0; + } kr = semaphore_timedwait(_semaphore, waitspec); while (kr == KERN_ABORTED) { - // reduce the timeout and try again - int64_t totalwait = millis_to_nanos(millis); - int64_t current = os::javaTimeNanos(); - int64_t passedtime = current - starttime; + if (!is_trywait) { + // reduce the timeout and try again + int64_t totalwait = millis_to_nanos(millis); + int64_t current = os::javaTimeNanos(); + int64_t passedtime = current - starttime; - if (passedtime >= totalwait) { - waitspec.tv_sec = 0; - waitspec.tv_nsec = 0; - } else { - int64_t waittime = totalwait - (current - starttime); - waitspec.tv_sec = waittime / NANOSECS_PER_SEC; - waitspec.tv_nsec = waittime % NANOSECS_PER_SEC; + if (passedtime >= totalwait) { + waitspec.tv_sec = 0; + waitspec.tv_nsec = 0; + } else { + int64_t waittime = totalwait - (current - starttime); + waitspec.tv_sec = waittime / NANOSECS_PER_SEC; + waitspec.tv_nsec = waittime % NANOSECS_PER_SEC; + } } kr = semaphore_timedwait(_semaphore, waitspec); From 58d2c1d47db8a7defe2c3319cfab943296cf34f1 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 4 Mar 2026 07:17:26 +0000 Subject: [PATCH 147/636] 8371155: Type annotations on local variables are classified after the local var initializer has been type checked Reviewed-by: vromero --- .../sun/tools/javac/code/TypeAnnotations.java | 94 +++++--- .../com/sun/tools/javac/comp/Attr.java | 5 +- .../TypeAnnotationsOnVariables.java | 211 ++++++++++++++++++ 3 files changed, 283 insertions(+), 27 deletions(-) create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java 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 86319f20c73..8eba79c7480 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 @@ -50,6 +50,7 @@ import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Type.ModuleType; +import com.sun.tools.javac.code.Type.UnionClassType; import com.sun.tools.javac.comp.Annotate; import com.sun.tools.javac.comp.Attr; import com.sun.tools.javac.comp.AttrContext; @@ -61,6 +62,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotatedType; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; @@ -70,6 +72,7 @@ import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCNewClass; +import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeIntersection; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; @@ -111,6 +114,7 @@ public class TypeAnnotations { final Symtab syms; final Annotate annotate; final Attr attr; + final Types types; @SuppressWarnings("this-escape") protected TypeAnnotations(Context context) { @@ -120,6 +124,7 @@ public class TypeAnnotations { syms = Symtab.instance(context); annotate = Annotate.instance(context); attr = Attr.instance(context); + types = Types.instance(context); } /** @@ -132,7 +137,24 @@ public class TypeAnnotations { annotate.afterTypes(() -> { JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile); try { - new TypeAnnotationPositions(true).scan(tree); + new TypeAnnotationPositions(null, true).scan(tree); + } finally { + log.useSource(oldSource); + } + }); + } + + public void organizeTypeAnnotationsSignaturesForLocalVarType(final Env env, final JCVariableDecl tree) { + annotate.afterTypes(() -> { + JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile); + try { + TypeAnnotationPositions pos = new TypeAnnotationPositions(env.tree, true); + if (env.tree instanceof JCLambda) { + pos.push(env.tree); + } else { + pos.push(env.enclMethod); + } + pos.scan(tree); } finally { log.useSource(oldSource); } @@ -155,7 +177,7 @@ public class TypeAnnotations { * top-level blocks, and method bodies, and should be called from Attr. */ public void organizeTypeAnnotationsBodies(JCClassDecl tree) { - new TypeAnnotationPositions(false).scan(tree); + new TypeAnnotationPositions(null, false).scan(tree); } public enum AnnotationType { DECLARATION, TYPE, NONE, BOTH } @@ -265,9 +287,11 @@ public class TypeAnnotations { private class TypeAnnotationPositions extends TreeScanner { + private final JCTree contextTree; private final boolean sigOnly; - TypeAnnotationPositions(boolean sigOnly) { + TypeAnnotationPositions(JCTree contextTree, boolean sigOnly) { + this.contextTree = contextTree; this.sigOnly = sigOnly; } @@ -455,14 +479,15 @@ public class TypeAnnotations { return type.annotatedType(onlyTypeAnnotations); } else if (type.getKind() == TypeKind.UNION) { // There is a TypeKind, but no TypeTag. + UnionClassType ut = (UnionClassType) type; JCTypeUnion tutree = (JCTypeUnion)typetree; JCExpression fst = tutree.alternatives.get(0); Type res = typeWithAnnotations(fst, fst.type, annotations, onlyTypeAnnotations, pos); fst.type = res; - // TODO: do we want to set res as first element in uct.alternatives? - // UnionClassType uct = (com.sun.tools.javac.code.Type.UnionClassType)type; - // Return the un-annotated union-type. - return type; + ListBuffer alternatives = new ListBuffer<>(); + alternatives.add(res); + alternatives.addAll(ut.alternatives_field.tail); + return new UnionClassType((ClassType) ut.getLub(), alternatives.toList()); } else { Type enclTy = type; Element enclEl = type.asElement(); @@ -1237,7 +1262,17 @@ public class TypeAnnotations { } else if (tree.sym == null) { Assert.error("Visiting tree node before memberEnter"); } else if (tree.sym.getKind() == ElementKind.PARAMETER) { - // Parameters are handled in visitMethodDef or visitLambda. + if (sigOnly) { + if (contextTree instanceof JCCatch c && c.param == tree) { + //exception "parameter": + final TypeAnnotationPosition pos = + TypeAnnotationPosition.exceptionParameter(currentLambda, + tree.pos); + separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + } else { + // (real) parameters are handled in visitMethodDef or visitLambda. + } + } } else if (tree.sym.getKind() == ElementKind.FIELD) { if (sigOnly) { TypeAnnotationPosition pos = @@ -1245,27 +1280,36 @@ public class TypeAnnotations { separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); } } else if (tree.sym.getKind() == ElementKind.LOCAL_VARIABLE) { - final TypeAnnotationPosition pos = - TypeAnnotationPosition.localVariable(currentLambda, - tree.pos); - if (!tree.declaredUsingVar()) { - separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + if (sigOnly && !tree.declaredUsingVar()) { + if (contextTree instanceof JCTry t && t.resources.contains(tree)) { + final TypeAnnotationPosition pos = + TypeAnnotationPosition.resourceVariable(currentLambda, + tree.pos); + separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + } else { + final TypeAnnotationPosition pos = + TypeAnnotationPosition.localVariable(currentLambda, + tree.pos); + if (!tree.declaredUsingVar()) { + separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + } + } } } else if (tree.sym.getKind() == ElementKind.BINDING_VARIABLE) { - final TypeAnnotationPosition pos = - TypeAnnotationPosition.localVariable(currentLambda, - tree.pos); - separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + if (sigOnly) { + final TypeAnnotationPosition pos = + TypeAnnotationPosition.localVariable(currentLambda, + tree.pos); + separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + } } else if (tree.sym.getKind() == ElementKind.EXCEPTION_PARAMETER) { - final TypeAnnotationPosition pos = - TypeAnnotationPosition.exceptionParameter(currentLambda, - tree.pos); - separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + if (sigOnly) { + Assert.error("Should not get variable kind: " + tree.sym.getKind()); + } } else if (tree.sym.getKind() == ElementKind.RESOURCE_VARIABLE) { - final TypeAnnotationPosition pos = - TypeAnnotationPosition.resourceVariable(currentLambda, - tree.pos); - separateAnnotationsKinds(tree, tree.vartype, tree.sym.type, tree.sym, pos); + if (sigOnly) { + Assert.error("Should not get variable kind: " + tree.sym.getKind()); + } } else if (tree.sym.getKind() == ElementKind.ENUM_CONSTANT) { // No type annotations can occur here. } else { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 83b684e1225..444530c7266 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1281,6 +1281,7 @@ public class Attr extends JCTree.Visitor { try { annotate.blockAnnotations(); memberEnter.memberEnter(tree, env); + typeAnnotations.organizeTypeAnnotationsSignaturesForLocalVarType(env, tree); } finally { annotate.unblockAnnotations(); } @@ -4226,7 +4227,6 @@ public class Attr extends JCTree.Visitor { } else { type = resultInfo.pt; } - tree.type = tree.var.type = type; BindingSymbol v = new BindingSymbol(tree.var.mods.flags | tree.var.declKind.additionalSymbolFlags, tree.var.name, type, env.info.scope.owner); v.pos = tree.pos; @@ -4244,7 +4244,8 @@ public class Attr extends JCTree.Visitor { annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v); } annotate.flush(); - result = tree.type; + typeAnnotations.organizeTypeAnnotationsSignaturesForLocalVarType(env, tree.var); + result = tree.type = tree.var.type = v.type; if (v.isUnnamedVariable()) { matchBindings = MatchBindingsComputer.EMPTY; } else { diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java new file mode 100644 index 00000000000..2e68e18f8f7 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java @@ -0,0 +1,211 @@ +/* + * 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 8371155 + * @summary Verify type annotations on local-like variables are propagated to + * their types at an appropriate time. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main TypeAnnotationsOnVariables + */ + +import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.UnionType; +import toolbox.JavacTask; +import toolbox.ToolBox; + +public class TypeAnnotationsOnVariables { + + public static void main(String... args) throws Exception { + new TypeAnnotationsOnVariables().run(); + } + + ToolBox tb = new ToolBox(); + + void run() throws Exception { + typeAnnotationInConstantExpressionFieldInit(Paths.get(".")); + } + + void typeAnnotationInConstantExpressionFieldInit(Path base) throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + import java.util.function.Supplier; + + class Test { + @Target(ElementType.TYPE_USE) + @interface TypeAnno { } + + @TypeAnno Supplier r_f_i = () -> "r_f_i"; + static @TypeAnno Supplier r_f_s = () -> "r_f_s"; + + { + @TypeAnno Supplier r_init_i = () -> "r_init_i"; + } + + static { + @TypeAnno Supplier r_init_s = () -> "r_init_s"; + } + + void m() { + @TypeAnno Supplier r_m_i = () -> "r_m_i"; + } + + static void g() { + @TypeAnno Supplier r_g_s = () -> "r_g_s"; + } + + void h() { + t_cr(() -> "t_cr"); + } + + void i() { + t_no_cr((@TypeAnno Supplier)() -> "t_no_cr"); + } + + void j() { + t_no_cr((java.io.Serializable & @TypeAnno Supplier)() -> "t_no_cr"); + } + + void k() throws Throwable { + try (@TypeAnno AutoCloseable ac = () -> {}) {} + } + + void l() { + try { + } catch (@TypeAnno Exception e1) {} + } + + void n() { + try { + } catch (@TypeAnno final Exception e2) {} + } + + void o() { + try { + } catch (@TypeAnno IllegalStateException | @TypeAnno NullPointerException | IllegalArgumentException e3) {} + } + + void t_cr(@TypeAnno Supplier r_p) { } + void t_no_cr(@TypeAnno Supplier r_p) { } + } + """); + Files.createDirectories(classes); + List actual = new ArrayList<>(); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + Trees trees = Trees.instance(task); + new TreePathScanner() { + @Override + public Void visitVariable(VariableTree node, Void p) { + actual.add(node.getName() + ": " + typeToString(trees.getTypeMirror(getCurrentPath()))); + return super.visitVariable(node, p); + } + @Override + public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { + actual.add(treeToString(node)+ ": " + typeToString(trees.getTypeMirror(getCurrentPath()))); + return super.visitLambdaExpression(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + }); + }) + .run() + .writeAll(); + + List expected = List.of( + "r_f_i: java.util.function.@Test.TypeAnno Supplier", + "()->\"r_f_i\": java.util.function.@Test.TypeAnno Supplier", + "r_f_s: java.util.function.@Test.TypeAnno Supplier", + "()->\"r_f_s\": java.util.function.@Test.TypeAnno Supplier", + "r_init_i: java.util.function.@Test.TypeAnno Supplier", + "()->\"r_init_i\": java.util.function.@Test.TypeAnno Supplier", + "r_init_s: java.util.function.@Test.TypeAnno Supplier", + "()->\"r_init_s\": java.util.function.@Test.TypeAnno Supplier", + "r_m_i: java.util.function.@Test.TypeAnno Supplier", + "()->\"r_m_i\": java.util.function.@Test.TypeAnno Supplier", + "r_g_s: java.util.function.@Test.TypeAnno Supplier", + "()->\"r_g_s\": java.util.function.@Test.TypeAnno Supplier", + "()->\"t_cr\": java.util.function.@Test.TypeAnno Supplier", + "()->\"t_no_cr\": java.util.function.@Test.TypeAnno Supplier", + "()->\"t_no_cr\": java.lang.Object&java.io.Serializable&java.util.function.@Test.TypeAnno Supplier", + "ac: java.lang.@Test.TypeAnno AutoCloseable", + "()->{ }: java.lang.@Test.TypeAnno AutoCloseable", + "e1: java.lang.@Test.TypeAnno Exception", + "e2: java.lang.@Test.TypeAnno Exception", + "e3: java.lang.@Test.TypeAnno IllegalStateException | java.lang.@Test.TypeAnno NullPointerException | java.lang.IllegalArgumentException", + "r_p: java.util.function.@Test.TypeAnno Supplier", + "r_p: java.util.function.@Test.TypeAnno Supplier" + ); + + actual.forEach(System.out::println); + if (!expected.equals(actual)) { + throw new AssertionError("Expected: " + expected + ", but got: " + actual); + } + } + + static String typeToString(TypeMirror type) { + if (type != null && type.getKind() == TypeKind.UNION) { + return ((UnionType) type).getAlternatives().stream().map(t -> typeToString(t)).collect(Collectors.joining(" | ")); + } else { + return String.valueOf(type); + } + } + + static String treeToString(Tree tree) { + if (tree.toString().contains("\n")) { + System.err.println("!!!"); + } + return String.valueOf(tree).replaceAll("\\R", " "); + } +} From f7918df73318892cf2330812669f9c263f513127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 4 Mar 2026 07:40:44 +0000 Subject: [PATCH 148/636] 8378779: NBody demo test times out with C1 stress testing Reviewed-by: epeter, dfenacci --- .../jtreg/compiler/gallery/TestParticleLife.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/gallery/TestParticleLife.java b/test/hotspot/jtreg/compiler/gallery/TestParticleLife.java index e0fb6164acf..bb1eea927c7 100644 --- a/test/hotspot/jtreg/compiler/gallery/TestParticleLife.java +++ b/test/hotspot/jtreg/compiler/gallery/TestParticleLife.java @@ -91,7 +91,7 @@ public class TestParticleLife { ParticleLife.State state = new ParticleLife.State(); @Test - @Warmup(100) + @Warmup(75) @IR(counts = {IRNode.REPLICATE_F, "> 0", IRNode.LOAD_VECTOR_F, "> 0", IRNode.SUB_VF, "> 0", @@ -109,7 +109,7 @@ public class TestParticleLife { } @Test - @Warmup(10) + @Warmup(2) @IR(counts = {IRNode.REPLICATE_F, "= 0", IRNode.LOAD_VECTOR_F, "= 0", IRNode.REPLICATE_I, "= 0", @@ -138,7 +138,7 @@ public class TestParticleLife { } @Test - @Warmup(10) + @Warmup(2) @IR(counts = {IRNode.REPLICATE_F, "> 0", IRNode.LOAD_VECTOR_F, "> 0", IRNode.REPLICATE_I, "> 0", @@ -164,7 +164,7 @@ public class TestParticleLife { } @Test - @Warmup(10) + @Warmup(2) @IR(counts = {IRNode.REPLICATE_F, "> 0", IRNode.LOAD_VECTOR_F, "> 0", IRNode.REPLICATE_I, "= 0", // No gather operation @@ -191,7 +191,7 @@ public class TestParticleLife { @Test - @Warmup(10) + @Warmup(2) @IR(counts = {IRNode.REPLICATE_F, "> 0", IRNode.LOAD_VECTOR_F, "> 0", IRNode.REPLICATE_I, "> 0", From 5ab9ddf5e2fa31caa0f62736aff96dd8c12e0177 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 4 Mar 2026 09:32:05 +0000 Subject: [PATCH 149/636] 8378138: G1: Assertion failure from G1CollectedHeap::block_start processing during error reporting Co-authored-by: Yasumasa Suenaga Reviewed-by: ysuenaga, iwalulya --- src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp index 4f242b7a537..f92e37fee3c 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp @@ -42,6 +42,11 @@ #include "utilities/globalDefinitions.hpp" inline HeapWord* G1HeapRegion::block_start(const void* addr) const { + if (is_young()) { + // We are here because of BlockLocationPrinter. + // Can be invoked in any context, so this region might not be parsable. + return nullptr; + } return block_start(addr, parsable_bottom_acquire()); } @@ -64,6 +69,7 @@ inline HeapWord* G1HeapRegion::advance_to_block_containing_addr(const void* addr inline HeapWord* G1HeapRegion::block_start(const void* addr, HeapWord* const pb) const { assert(addr >= bottom() && addr < top(), "invalid address"); + assert(!is_young(), "Only non-young regions have BOT"); HeapWord* first_block = _bot->block_start_reaching_into_card(addr); return advance_to_block_containing_addr(addr, pb, first_block); } From f26b379b97301aca49dda9cb5dfe9a5472af7140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Wed, 4 Mar 2026 09:49:24 +0000 Subject: [PATCH 150/636] 8378927: H3MultipleConnectionsToSameHost.java#with-continuations intermittent fails Reviewed-by: syan, dfuchs --- .../http3/H3MultipleConnectionsToSameHost.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java index 67ac821b6f7..f9ba35359bc 100644 --- a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java +++ b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java @@ -30,6 +30,9 @@ * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError + * -Djdk.httpclient.quic.idleTimeout=100000 + * -Djdk.httpclient.keepalive.timeout.h3=100000 + * -Djdk.test.server.quic.idleTimeout=100000 * -Djdk.httpclient.quic.minPtoBackoffTime=60 * -Djdk.httpclient.quic.maxPtoBackoffTime=90 * -Djdk.httpclient.quic.maxPtoBackoff=10 @@ -53,6 +56,9 @@ * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError + * -Djdk.httpclient.quic.idleTimeout=100000 + * -Djdk.httpclient.keepalive.timeout.h3=100000 + * -Djdk.test.server.quic.idleTimeout=100000 * -Djdk.httpclient.quic.minPtoBackoffTime=45 * -Djdk.httpclient.quic.maxPtoBackoffTime=60 * -Djdk.httpclient.quic.maxPtoBackoff=9 @@ -76,9 +82,9 @@ * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError - * -Djdk.httpclient.quic.idleTimeout=120 - * -Djdk.httpclient.keepalive.timeout.h3=120 - * -Djdk.test.server.quic.idleTimeout=90 + * -Djdk.httpclient.quic.idleTimeout=100000 + * -Djdk.httpclient.keepalive.timeout.h3=100000 + * -Djdk.test.server.quic.idleTimeout=100000 * -Djdk.httpclient.quic.minPtoBackoffTime=60 * -Djdk.httpclient.quic.maxPtoBackoffTime=120 * -Djdk.httpclient.quic.maxPtoBackoff=9 @@ -101,9 +107,9 @@ * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer * @run junit/othervm/timeout=360 -XX:+CrashOnOutOfMemoryError - * -Djdk.httpclient.quic.idleTimeout=120 - * -Djdk.httpclient.keepalive.timeout.h3=120 - * -Djdk.test.server.quic.idleTimeout=90 + * -Djdk.httpclient.quic.idleTimeout=100000 + * -Djdk.httpclient.keepalive.timeout.h3=100000 + * -Djdk.test.server.quic.idleTimeout=100000 * -Djdk.httpclient.quic.minPtoBackoffTime=60 * -Djdk.httpclient.quic.maxPtoBackoffTime=120 * -Djdk.httpclient.quic.maxPtoBackoff=9 From d78a13a7cce07b28668ed88f209f94693f0e607f Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Wed, 4 Mar 2026 10:39:42 +0000 Subject: [PATCH 151/636] 8366138: Parse::jump_switch_ranges() could cause stack overflow when compiling huge switch statement Reviewed-by: rasbold, dfenacci, mchevalier --- src/hotspot/share/opto/parse2.cpp | 53 +++++++--- .../compiler/c2/TestSwitchStackOverflow.java | 99 +++++++++++++++++++ 2 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestSwitchStackOverflow.java diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index eac2b3e863a..7f41870ccea 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -966,12 +966,28 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, _max_switch_depth = 0; _est_switch_depth = log2i_graceful((hi - lo + 1) - 1) + 1; } + SwitchRange* orig_lo = lo; + SwitchRange* orig_hi = hi; #endif - assert(lo <= hi, "must be a non-empty set of ranges"); - if (lo == hi) { - jump_if_always_fork(lo->dest(), trim_ranges && lo->cnt() == 0); - } else { + // The lower-range processing is done iteratively to avoid O(N) stack depth + // when the profiling-based pivot repeatedly selects mid==lo (JDK-8366138). + // The upper-range processing remains recursive but is only reached for + // balanced splits, bounding its depth to O(log N). + // Termination: every iteration either exits or strictly decreases hi-lo: + // lo == mid && mid < hi, increments lo + // lo < mid <= hi, sets hi = mid - 1. + for (int depth = switch_depth;; depth++) { +#ifndef PRODUCT + _max_switch_depth = MAX2(depth, _max_switch_depth); +#endif + + assert(lo <= hi, "must be a non-empty set of ranges"); + if (lo == hi) { + jump_if_always_fork(lo->dest(), trim_ranges && lo->cnt() == 0); + break; + } + assert(lo->hi() == (lo+1)->lo()-1, "contiguous ranges"); assert(hi->lo() == (hi-1)->hi()+1, "contiguous ranges"); @@ -981,7 +997,12 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, float total_cnt = sum_of_cnts(lo, hi); int nr = hi - lo + 1; - if (UseSwitchProfiling) { + // With total_cnt==0 the profiling pivot degenerates to mid==lo + // (0 >= 0/2), producing a linear chain of If nodes instead of a + // balanced tree. A balanced tree is strictly better here: all paths + // are cold, so a balanced split gives fewer comparisons at runtime + // and avoids pathological memory usage in the optimizer. + if (UseSwitchProfiling && total_cnt > 0) { // Don't keep the binary search tree balanced: pick up mid point // that split frequencies in half. float cnt = 0; @@ -1002,7 +1023,7 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, assert(nr != 2 || mid == hi, "should pick higher of 2"); assert(nr != 3 || mid == hi-1, "should pick middle of 3"); } - + assert(mid != nullptr, "mid must be set"); Node *test_val = _gvn.intcon(mid == lo ? mid->hi() : mid->lo()); @@ -1025,7 +1046,7 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, Node *iffalse = _gvn.transform( new IfFalseNode(iff_lt) ); { PreserveJVMState pjvms(this); set_control(iffalse); - jump_switch_ranges(key_val, mid+1, hi, switch_depth+1); + jump_switch_ranges(key_val, mid+1, hi, depth+1); } set_control(iftrue); } @@ -1043,21 +1064,22 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, Node *iffalse = _gvn.transform( new IfFalseNode(iff_ge) ); { PreserveJVMState pjvms(this); set_control(iftrue); - jump_switch_ranges(key_val, mid == lo ? mid+1 : mid, hi, switch_depth+1); + jump_switch_ranges(key_val, mid == lo ? mid+1 : mid, hi, depth+1); } set_control(iffalse); } } - // in any case, process the lower range + // Process the lower range: iterate instead of recursing. if (mid == lo) { if (mid->is_singleton()) { - jump_switch_ranges(key_val, lo+1, hi, switch_depth+1); + lo++; } else { jump_if_always_fork(lo->dest(), trim_ranges && lo->cnt() == 0); + break; } } else { - jump_switch_ranges(key_val, lo, mid-1, switch_depth+1); + hi = mid - 1; } } @@ -1072,23 +1094,22 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, } #ifndef PRODUCT - _max_switch_depth = MAX2(switch_depth, _max_switch_depth); if (TraceOptoParse && Verbose && WizardMode && switch_depth == 0) { SwitchRange* r; int nsing = 0; - for( r = lo; r <= hi; r++ ) { + for (r = orig_lo; r <= orig_hi; r++) { if( r->is_singleton() ) nsing++; } tty->print(">>> "); _method->print_short_name(); tty->print_cr(" switch decision tree"); tty->print_cr(" %d ranges (%d singletons), max_depth=%d, est_depth=%d", - (int) (hi-lo+1), nsing, _max_switch_depth, _est_switch_depth); + (int) (orig_hi-orig_lo+1), nsing, _max_switch_depth, _est_switch_depth); if (_max_switch_depth > _est_switch_depth) { tty->print_cr("******** BAD SWITCH DEPTH ********"); } tty->print(" "); - for( r = lo; r <= hi; r++ ) { + for (r = orig_lo; r <= orig_hi; r++) { r->print(); } tty->cr(); diff --git a/test/hotspot/jtreg/compiler/c2/TestSwitchStackOverflow.java b/test/hotspot/jtreg/compiler/c2/TestSwitchStackOverflow.java new file mode 100644 index 00000000000..adbfa537aed --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestSwitchStackOverflow.java @@ -0,0 +1,99 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * 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 8366138 + * @summary C2: jump_switch_ranges could cause stack overflow when compiling + * huge switch statement with zero-count profiling data. + * @library /test/lib / + * @run main/othervm ${test.main.class} + * @run main/othervm -Xbatch -XX:CompileOnly=Test::test -XX:-DontCompileHugeMethods -XX:CompilerThreadStackSize=512 ${test.main.class} + * @run main/othervm -Xbatch -XX:CompileOnly=Test::test -XX:+DontCompileHugeMethods -XX:CompilerThreadStackSize=512 ${test.main.class} + */ + +package compiler.c2; + +import java.util.stream.Stream; + +import compiler.lib.compile_framework.CompileFramework; +import compiler.lib.template_framework.Template; +import static compiler.lib.template_framework.Template.scope; + +public class TestSwitchStackOverflow { + // Template method exceeds HugeMethodLimit, hence -XX:-DontCompileHugeMethods. + // 5000 cases + CompilerThreadStackSize=512 reliably overflows without the fix. + static final int NUM_CASES = 5000; + + // Generate a class with a large tableswitch method and a main that + // triggers recompilation with zero-count profiling data: + // Phase 1: warm up with default-only path -> C2 compiles + // Phase 2: hit a case -> triggers unstable_if deopt + // Phase 3: recompile with all counts zero (except the default case) + static String generate() { + var caseTemplate = Template.make("i", (Integer i) -> scope( + """ + case #i: + return #i; + """ + )); + var test = Template.make(() -> scope( + """ + public class Test { + static int test(int x) { + switch (x) { + """, + Stream.iterate(0, i -> i + 1) + .limit(NUM_CASES) + .map(i -> caseTemplate.asToken(i)) + .toList(), + """ + default: return -1; + } + } + + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(-1); + } + test(42); + for (int i = 0; i < 10_000; i++) { + test(-1); + } + System.out.println("Done"); + } + } + """ + )); + + return test.render(); + } + + public static void main(String[] args) { + CompileFramework comp = new CompileFramework(); + comp.addJavaSourceCode("Test", generate()); + comp.compile(); + comp.invoke("Test", "main", new Object[] { new String[0] }); + } +} From 329e14b0744912293faa7769b22fa348cb1d10aa Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Wed, 4 Mar 2026 10:56:49 +0000 Subject: [PATCH 152/636] 8375688: C2: Missed Ideal optimization opportunity with VectorMaskToLong and -XX:+StressIncrementalInlining Reviewed-by: qamai, dfenacci --- src/hotspot/share/opto/phaseX.cpp | 10 + .../vectorapi/TestVectorMaskToLongStress.java | 180 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestVectorMaskToLongStress.java diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 3cbbc114778..bc0bed31370 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2725,6 +2725,16 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ worklist.push(cmp); } } + // VectorMaskToLongNode::Ideal_MaskAll looks through VectorStoreMask + // to fold constant masks. + if (use_op == Op_VectorStoreMask) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->Opcode() == Op_VectorMaskToLong) { + worklist.push(u); + } + } + } // From CastX2PNode::Ideal // CastX2P(AddX(x, y)) diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorMaskToLongStress.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMaskToLongStress.java new file mode 100644 index 00000000000..f9807754539 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorMaskToLongStress.java @@ -0,0 +1,180 @@ +/* + * 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 8375688 + * @key randomness + * @library /test/lib / + * @summary VectorMaskToLong constant folding through VectorStoreMask must work under StressIncrementalInlining + * @modules jdk.incubator.vector + * + * @run driver ${test.main.class} + */ + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +/** + * Tests that VectorMaskToLongNode::Ideal_MaskAll folds constant masks even + * when StressIncrementalInlining randomizes the IGVN worklist order. + * + * Each test method does {@code VectorMask.fromLong(SPECIES, constant).toLong()} + * and asserts that VectorMaskToLong is folded away entirely (count = 0). + * + * Ideal_MaskAll looks through VectorStoreMask to inspect its input. Without the worklist + * propagation fix in PhaseIterGVN::add_users_of_use_to_worklist (JDK-8375688), VectorMaskToLong + * is not re-visited after VectorStoreMask's input becomes a recognized constant, + * leaving the fold opportunity missed. + * + * IR rules cover three hardware paths: + * - AVX-512/SVE/RVV: masks fold through MaskAll (correctness check only, VectorStoreMask + * is not involved on these platforms) + * - AVX2 without AVX-512: masks go through VectorStoreMask, directly exercising the worklist fix + * - ASIMD without SVE: same VectorStoreMask path, on AArch64 + * + * {@code @Check} methods verify correctness on all platforms, including those where no IR rule applies. + * Float/Double species are excluded because VectorMaskToLong fails to fold their masks + * due to an intervening VectorMaskCast (JDK-8377588). + */ +public class TestVectorMaskToLongStress { + static final VectorSpecies B_SPECIES = ByteVector.SPECIES_MAX; + static final VectorSpecies S_SPECIES = ShortVector.SPECIES_MAX; + static final VectorSpecies I_SPECIES = IntVector.SPECIES_MAX; + static final VectorSpecies L_SPECIES = LongVector.SPECIES_MAX; + + // --- All-ones mask: fromLong(-1).toLong() should fold to a constant --- + + @Test + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "avx512", "true", "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static long testAllOnesByte() { + return VectorMask.fromLong(B_SPECIES, -1L).toLong(); + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "avx512", "true", "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static long testAllOnesShort() { + return VectorMask.fromLong(S_SPECIES, -1L).toLong(); + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "avx512", "true", "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static long testAllOnesInt() { + return VectorMask.fromLong(I_SPECIES, -1L).toLong(); + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "avx512", "true", "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static long testAllOnesLong() { + return VectorMask.fromLong(L_SPECIES, -1L).toLong(); + } + + // --- All-zeros mask: fromLong(0).toLong() should fold to a constant --- + + @Test + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "avx512", "true", "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static long testAllZerosByte() { + return VectorMask.fromLong(B_SPECIES, 0L).toLong(); + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "avx512", "true", "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + @IR(counts = { IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static long testAllZerosInt() { + return VectorMask.fromLong(I_SPECIES, 0L).toLong(); + } + + // --- Verification --- + + @Check(test = "testAllOnesByte") + public static void checkAllOnesByte(long result) { + Asserts.assertEquals(-1L >>> (64 - B_SPECIES.length()), result); + } + + @Check(test = "testAllOnesShort") + public static void checkAllOnesShort(long result) { + Asserts.assertEquals(-1L >>> (64 - S_SPECIES.length()), result); + } + + @Check(test = "testAllOnesInt") + public static void checkAllOnesInt(long result) { + Asserts.assertEquals(-1L >>> (64 - I_SPECIES.length()), result); + } + + @Check(test = "testAllOnesLong") + public static void checkAllOnesLong(long result) { + Asserts.assertEquals(-1L >>> (64 - L_SPECIES.length()), result); + } + + @Check(test = "testAllZerosByte") + public static void checkAllZerosByte(long result) { + Asserts.assertEquals(0L, result); + } + + @Check(test = "testAllZerosInt") + public static void checkAllZerosInt(long result) { + Asserts.assertEquals(0L, result); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.addFlags("--add-modules=jdk.incubator.vector", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+StressIncrementalInlining", + "-XX:CompileCommand=compileonly,compiler.vectorapi.TestVectorMaskToLongStress::*", + "-XX:VerifyIterativeGVN=1110") + .start(); + } +} From a3b468cec3997c62a2f55302b0338aa0a2bb3055 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 4 Mar 2026 11:12:40 +0000 Subject: [PATCH 153/636] 8379119: G1: Move NoteStartOfMarkHRClosure out of global namespace Reviewed-by: aboldtch, ayang --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 54 +++++++++----------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index ec5649f4fe2..074231a02d4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -898,9 +898,26 @@ public: }; class G1PreConcurrentStartTask::NoteStartOfMarkTask : public G1AbstractSubTask { + + class NoteStartOfMarkHRClosure : public G1HeapRegionClosure { + G1ConcurrentMark* _cm; + + public: + NoteStartOfMarkHRClosure() : G1HeapRegionClosure(), _cm(G1CollectedHeap::heap()->concurrent_mark()) { } + + bool do_heap_region(G1HeapRegion* r) override { + if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) { + _cm->update_top_at_mark_start(r); + } else { + _cm->reset_top_at_mark_start(r); + } + return false; + } + } _region_cl; + G1HeapRegionClaimer _claimer; public: - NoteStartOfMarkTask() : G1AbstractSubTask(G1GCPhaseTimes::NoteStartOfMark), _claimer(0) { } + NoteStartOfMarkTask() : G1AbstractSubTask(G1GCPhaseTimes::NoteStartOfMark), _region_cl(), _claimer(0) { } double worker_cost() const override { // The work done per region is very small, therefore we choose this magic number to cap the number @@ -909,8 +926,13 @@ public: return _claimer.n_regions() / regions_per_thread; } - void set_max_workers(uint max_workers) override; - void do_work(uint worker_id) override; + void set_max_workers(uint max_workers) override { + _claimer.set_n_workers(max_workers); + } + + void do_work(uint worker_id) override { + G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&_region_cl, &_claimer, worker_id); + } }; void G1PreConcurrentStartTask::ResetMarkingStateTask::do_work(uint worker_id) { @@ -918,31 +940,6 @@ void G1PreConcurrentStartTask::ResetMarkingStateTask::do_work(uint worker_id) { _cm->reset(); } -class NoteStartOfMarkHRClosure : public G1HeapRegionClosure { - G1ConcurrentMark* _cm; - -public: - NoteStartOfMarkHRClosure() : G1HeapRegionClosure(), _cm(G1CollectedHeap::heap()->concurrent_mark()) { } - - bool do_heap_region(G1HeapRegion* r) override { - if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) { - _cm->update_top_at_mark_start(r); - } else { - _cm->reset_top_at_mark_start(r); - } - return false; - } -}; - -void G1PreConcurrentStartTask::NoteStartOfMarkTask::do_work(uint worker_id) { - NoteStartOfMarkHRClosure start_cl; - G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&start_cl, &_claimer, worker_id); -} - -void G1PreConcurrentStartTask::NoteStartOfMarkTask::set_max_workers(uint max_workers) { - _claimer.set_n_workers(max_workers); -} - G1PreConcurrentStartTask::G1PreConcurrentStartTask(GCCause::Cause cause, G1ConcurrentMark* cm) : G1BatchedTask("Pre Concurrent Start", G1CollectedHeap::heap()->phase_times()) { add_serial_task(new ResetMarkingStateTask(cm)); @@ -962,7 +959,6 @@ void G1ConcurrentMark::pre_concurrent_start(GCCause::Cause cause) { _gc_tracer_cm->set_gc_cause(cause); } - void G1ConcurrentMark::post_concurrent_mark_start() { // Start Concurrent Marking weak-reference discovery. ReferenceProcessor* rp = _g1h->ref_processor_cm(); From ff8b0ac048f6a6f75d1ad738d3354890d76d8128 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 4 Mar 2026 11:45:09 +0000 Subject: [PATCH 154/636] 8214934: Wrong type annotation offset on casts on expressions Reviewed-by: jlahoda --- .../com/sun/tools/javac/comp/TransTypes.java | 24 ++- .../IncorrectCastOffsetTest.java | 175 ++++++++++++++++++ 2 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java index 862c02ea5f0..1229939c0bf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java @@ -29,7 +29,6 @@ package com.sun.tools.javac.comp; import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Attribute.TypeCompound; -import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Type.TypeVar; import com.sun.tools.javac.jvm.Target; @@ -49,8 +48,6 @@ import static com.sun.tools.javac.code.TypeTag.VOID; import static com.sun.tools.javac.comp.CompileStates.CompileState; import com.sun.tools.javac.tree.JCTree.JCBreak; -import javax.lang.model.type.TypeKind; - /** This pass translates Generic Java to conventional Java. * *

This is NOT part of any supported API. @@ -109,7 +106,9 @@ public class TransTypes extends TreeTranslator { if (!types.isSameType(tree.type, target)) { if (!resolve.isAccessible(env, target.tsym)) resolve.logAccessErrorInternal(env, tree, target); - tree = make.TypeCast(make.Type(target), tree).setType(target); + tree = explicitCastTP != null && types.isSameType(target, explicitCastTP) ? + tree : + make.TypeCast(make.Type(target), tree).setType(target); } make.pos = oldpos; return tree; @@ -440,16 +439,29 @@ public class TransTypes extends TreeTranslator { /** Visitor argument: proto-type. */ private Type pt; + /** we use this type to indicate that "upstream" there is an explicit cast to this type, + * this way we can avoid generating redundant type casts. Redundant casts are not + * innocuous as they can trump user provided ones and affect the offset + * calculation of type annotations applied to the user provided type cast. + */ + private Type explicitCastTP; /** Visitor method: perform a type translation on tree. */ public T translate(T tree, Type pt) { + return translate(tree, pt, pt == explicitCastTP ? explicitCastTP : null); + } + + public T translate(T tree, Type pt, Type castTP) { Type prevPt = this.pt; + Type prevCastPT = this.explicitCastTP; try { this.pt = pt; + this.explicitCastTP = castTP; return translate(tree); } finally { this.pt = prevPt; + this.explicitCastTP = prevCastPT; } } @@ -1037,7 +1049,9 @@ public class TransTypes extends TreeTranslator { tree.clazz = translate(tree.clazz, null); Type originalTarget = tree.type; tree.type = erasure(tree.type); - JCExpression newExpression = translate(tree.expr, tree.type); + JCExpression newExpression = tree.clazz.hasTag(Tag.ANNOTATED_TYPE) ? + translate(tree.expr, tree.type, tree.type) : + translate(tree.expr, tree.type); if (newExpression != tree.expr) { JCTypeCast typeCast = newExpression.hasTag(Tag.TYPECAST) ? (JCTypeCast) newExpression diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java new file mode 100644 index 00000000000..f679926bcf4 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java @@ -0,0 +1,175 @@ +/* + * 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 8214934 + * @summary Wrong type annotation offset on casts on expressions + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.jdeps/com.sun.tools.javap + * @build toolbox.ToolBox toolbox.JavapTask + * @run compile IncorrectCastOffsetTest.java + * @run main IncorrectCastOffsetTest + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import java.util.List; + +import toolbox.JavapTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class IncorrectCastOffsetTest { + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + @interface TypeUse {} + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + @interface TypeUse2 {} + + class AnnotatedCast1 { + private static String checkcast(boolean test, Object obj, Object obj2) { + return (@TypeUse String)(test ? obj : obj2); + } + } + + class AnnotatedCast2 { + private static String checkcast(Object obj) { + return (@TypeUse String)(obj); + } + } + + class AnnotatedCast3 { + private static String checkcast(boolean test, Object obj, Object obj2) { + return (@TypeUse @TypeUse2 String)(test ? obj : obj2); + } + } + + class AnnotatedCast4 { + private static String checkcast(Object obj) { + return (@TypeUse String)(@TypeUse2 CharSequence)(obj); + } + } + + ToolBox tb; + + IncorrectCastOffsetTest() { + tb = new ToolBox(); + } + + public static void main(String args[]) { + IncorrectCastOffsetTest incorrectCastOffsetTest = new IncorrectCastOffsetTest(); + incorrectCastOffsetTest.run(); + } + + void run() { + test("IncorrectCastOffsetTest$AnnotatedCast1.class", + /* + * generated code: + * 0: iload_0 + * 1: ifeq 8 + * 4: aload_1 + * 5: goto 9 + * 8: aload_2 + * 9: checkcast #13 // class java/lang/String + * 12: areturn + */ + List.of( + "RuntimeVisibleTypeAnnotations:", + "0: #35(): CAST, offset=9, type_index=0" + ) + ); + test("IncorrectCastOffsetTest$AnnotatedCast2.class", + /* + * generated code: + * 0: aload_0 + * 1: checkcast #13 // class java/lang/String + * 4: areturn + */ + List.of( + "RuntimeVisibleTypeAnnotations:", + "0: #31(): CAST, offset=1, type_index=0" + ) + ); + test("IncorrectCastOffsetTest$AnnotatedCast3.class", + /* + * generated code: + * 0: iload_0 + * 1: ifeq 8 + * 4: aload_1 + * 5: goto 9 + * 8: aload_2 + * 9: checkcast #13 // class java/lang/String + * 12: areturn + */ + List.of( + "RuntimeVisibleTypeAnnotations:", + "0: #35(): CAST, offset=9, type_index=0", + "IncorrectCastOffsetTest$TypeUse", + "1: #36(): CAST, offset=9, type_index=0", + "IncorrectCastOffsetTest$TypeUse2" + ) + ); + test("IncorrectCastOffsetTest$AnnotatedCast4.class", + /* + * generated code: + * 0: aload_0 + * 1: checkcast #13 // class java/lang/CharSequence + * 4: checkcast #15 // class java/lang/String + * 7: areturn + */ + List.of( + "RuntimeVisibleTypeAnnotations:", + "0: #33(): CAST, offset=4, type_index=0", + "IncorrectCastOffsetTest$TypeUse", + "#34(): CAST, offset=1, type_index=0", + "IncorrectCastOffsetTest$TypeUse2" + ) + ); + } + + void test(String clazz, List expectedOutput) { + Path pathToClass = Paths.get(ToolBox.testClasses, clazz); + String javapOut = new JavapTask(tb) + .options("-v", "-p") + .classes(pathToClass.toString()) + .run() + .getOutput(Task.OutputKind.DIRECT); + + for (String expected : expectedOutput) { + if (!javapOut.contains(expected)) + throw new AssertionError("unexpected output"); + } + } + +} From cb059a6b1b1686b7adcb2e88536060f4f7d47118 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 4 Mar 2026 12:23:31 +0000 Subject: [PATCH 155/636] 8378836: Enable linktime-gc by default on Linux ppc64le Reviewed-by: erikj, jwaters, mdoerr --- make/autoconf/jdk-options.m4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 87d147d4f07..dafac618c59 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -103,8 +103,12 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS], AC_SUBST(ENABLE_HEADLESS_ONLY) # should we linktime gc unused code sections in the JDK build ? - if test "x$OPENJDK_TARGET_OS" = "xlinux" && test "x$OPENJDK_TARGET_CPU" = xs390x; then - LINKTIME_GC_DEFAULT=true + if test "x$OPENJDK_TARGET_OS" = "xlinux"; then + if test "x$OPENJDK_TARGET_CPU" = "xs390x" || test "x$OPENJDK_TARGET_CPU" = "xppc64le"; then + LINKTIME_GC_DEFAULT=true + else + LINKTIME_GC_DEFAULT=false + fi else LINKTIME_GC_DEFAULT=false fi From d8d543a5dccf126b23d79750c67424e454f97a7f Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 4 Mar 2026 13:15:27 +0000 Subject: [PATCH 156/636] 8379121: G1: Remove redundant const_cast in g1BlockOffsetTable Reviewed-by: tschatzl, aboldtch --- src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp | 4 ++-- src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp index fd70796251d..7f0e5e86cd9 100644 --- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp +++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp @@ -73,8 +73,8 @@ void G1BlockOffsetTable::set_offset_array(Atomic* left, Atomic #ifdef ASSERT void G1BlockOffsetTable::check_address(Atomic* addr, const char* msg) const { - Atomic* start_addr = const_cast*>(_offset_base + (uintptr_t(_reserved.start()) >> CardTable::card_shift())); - Atomic* end_addr = const_cast*>(_offset_base + (uintptr_t(_reserved.end()) >> CardTable::card_shift())); + Atomic* start_addr = _offset_base + (uintptr_t(_reserved.start()) >> CardTable::card_shift()); + Atomic* end_addr = _offset_base + (uintptr_t(_reserved.end()) >> CardTable::card_shift()); assert(addr >= start_addr && addr <= end_addr, "%s - offset address: " PTR_FORMAT ", start address: " PTR_FORMAT ", end address: " PTR_FORMAT, msg, (p2i(addr)), (p2i(start_addr)), (p2i(end_addr))); diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp index b707e310781..1236d24bb03 100644 --- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp +++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp @@ -54,7 +54,7 @@ uint8_t G1BlockOffsetTable::offset_array(Atomic* addr) const { inline Atomic* G1BlockOffsetTable::entry_for_addr(const void* const p) const { assert(_reserved.contains(p), "out of bounds access to block offset table"); - Atomic* result = const_cast*>(&_offset_base[uintptr_t(p) >> CardTable::card_shift()]); + Atomic* result = &_offset_base[uintptr_t(p) >> CardTable::card_shift()]; return result; } From 12af936ae47b8c3be9ff639ef550cb63a94f746e Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Wed, 4 Mar 2026 13:21:27 +0000 Subject: [PATCH 157/636] 8377986: C2: New method to add specific users to the worklist Reviewed-by: qamai, mchevalier, dfenacci, bmaillard --- src/hotspot/share/opto/phaseX.cpp | 189 +++++++++++------------------- src/hotspot/share/opto/phaseX.hpp | 1 + 2 files changed, 68 insertions(+), 122 deletions(-) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index bc0bed31370..c77316e7d0b 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -360,6 +360,16 @@ NodeHash::~NodeHash() { } #endif +// Add users of 'n' that match 'predicate' to worklist +template +static void add_users_to_worklist_if(Unique_Node_List& worklist, const Node* n, Predicate predicate) { + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* u = n->fast_out(i); + if (predicate(u)) { + worklist.push(u); + } + } +} //============================================================================= //------------------------------PhaseRemoveUseless----------------------------- @@ -2298,12 +2308,7 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { // A Load that directly follows an InitializeNode is // going away. The Stores that follow are candidates // again to be captured by the InitializeNode. - for (DUIterator_Fast jmax, j = in->fast_outs(jmax); j < jmax; j++) { - Node *n = in->fast_out(j); - if (n->is_Store()) { - _worklist.push(n); - } - } + add_users_to_worklist_if(_worklist, in, [](Node* n) { return n->is_Store(); }); } } // if (in != nullptr && in != C->top()) } // for (uint i = 0; i < dead->req(); i++) @@ -2559,41 +2564,29 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // If changed LShift inputs, check RShift/URShift users for // "(X << C) >> C" sign-ext and "(X << C) >>> C" zero-ext optimizations. if (use_op == Op_LShiftI || use_op == Op_LShiftL) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || - u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || + u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + }); } // If changed LShift inputs, check And users for shift and mask (And) operation if (use_op == Op_LShiftI || use_op == Op_LShiftL) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_AndI || u->Opcode() == Op_AndL) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_AndI || u->Opcode() == Op_AndL; + }); } // If changed AddI/SubI inputs, check CmpU for range check optimization. if (use_op == Op_AddI || use_op == Op_SubI) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->is_Cmp() && (u->Opcode() == Op_CmpU)) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_CmpU; + }); } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity if (use_op == Op_AndI || use_op == Op_AndL) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || - u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || + u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + }); } // Check for redundant conversion patterns: // ConvD2L->ConvL2D->ConvD2L @@ -2606,35 +2599,22 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ use_op == Op_ConvI2F || use_op == Op_ConvL2F || use_op == Op_ConvF2I) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if ((use_op == Op_ConvL2D && u->Opcode() == Op_ConvD2L) || - (use_op == Op_ConvI2F && u->Opcode() == Op_ConvF2I) || - (use_op == Op_ConvL2F && u->Opcode() == Op_ConvF2L) || - (use_op == Op_ConvF2I && u->Opcode() == Op_ConvI2F)) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [=](Node* u) { + return (use_op == Op_ConvL2D && u->Opcode() == Op_ConvD2L) || + (use_op == Op_ConvI2F && u->Opcode() == Op_ConvF2I) || + (use_op == Op_ConvL2F && u->Opcode() == Op_ConvF2L) || + (use_op == Op_ConvF2I && u->Opcode() == Op_ConvI2F); + }); } // ConvD2F::Ideal matches ConvD2F(SqrtD(ConvF2D(x))) => SqrtF(x). // Notify ConvD2F users of SqrtD when any input of the SqrtD changes. if (use_op == Op_SqrtD) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_ConvD2F) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_ConvD2F; }); } // ConvF2HF::Ideal matches ConvF2HF(binopF(ConvHF2F(...))) => FP16BinOp(...). // Notify ConvF2HF users of float binary ops when any input changes. if (Float16NodeFactory::is_float32_binary_oper(use_op)) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_ConvF2HF) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_ConvF2HF; }); } // If changed AddP inputs: // - check Stores for loop invariant, and @@ -2642,33 +2622,21 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // address expression flattening. if (use_op == Op_AddP) { bool offset_changed = n == use->in(AddPNode::Offset); - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->is_Mem()) { - worklist.push(u); - } else if (offset_changed && u->is_AddP() && u->in(AddPNode::Offset)->is_Con()) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [=](Node* u) { + return u->is_Mem() || + (offset_changed && u->is_AddP() && u->in(AddPNode::Offset)->is_Con()); + }); } // Check for "abs(0-x)" into "abs(x)" conversion if (use->is_Sub()) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_AbsD || u->Opcode() == Op_AbsF || - u->Opcode() == Op_AbsL || u->Opcode() == Op_AbsI) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_AbsD || u->Opcode() == Op_AbsF || + u->Opcode() == Op_AbsL || u->Opcode() == Op_AbsI; + }); } // Check for Max/Min(A, Max/Min(B, C)) where A == B or A == C if (use->is_MinMax()) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->is_MinMax()) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { return u->is_MinMax(); }); } auto enqueue_init_mem_projs = [&](ProjNode* proj) { add_users_to_worklist0(proj, worklist); @@ -2707,12 +2675,9 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ if (u->Opcode() == Op_LoadP && ut->isa_instptr()) { if (has_load_barrier_nodes) { // Search for load barriers behind the load - for (DUIterator_Fast i3max, i3 = u->fast_outs(i3max); i3 < i3max; i3++) { - Node* b = u->fast_out(i3); - if (bs->is_gc_barrier_node(b)) { - worklist.push(b); - } - } + add_users_to_worklist_if(worklist, u, [&](Node* b) { + return bs->is_gc_barrier_node(b); + }); } worklist.push(u); } @@ -2728,24 +2693,14 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // VectorMaskToLongNode::Ideal_MaskAll looks through VectorStoreMask // to fold constant masks. if (use_op == Op_VectorStoreMask) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_VectorMaskToLong) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_VectorMaskToLong; }); } // From CastX2PNode::Ideal // CastX2P(AddX(x, y)) // CastX2P(SubX(x, y)) if (use->Opcode() == Op_AddX || use->Opcode() == Op_SubX) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_CastX2P) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_CastX2P; }); } /* AndNode has a special handling when one of the operands is a LShiftNode: @@ -2780,12 +2735,7 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // e.g., (x - y) + y -> x; x + (y - x) -> y. if (use_op == Op_SubI || use_op == Op_SubL) { const int add_op = (use_op == Op_SubI) ? Op_AddI : Op_AddL; - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == add_op) { - worklist.push(u); - } - } + add_users_to_worklist_if(worklist, use, [=](Node* u) { return u->Opcode() == add_op; }); } } @@ -2970,6 +2920,10 @@ void PhaseCCP::dump_type_and_node(const Node* n, const Type* t) { } #endif +bool PhaseCCP::not_bottom_type(Node* n) const { + return n->bottom_type() != type(n); +} + // We need to propagate the type change of 'n' to all its uses. Depending on the kind of node, additional nodes // (grandchildren or even further down) need to be revisited as their types could also be improved as a result // of the new type of 'n'. Push these nodes to the worklist. @@ -2982,7 +2936,7 @@ void PhaseCCP::push_child_nodes_to_worklist(Unique_Node_List& worklist, Node* n) } void PhaseCCP::push_if_not_bottom_type(Unique_Node_List& worklist, Node* n) const { - if (n->bottom_type() != type(n)) { + if (not_bottom_type(n)) { worklist.push(n); } } @@ -3005,9 +2959,9 @@ void PhaseCCP::push_more_uses(Unique_Node_List& worklist, Node* parent, const No // We must recheck Phis too if use is a Region. void PhaseCCP::push_phis(Unique_Node_List& worklist, const Node* use) const { if (use->is_Region()) { - for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) { - push_if_not_bottom_type(worklist, use->fast_out(i)); - } + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return not_bottom_type(u); + }); } } @@ -3034,14 +2988,11 @@ void PhaseCCP::push_catch(Unique_Node_List& worklist, const Node* use) { void PhaseCCP::push_cmpu(Unique_Node_List& worklist, const Node* use) const { uint use_op = use->Opcode(); if (use_op == Op_AddI || use_op == Op_SubI) { - for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) { - Node* cmpu = use->fast_out(i); - const uint cmpu_opcode = cmpu->Opcode(); - if (cmpu_opcode == Op_CmpU || cmpu_opcode == Op_CmpU3) { - // Got a CmpU or CmpU3 which might need the new type information from node n. - push_if_not_bottom_type(worklist, cmpu); - } - } + // Got a CmpU or CmpU3 which might need the new type information from node n. + add_users_to_worklist_if(worklist, use, [&](Node* u) { + uint op = u->Opcode(); + return (op == Op_CmpU || op == Op_CmpU3) && not_bottom_type(u); + }); } } @@ -3130,12 +3081,9 @@ void PhaseCCP::push_loadp(Unique_Node_List& worklist, const Node* use) const { } void PhaseCCP::push_load_barrier(Unique_Node_List& worklist, const BarrierSetC2* barrier_set, const Node* use) { - for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) { - Node* barrier_node = use->fast_out(i); - if (barrier_set->is_gc_barrier_node(barrier_node)) { - worklist.push(barrier_node); - } - } + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return barrier_set->is_gc_barrier_node(u); + }); } // AndI/L::Value() optimizes patterns similar to (v << 2) & 3, or CON & 3 to zero if they are bitwise disjoint. @@ -3171,12 +3119,9 @@ void PhaseCCP::push_and(Unique_Node_List& worklist, const Node* parent, const No void PhaseCCP::push_cast_ii(Unique_Node_List& worklist, const Node* parent, const Node* use) const { if (use->Opcode() == Op_CmpI && use->in(2) == parent) { Node* other_cmp_input = use->in(1); - for (DUIterator_Fast imax, i = other_cmp_input->fast_outs(imax); i < imax; i++) { - Node* cast_ii = other_cmp_input->fast_out(i); - if (cast_ii->is_CastII()) { - push_if_not_bottom_type(worklist, cast_ii); - } - } + add_users_to_worklist_if(worklist, other_cmp_input, [&](Node* u) { + return u->is_CastII() && not_bottom_type(u); + }); } } diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index cd38f37ccf5..94890c250c4 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -652,6 +652,7 @@ class PhaseCCP : public PhaseIterGVN { Node* fetch_next_node(Unique_Node_List& worklist); static void dump_type_and_node(const Node* n, const Type* t) PRODUCT_RETURN; + bool not_bottom_type(Node* n) const; void push_child_nodes_to_worklist(Unique_Node_List& worklist, Node* n) const; void push_if_not_bottom_type(Unique_Node_List& worklist, Node* n) const; void push_more_uses(Unique_Node_List& worklist, Node* parent, const Node* use) const; From 7c8f66c831e96d7ba6ffa0042130c556290761fc Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 4 Mar 2026 13:22:24 +0000 Subject: [PATCH 158/636] 8379041: Crash in ResolvedFieldEntry::assert_is_valid(): invalid put bytecode 0 Reviewed-by: matsaave, dholmes --- src/hotspot/share/oops/resolvedFieldEntry.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/oops/resolvedFieldEntry.cpp b/src/hotspot/share/oops/resolvedFieldEntry.cpp index 49e9115ca9a..122ecf092d8 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.cpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.cpp @@ -77,10 +77,13 @@ void ResolvedFieldEntry::assert_is_valid() const { "field offset out of range %d >= %d", field_offset(), instanceOopDesc::base_offset_in_bytes()); assert(as_BasicType((TosState)tos_state()) != T_ILLEGAL, "tos_state is ILLEGAL"); assert(_flags < (1 << (max_flag_shift + 1)), "flags are too large %d", _flags); - assert((get_code() == 0 || get_code() == Bytecodes::_getstatic || get_code() == Bytecodes::_getfield), - "invalid get bytecode %d", get_code()); - assert((put_code() == 0 || put_code() == Bytecodes::_putstatic || put_code() == Bytecodes::_putfield), - "invalid put bytecode %d", put_code()); + + // Read each bytecode once. + volatile Bytecodes::Code g = (Bytecodes::Code)get_code(); + assert(g == 0 || g == Bytecodes::_getstatic || g == Bytecodes::_getfield, "invalid get bytecode %d", g); + + volatile Bytecodes::Code p = (Bytecodes::Code)put_code(); + assert(p == 0 || p == Bytecodes::_putstatic || p == Bytecodes::_putfield, "invalid put bytecode %d", p); } #endif From eb50630d9893970ed58034e878dfdc0ecf0961da Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 4 Mar 2026 13:57:22 +0000 Subject: [PATCH 159/636] 8379013: Remove some unused code in generateOopMap.cpp Reviewed-by: matsaave, dholmes --- src/hotspot/share/interpreter/oopMapCache.cpp | 28 +---------- src/hotspot/share/oops/generateOopMap.cpp | 50 ++----------------- src/hotspot/share/oops/generateOopMap.hpp | 41 ++------------- 3 files changed, 8 insertions(+), 111 deletions(-) diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index 29d6825d3e5..3769a473e3d 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -78,15 +78,10 @@ class OopMapForCacheEntry: public GenerateOopMap { int _stack_top; virtual bool report_results() const { return false; } - virtual bool possible_gc_point (BytecodeStream *bcs); - virtual void fill_stackmap_prolog (int nof_gc_points); - virtual void fill_stackmap_epilog (); virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stack_top); - virtual void fill_init_vars (GrowableArray *init_vars); - public: OopMapForCacheEntry(const methodHandle& method, int bci, OopMapCacheEntry *entry); @@ -119,27 +114,6 @@ bool OopMapForCacheEntry::compute_map(Thread* current) { return true; } - -bool OopMapForCacheEntry::possible_gc_point(BytecodeStream *bcs) { - return false; // We are not reporting any result. We call result_for_basicblock directly -} - - -void OopMapForCacheEntry::fill_stackmap_prolog(int nof_gc_points) { - // Do nothing -} - - -void OopMapForCacheEntry::fill_stackmap_epilog() { - // Do nothing -} - - -void OopMapForCacheEntry::fill_init_vars(GrowableArray *init_vars) { - // Do nothing -} - - void OopMapForCacheEntry::fill_stackmap_for_opcodes(BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index 97d8bf3d526..db1e8b4548b 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -391,7 +391,6 @@ void CellTypeState::print(outputStream *os) { // void GenerateOopMap::initialize_bb() { - _gc_points = 0; _bb_count = 0; _bb_hdr_bits.reinitialize(method()->code_size()); } @@ -409,7 +408,7 @@ void GenerateOopMap::bb_mark_fct(GenerateOopMap *c, int bci, int *data) { } -void GenerateOopMap::mark_bbheaders_and_count_gc_points() { +void GenerateOopMap::mark_bbheaders() { initialize_bb(); bool fellThrough = false; // False to get first BB marked. @@ -445,9 +444,6 @@ void GenerateOopMap::mark_bbheaders_and_count_gc_points() { default: break; } - - if (possible_gc_point(&bcs)) - _gc_points++; } } @@ -1051,9 +1047,6 @@ void GenerateOopMap::setup_method_entry_state() { // Initialize CellState type of arguments methodsig_to_effect(method()->signature(), method()->is_static(), vars()); - // If some references must be pre-assigned to null, then set that up - initialize_vars(); - // This is the start state merge_state_into_bb(&_basic_blocks[0]); @@ -1077,27 +1070,6 @@ void GenerateOopMap::update_basic_blocks(int bci, int delta, } } -// -// Initvars handling -// - -void GenerateOopMap::initialize_vars() { - for (int k = 0; k < _init_vars->length(); k++) - _state[_init_vars->at(k)] = CellTypeState::make_slot_ref(k); -} - -void GenerateOopMap::add_to_ref_init_set(int localNo) { - - if (TraceNewOopMapGeneration) - tty->print_cr("Added init vars: %d", localNo); - - // Is it already in the set? - if (_init_vars->contains(localNo) ) - return; - - _init_vars->append(localNo); -} - // // Interpreration code // @@ -1672,7 +1644,6 @@ void GenerateOopMap::ppload(CellTypeState *out, int loc_no) { if (vcts.can_be_uninit()) { // It is a ref-uninit conflict (at least). If there are other // problems, we'll get them in the next round - add_to_ref_init_set(loc_no); vcts = out1; } else { // It wasn't a ref-uninit conflict. So must be a @@ -2065,7 +2036,6 @@ GenerateOopMap::GenerateOopMap(const methodHandle& method) { // We have to initialize all variables here, that can be queried directly _method = method; _max_locals=0; - _init_vars = nullptr; #ifndef PRODUCT // If we are doing a detailed trace, include the regular trace information. @@ -2095,7 +2065,6 @@ bool GenerateOopMap::compute_map(Thread* current) { _max_stack = method()->max_stack(); _has_exceptions = (method()->has_exception_handler()); _nof_refval_conflicts = 0; - _init_vars = new GrowableArray(5); // There are seldom more than 5 init_vars _report_result = false; _report_result_for_send = false; _new_var_map = nullptr; @@ -2119,8 +2088,6 @@ bool GenerateOopMap::compute_map(Thread* current) { // if no code - do nothing // compiler needs info if (method()->code_size() == 0 || _max_locals + method()->max_stack() == 0) { - fill_stackmap_prolog(0); - fill_stackmap_epilog(); return true; } // Step 1: Compute all jump targets and their return value @@ -2129,7 +2096,7 @@ bool GenerateOopMap::compute_map(Thread* current) { // Step 2: Find all basic blocks and count GC points if (!_got_error) - mark_bbheaders_and_count_gc_points(); + mark_bbheaders(); // Step 3: Calculate stack maps if (!_got_error) @@ -2186,9 +2153,6 @@ void GenerateOopMap::report_result() { // We now want to report the result of the parse _report_result = true; - // Prolog code - fill_stackmap_prolog(_gc_points); - // Mark everything changed, then do one interpretation pass. for (int i = 0; i<_bb_count; i++) { if (_basic_blocks[i].is_reachable()) { @@ -2197,14 +2161,6 @@ void GenerateOopMap::report_result() { } } - // Note: Since we are skipping dead-code when we are reporting results, then - // the no. of encountered gc-points might be fewer than the previously number - // we have counted. (dead-code is a pain - it should be removed before we get here) - fill_stackmap_epilog(); - - // Report initvars - fill_init_vars(_init_vars); - _report_result = false; } diff --git a/src/hotspot/share/oops/generateOopMap.hpp b/src/hotspot/share/oops/generateOopMap.hpp index 0da3779d463..7c445887ee7 100644 --- a/src/hotspot/share/oops/generateOopMap.hpp +++ b/src/hotspot/share/oops/generateOopMap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -348,17 +348,15 @@ class GenerateOopMap { // Basicblock info BasicBlock * _basic_blocks; // Array of basicblock info - int _gc_points; int _bb_count; ResourceBitMap _bb_hdr_bits; // Basicblocks methods void initialize_bb (); - void mark_bbheaders_and_count_gc_points(); + void mark_bbheaders(); bool is_bb_header (int bci) const { return _bb_hdr_bits.at(bci); } - int gc_points () const { return _gc_points; } int bb_count () const { return _bb_count; } void set_bbmark_bit (int bci); BasicBlock * get_basic_block_at (int bci) const; @@ -419,12 +417,6 @@ class GenerateOopMap { void report_result (); - // Initvars - GrowableArray * _init_vars; - - void initialize_vars (); - void add_to_ref_init_set (int localNo); - // Conflicts rewrite logic bool _conflict; // True, if a conflict occurred during interpretation int _nof_refval_conflicts; // No. of conflicts that require rewrites @@ -450,7 +442,7 @@ class GenerateOopMap { int binsToHold (int no) { return ((no+(BitsPerWord-1))/BitsPerWord); } char *state_vec_to_string (CellTypeState* vec, int len); - // Helper method. Can be used in subclasses to fx. calculate gc_points. If the current instruction + // Helper method. If the current instruction // is a control transfer, then calls the jmpFct all possible destinations. void ret_jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int varNo,int *data); bool jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int *data); @@ -480,14 +472,7 @@ class GenerateOopMap { bool monitor_safe() { return _monitor_safe; } // Specialization methods. Intended use: - // - possible_gc_point must return true for every bci for which the stackmaps must be returned - // - fill_stackmap_prolog is called just before the result is reported. The arguments tells the estimated - // number of gc points // - fill_stackmap_for_opcodes is called once for each bytecode index in order (0...code_length-1) - // - fill_stackmap_epilog is called after all results has been reported. Note: Since the algorithm does not report - // stackmaps for deadcode, fewer gc_points might have been encountered than assumed during the epilog. It is the - // responsibility of the subclass to count the correct number. - // - fill_init_vars are called once with the result of the init_vars computation // // All these methods are used during a call to: compute_map. Note: Non of the return results are valid // after compute_map returns, since all values are allocated as resource objects. @@ -495,15 +480,10 @@ class GenerateOopMap { // All virtual method must be implemented in subclasses virtual bool allow_rewrites () const { return false; } virtual bool report_results () const { return true; } - virtual bool report_init_vars () const { return true; } - virtual bool possible_gc_point (BytecodeStream *bcs) { ShouldNotReachHere(); return false; } - virtual void fill_stackmap_prolog (int nof_gc_points) { ShouldNotReachHere(); } - virtual void fill_stackmap_epilog () { ShouldNotReachHere(); } virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stackTop) { ShouldNotReachHere(); } - virtual void fill_init_vars (GrowableArray *init_vars) { ShouldNotReachHere();; } }; // @@ -513,19 +493,12 @@ class GenerateOopMap { class ResolveOopMapConflicts: public GenerateOopMap { private: - bool _must_clear_locals; - virtual bool report_results() const { return false; } - virtual bool report_init_vars() const { return true; } virtual bool allow_rewrites() const { return true; } - virtual bool possible_gc_point (BytecodeStream *bcs) { return false; } - virtual void fill_stackmap_prolog (int nof_gc_points) {} - virtual void fill_stackmap_epilog () {} virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stack_top) {} - virtual void fill_init_vars (GrowableArray *init_vars) { _must_clear_locals = init_vars->length() > 0; } #ifndef PRODUCT // Statistics @@ -535,10 +508,9 @@ class ResolveOopMapConflicts: public GenerateOopMap { #endif public: - ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method) { _must_clear_locals = false; }; + ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method) { } methodHandle do_potential_rewrite(TRAPS); - bool must_clear_locals() const { return _must_clear_locals; } }; @@ -549,16 +521,11 @@ class GeneratePairingInfo: public GenerateOopMap { private: virtual bool report_results() const { return false; } - virtual bool report_init_vars() const { return false; } virtual bool allow_rewrites() const { return false; } - virtual bool possible_gc_point (BytecodeStream *bcs) { return false; } - virtual void fill_stackmap_prolog (int nof_gc_points) {} - virtual void fill_stackmap_epilog () {} virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stack_top) {} - virtual void fill_init_vars (GrowableArray *init_vars) {} public: GeneratePairingInfo(const methodHandle& method) : GenerateOopMap(method) {}; From 3531c78dea58f1c33752685bd1a876162ec03825 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 4 Mar 2026 14:30:31 +0000 Subject: [PATCH 160/636] 8379196: delta apply fix for JDK-8214934 Reviewed-by: jlahoda --- .../com/sun/tools/javac/comp/TransTypes.java | 24 +-- .../IncorrectCastOffsetTest.java | 175 ------------------ 2 files changed, 5 insertions(+), 194 deletions(-) delete mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java index 1229939c0bf..862c02ea5f0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java @@ -29,6 +29,7 @@ package com.sun.tools.javac.comp; import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Attribute.TypeCompound; +import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Type.TypeVar; import com.sun.tools.javac.jvm.Target; @@ -48,6 +49,8 @@ import static com.sun.tools.javac.code.TypeTag.VOID; import static com.sun.tools.javac.comp.CompileStates.CompileState; import com.sun.tools.javac.tree.JCTree.JCBreak; +import javax.lang.model.type.TypeKind; + /** This pass translates Generic Java to conventional Java. * *

This is NOT part of any supported API. @@ -106,9 +109,7 @@ public class TransTypes extends TreeTranslator { if (!types.isSameType(tree.type, target)) { if (!resolve.isAccessible(env, target.tsym)) resolve.logAccessErrorInternal(env, tree, target); - tree = explicitCastTP != null && types.isSameType(target, explicitCastTP) ? - tree : - make.TypeCast(make.Type(target), tree).setType(target); + tree = make.TypeCast(make.Type(target), tree).setType(target); } make.pos = oldpos; return tree; @@ -439,29 +440,16 @@ public class TransTypes extends TreeTranslator { /** Visitor argument: proto-type. */ private Type pt; - /** we use this type to indicate that "upstream" there is an explicit cast to this type, - * this way we can avoid generating redundant type casts. Redundant casts are not - * innocuous as they can trump user provided ones and affect the offset - * calculation of type annotations applied to the user provided type cast. - */ - private Type explicitCastTP; /** Visitor method: perform a type translation on tree. */ public T translate(T tree, Type pt) { - return translate(tree, pt, pt == explicitCastTP ? explicitCastTP : null); - } - - public T translate(T tree, Type pt, Type castTP) { Type prevPt = this.pt; - Type prevCastPT = this.explicitCastTP; try { this.pt = pt; - this.explicitCastTP = castTP; return translate(tree); } finally { this.pt = prevPt; - this.explicitCastTP = prevCastPT; } } @@ -1049,9 +1037,7 @@ public class TransTypes extends TreeTranslator { tree.clazz = translate(tree.clazz, null); Type originalTarget = tree.type; tree.type = erasure(tree.type); - JCExpression newExpression = tree.clazz.hasTag(Tag.ANNOTATED_TYPE) ? - translate(tree.expr, tree.type, tree.type) : - translate(tree.expr, tree.type); + JCExpression newExpression = translate(tree.expr, tree.type); if (newExpression != tree.expr) { JCTypeCast typeCast = newExpression.hasTag(Tag.TYPECAST) ? (JCTypeCast) newExpression diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java deleted file mode 100644 index f679926bcf4..00000000000 --- a/test/langtools/tools/javac/annotations/typeAnnotations/IncorrectCastOffsetTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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 8214934 - * @summary Wrong type annotation offset on casts on expressions - * @library /tools/lib - * @modules jdk.compiler/com.sun.tools.javac.api - * jdk.compiler/com.sun.tools.javac.main - * jdk.jdeps/com.sun.tools.javap - * @build toolbox.ToolBox toolbox.JavapTask - * @run compile IncorrectCastOffsetTest.java - * @run main IncorrectCastOffsetTest - */ - -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import java.util.List; - -import toolbox.JavapTask; -import toolbox.Task; -import toolbox.ToolBox; - -public class IncorrectCastOffsetTest { - @Target(ElementType.TYPE_USE) - @Retention(RetentionPolicy.RUNTIME) - @interface TypeUse {} - - @Target(ElementType.TYPE_USE) - @Retention(RetentionPolicy.RUNTIME) - @interface TypeUse2 {} - - class AnnotatedCast1 { - private static String checkcast(boolean test, Object obj, Object obj2) { - return (@TypeUse String)(test ? obj : obj2); - } - } - - class AnnotatedCast2 { - private static String checkcast(Object obj) { - return (@TypeUse String)(obj); - } - } - - class AnnotatedCast3 { - private static String checkcast(boolean test, Object obj, Object obj2) { - return (@TypeUse @TypeUse2 String)(test ? obj : obj2); - } - } - - class AnnotatedCast4 { - private static String checkcast(Object obj) { - return (@TypeUse String)(@TypeUse2 CharSequence)(obj); - } - } - - ToolBox tb; - - IncorrectCastOffsetTest() { - tb = new ToolBox(); - } - - public static void main(String args[]) { - IncorrectCastOffsetTest incorrectCastOffsetTest = new IncorrectCastOffsetTest(); - incorrectCastOffsetTest.run(); - } - - void run() { - test("IncorrectCastOffsetTest$AnnotatedCast1.class", - /* - * generated code: - * 0: iload_0 - * 1: ifeq 8 - * 4: aload_1 - * 5: goto 9 - * 8: aload_2 - * 9: checkcast #13 // class java/lang/String - * 12: areturn - */ - List.of( - "RuntimeVisibleTypeAnnotations:", - "0: #35(): CAST, offset=9, type_index=0" - ) - ); - test("IncorrectCastOffsetTest$AnnotatedCast2.class", - /* - * generated code: - * 0: aload_0 - * 1: checkcast #13 // class java/lang/String - * 4: areturn - */ - List.of( - "RuntimeVisibleTypeAnnotations:", - "0: #31(): CAST, offset=1, type_index=0" - ) - ); - test("IncorrectCastOffsetTest$AnnotatedCast3.class", - /* - * generated code: - * 0: iload_0 - * 1: ifeq 8 - * 4: aload_1 - * 5: goto 9 - * 8: aload_2 - * 9: checkcast #13 // class java/lang/String - * 12: areturn - */ - List.of( - "RuntimeVisibleTypeAnnotations:", - "0: #35(): CAST, offset=9, type_index=0", - "IncorrectCastOffsetTest$TypeUse", - "1: #36(): CAST, offset=9, type_index=0", - "IncorrectCastOffsetTest$TypeUse2" - ) - ); - test("IncorrectCastOffsetTest$AnnotatedCast4.class", - /* - * generated code: - * 0: aload_0 - * 1: checkcast #13 // class java/lang/CharSequence - * 4: checkcast #15 // class java/lang/String - * 7: areturn - */ - List.of( - "RuntimeVisibleTypeAnnotations:", - "0: #33(): CAST, offset=4, type_index=0", - "IncorrectCastOffsetTest$TypeUse", - "#34(): CAST, offset=1, type_index=0", - "IncorrectCastOffsetTest$TypeUse2" - ) - ); - } - - void test(String clazz, List expectedOutput) { - Path pathToClass = Paths.get(ToolBox.testClasses, clazz); - String javapOut = new JavapTask(tb) - .options("-v", "-p") - .classes(pathToClass.toString()) - .run() - .getOutput(Task.OutputKind.DIRECT); - - for (String expected : expectedOutput) { - if (!javapOut.contains(expected)) - throw new AssertionError("unexpected output"); - } - } - -} From 4d2c537189cba77d3bbfab36ca3e1c3ceb7c603f Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Wed, 4 Mar 2026 17:16:32 +0000 Subject: [PATCH 161/636] 8378992: Case folding cache should not look up code point U+0000 Reviewed-by: sherman, rriggs --- .../classes/jdk/internal/lang/CaseFolding.java.template | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template b/src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template index 24a183c8da0..24f48151f21 100644 --- a/src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template +++ b/src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template @@ -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 @@ -188,6 +188,12 @@ public final class CaseFolding { } private static long getDefined(int cp) { + // Exclude code point U+0000, which is guaranteed to have no + // case-folding mapping. + if (cp == 0) { + return -1; + } + var hashes = CASE_FOLDING_HASHES; var length = CASE_FOLDING_CPS.length; // hashed based on total defined. var hash = cp % length; From 8b91537f109b22333c6008ba64cada9711534cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 4 Mar 2026 17:17:31 +0000 Subject: [PATCH 162/636] 8379203: [BACKOUT] Remove some unused code in generateOopMap.cpp Reviewed-by: liach, coleenp --- src/hotspot/share/interpreter/oopMapCache.cpp | 28 ++++++++++- src/hotspot/share/oops/generateOopMap.cpp | 50 +++++++++++++++++-- src/hotspot/share/oops/generateOopMap.hpp | 41 +++++++++++++-- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index 3769a473e3d..29d6825d3e5 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,10 +78,15 @@ class OopMapForCacheEntry: public GenerateOopMap { int _stack_top; virtual bool report_results() const { return false; } + virtual bool possible_gc_point (BytecodeStream *bcs); + virtual void fill_stackmap_prolog (int nof_gc_points); + virtual void fill_stackmap_epilog (); virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stack_top); + virtual void fill_init_vars (GrowableArray *init_vars); + public: OopMapForCacheEntry(const methodHandle& method, int bci, OopMapCacheEntry *entry); @@ -114,6 +119,27 @@ bool OopMapForCacheEntry::compute_map(Thread* current) { return true; } + +bool OopMapForCacheEntry::possible_gc_point(BytecodeStream *bcs) { + return false; // We are not reporting any result. We call result_for_basicblock directly +} + + +void OopMapForCacheEntry::fill_stackmap_prolog(int nof_gc_points) { + // Do nothing +} + + +void OopMapForCacheEntry::fill_stackmap_epilog() { + // Do nothing +} + + +void OopMapForCacheEntry::fill_init_vars(GrowableArray *init_vars) { + // Do nothing +} + + void OopMapForCacheEntry::fill_stackmap_for_opcodes(BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index db1e8b4548b..97d8bf3d526 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -391,6 +391,7 @@ void CellTypeState::print(outputStream *os) { // void GenerateOopMap::initialize_bb() { + _gc_points = 0; _bb_count = 0; _bb_hdr_bits.reinitialize(method()->code_size()); } @@ -408,7 +409,7 @@ void GenerateOopMap::bb_mark_fct(GenerateOopMap *c, int bci, int *data) { } -void GenerateOopMap::mark_bbheaders() { +void GenerateOopMap::mark_bbheaders_and_count_gc_points() { initialize_bb(); bool fellThrough = false; // False to get first BB marked. @@ -444,6 +445,9 @@ void GenerateOopMap::mark_bbheaders() { default: break; } + + if (possible_gc_point(&bcs)) + _gc_points++; } } @@ -1047,6 +1051,9 @@ void GenerateOopMap::setup_method_entry_state() { // Initialize CellState type of arguments methodsig_to_effect(method()->signature(), method()->is_static(), vars()); + // If some references must be pre-assigned to null, then set that up + initialize_vars(); + // This is the start state merge_state_into_bb(&_basic_blocks[0]); @@ -1070,6 +1077,27 @@ void GenerateOopMap::update_basic_blocks(int bci, int delta, } } +// +// Initvars handling +// + +void GenerateOopMap::initialize_vars() { + for (int k = 0; k < _init_vars->length(); k++) + _state[_init_vars->at(k)] = CellTypeState::make_slot_ref(k); +} + +void GenerateOopMap::add_to_ref_init_set(int localNo) { + + if (TraceNewOopMapGeneration) + tty->print_cr("Added init vars: %d", localNo); + + // Is it already in the set? + if (_init_vars->contains(localNo) ) + return; + + _init_vars->append(localNo); +} + // // Interpreration code // @@ -1644,6 +1672,7 @@ void GenerateOopMap::ppload(CellTypeState *out, int loc_no) { if (vcts.can_be_uninit()) { // It is a ref-uninit conflict (at least). If there are other // problems, we'll get them in the next round + add_to_ref_init_set(loc_no); vcts = out1; } else { // It wasn't a ref-uninit conflict. So must be a @@ -2036,6 +2065,7 @@ GenerateOopMap::GenerateOopMap(const methodHandle& method) { // We have to initialize all variables here, that can be queried directly _method = method; _max_locals=0; + _init_vars = nullptr; #ifndef PRODUCT // If we are doing a detailed trace, include the regular trace information. @@ -2065,6 +2095,7 @@ bool GenerateOopMap::compute_map(Thread* current) { _max_stack = method()->max_stack(); _has_exceptions = (method()->has_exception_handler()); _nof_refval_conflicts = 0; + _init_vars = new GrowableArray(5); // There are seldom more than 5 init_vars _report_result = false; _report_result_for_send = false; _new_var_map = nullptr; @@ -2088,6 +2119,8 @@ bool GenerateOopMap::compute_map(Thread* current) { // if no code - do nothing // compiler needs info if (method()->code_size() == 0 || _max_locals + method()->max_stack() == 0) { + fill_stackmap_prolog(0); + fill_stackmap_epilog(); return true; } // Step 1: Compute all jump targets and their return value @@ -2096,7 +2129,7 @@ bool GenerateOopMap::compute_map(Thread* current) { // Step 2: Find all basic blocks and count GC points if (!_got_error) - mark_bbheaders(); + mark_bbheaders_and_count_gc_points(); // Step 3: Calculate stack maps if (!_got_error) @@ -2153,6 +2186,9 @@ void GenerateOopMap::report_result() { // We now want to report the result of the parse _report_result = true; + // Prolog code + fill_stackmap_prolog(_gc_points); + // Mark everything changed, then do one interpretation pass. for (int i = 0; i<_bb_count; i++) { if (_basic_blocks[i].is_reachable()) { @@ -2161,6 +2197,14 @@ void GenerateOopMap::report_result() { } } + // Note: Since we are skipping dead-code when we are reporting results, then + // the no. of encountered gc-points might be fewer than the previously number + // we have counted. (dead-code is a pain - it should be removed before we get here) + fill_stackmap_epilog(); + + // Report initvars + fill_init_vars(_init_vars); + _report_result = false; } diff --git a/src/hotspot/share/oops/generateOopMap.hpp b/src/hotspot/share/oops/generateOopMap.hpp index 7c445887ee7..0da3779d463 100644 --- a/src/hotspot/share/oops/generateOopMap.hpp +++ b/src/hotspot/share/oops/generateOopMap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -348,15 +348,17 @@ class GenerateOopMap { // Basicblock info BasicBlock * _basic_blocks; // Array of basicblock info + int _gc_points; int _bb_count; ResourceBitMap _bb_hdr_bits; // Basicblocks methods void initialize_bb (); - void mark_bbheaders(); + void mark_bbheaders_and_count_gc_points(); bool is_bb_header (int bci) const { return _bb_hdr_bits.at(bci); } + int gc_points () const { return _gc_points; } int bb_count () const { return _bb_count; } void set_bbmark_bit (int bci); BasicBlock * get_basic_block_at (int bci) const; @@ -417,6 +419,12 @@ class GenerateOopMap { void report_result (); + // Initvars + GrowableArray * _init_vars; + + void initialize_vars (); + void add_to_ref_init_set (int localNo); + // Conflicts rewrite logic bool _conflict; // True, if a conflict occurred during interpretation int _nof_refval_conflicts; // No. of conflicts that require rewrites @@ -442,7 +450,7 @@ class GenerateOopMap { int binsToHold (int no) { return ((no+(BitsPerWord-1))/BitsPerWord); } char *state_vec_to_string (CellTypeState* vec, int len); - // Helper method. If the current instruction + // Helper method. Can be used in subclasses to fx. calculate gc_points. If the current instruction // is a control transfer, then calls the jmpFct all possible destinations. void ret_jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int varNo,int *data); bool jump_targets_do (BytecodeStream *bcs, jmpFct_t jmpFct, int *data); @@ -472,7 +480,14 @@ class GenerateOopMap { bool monitor_safe() { return _monitor_safe; } // Specialization methods. Intended use: + // - possible_gc_point must return true for every bci for which the stackmaps must be returned + // - fill_stackmap_prolog is called just before the result is reported. The arguments tells the estimated + // number of gc points // - fill_stackmap_for_opcodes is called once for each bytecode index in order (0...code_length-1) + // - fill_stackmap_epilog is called after all results has been reported. Note: Since the algorithm does not report + // stackmaps for deadcode, fewer gc_points might have been encountered than assumed during the epilog. It is the + // responsibility of the subclass to count the correct number. + // - fill_init_vars are called once with the result of the init_vars computation // // All these methods are used during a call to: compute_map. Note: Non of the return results are valid // after compute_map returns, since all values are allocated as resource objects. @@ -480,10 +495,15 @@ class GenerateOopMap { // All virtual method must be implemented in subclasses virtual bool allow_rewrites () const { return false; } virtual bool report_results () const { return true; } + virtual bool report_init_vars () const { return true; } + virtual bool possible_gc_point (BytecodeStream *bcs) { ShouldNotReachHere(); return false; } + virtual void fill_stackmap_prolog (int nof_gc_points) { ShouldNotReachHere(); } + virtual void fill_stackmap_epilog () { ShouldNotReachHere(); } virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stackTop) { ShouldNotReachHere(); } + virtual void fill_init_vars (GrowableArray *init_vars) { ShouldNotReachHere();; } }; // @@ -493,12 +513,19 @@ class GenerateOopMap { class ResolveOopMapConflicts: public GenerateOopMap { private: + bool _must_clear_locals; + virtual bool report_results() const { return false; } + virtual bool report_init_vars() const { return true; } virtual bool allow_rewrites() const { return true; } + virtual bool possible_gc_point (BytecodeStream *bcs) { return false; } + virtual void fill_stackmap_prolog (int nof_gc_points) {} + virtual void fill_stackmap_epilog () {} virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stack_top) {} + virtual void fill_init_vars (GrowableArray *init_vars) { _must_clear_locals = init_vars->length() > 0; } #ifndef PRODUCT // Statistics @@ -508,9 +535,10 @@ class ResolveOopMapConflicts: public GenerateOopMap { #endif public: - ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method) { } + ResolveOopMapConflicts(const methodHandle& method) : GenerateOopMap(method) { _must_clear_locals = false; }; methodHandle do_potential_rewrite(TRAPS); + bool must_clear_locals() const { return _must_clear_locals; } }; @@ -521,11 +549,16 @@ class GeneratePairingInfo: public GenerateOopMap { private: virtual bool report_results() const { return false; } + virtual bool report_init_vars() const { return false; } virtual bool allow_rewrites() const { return false; } + virtual bool possible_gc_point (BytecodeStream *bcs) { return false; } + virtual void fill_stackmap_prolog (int nof_gc_points) {} + virtual void fill_stackmap_epilog () {} virtual void fill_stackmap_for_opcodes (BytecodeStream *bcs, CellTypeState* vars, CellTypeState* stack, int stack_top) {} + virtual void fill_init_vars (GrowableArray *init_vars) {} public: GeneratePairingInfo(const methodHandle& method) : GenerateOopMap(method) {}; From 0fbf58d8ff4fae370d5a839e59cebf713a1f1e5a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 4 Mar 2026 17:33:32 +0000 Subject: [PATCH 163/636] 8372353: API to compute the byte length of a String encoded in a given Charset Reviewed-by: rriggs, naoto, vyazici --- .../share/classes/java/lang/String.java | 95 +++++++++++++- test/jdk/java/lang/String/Encodings.java | 8 +- test/jdk/java/lang/String/Exceptions.java | 13 +- test/jdk/sun/nio/cs/TestStringCoding.java | 10 +- .../lang/foreign/StringLoopJmhBenchmark.java | 122 ++++++++++++++++++ 5 files changed, 235 insertions(+), 13 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/lang/foreign/StringLoopJmhBenchmark.java diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index fc05febdb45..c6c08ed4473 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -1133,6 +1133,34 @@ public final class String return Arrays.copyOf(dst, dp); } + // This follows the implementation of encodeASCII and encode8859_1 + private static int encodedLengthASCIIor8859_1(byte coder, byte[] val) { + if (coder == LATIN1) { + return val.length; + } + int len = val.length >> 1; + int dp = 0; + int sp = 0; + int sl = len; + while (sp < sl) { + char c = StringUTF16.getChar(val, sp); + if (c >= Character.MIN_HIGH_SURROGATE) { + break; + } + dp++; + sp++; + } + while (sp < sl) { + char c = StringUTF16.getChar(val, sp++); + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(val, sp))) { + sp++; + } + dp++; + } + return dp; + } + //------------------------------ utf8 ------------------------------------ /** @@ -1467,6 +1495,27 @@ public final class String return Arrays.copyOf(dst, dp); } + // This follows the implementation of encodeUTF8 + private static int encodedLengthUTF8(byte coder, byte[] val) { + if (coder == UTF16) { + return encodedLengthUTF8_UTF16(val, null); + } + int positives = StringCoding.countPositives(val, 0, val.length); + if (positives == val.length) { + return positives; + } + int dp = positives; + for (int i = dp; i < val.length; i++) { + byte c = val[i]; + if (c < 0) { + dp += 2; + } else { + dp++; + } + } + return dp; + } + /** * {@return the byte array obtained by first decoding {@code val} with * UTF-16, and then encoding the result with UTF-8} @@ -1484,11 +1533,8 @@ public final class String int sl = val.length >> 1; // UTF-8 encoded can be as much as 3 times the string length // For very large estimate, (as in overflow of 32 bit int), precompute the exact size - long allocLen = (sl * 3 < 0) ? computeSizeUTF8_UTF16(val, exClass) : sl * 3; - if (allocLen > (long)Integer.MAX_VALUE) { - throw new OutOfMemoryError("Required length exceeds implementation limit"); - } - byte[] dst = new byte[(int) allocLen]; + int allocLen = (sl * 3 < 0) ? encodedLengthUTF8_UTF16(val, exClass) : sl * 3; + byte[] dst = new byte[allocLen]; while (sp < sl) { // ascii fast loop; char c = StringUTF16.getChar(val, sp); @@ -1547,11 +1593,20 @@ public final class String * @param The exception type parameter to enable callers to avoid * having to declare the exception */ - private static long computeSizeUTF8_UTF16(byte[] val, Class exClass) throws E { + private static int encodedLengthUTF8_UTF16(byte[] val, Class exClass) throws E { long dp = 0L; int sp = 0; int sl = val.length >> 1; + while (sp < sl) { + // ascii fast loop; + char c = StringUTF16.getChar(val, sp); + if (c >= '\u0080') { + break; + } + dp++; + sp++; + } while (sp < sl) { char c = StringUTF16.getChar(val, sp++); if (c < 0x80) { @@ -1580,7 +1635,10 @@ public final class String dp += 3; } } - return dp; + if (dp > (long)Integer.MAX_VALUE) { + throw new OutOfMemoryError("Required length exceeds implementation limit"); + } + return (int) dp; } /** @@ -2045,6 +2103,29 @@ public final class String return encode(Charset.defaultCharset(), coder(), value); } + /** + * {@return the length in bytes of this {@code String} encoded with the given {@link Charset}} + * + *

The returned length accounts for the replacement of malformed-input and unmappable-character + * sequences with the charset's default replacement byte array. The result will be the same value + * as {@link #getBytes(Charset) getBytes(cs).length}. + * + * @apiNote This method provides equivalent or better performance than {@link #getBytes(Charset) + * getBytes(cs).length}. + * + * @param cs The {@link Charset} used to the compute the length + * @since 27 + */ + public int encodedLength(Charset cs) { + Objects.requireNonNull(cs); + if (cs == UTF_8.INSTANCE) { + return encodedLengthUTF8(coder, value); + } else if (cs == ISO_8859_1.INSTANCE || cs == US_ASCII.INSTANCE) { + return encodedLengthASCIIor8859_1(coder, value); + } + return getBytes(cs).length; + } + boolean bytesCompatible(Charset charset, int srcIndex, int numChars) { if (isLatin1()) { if (charset == ISO_8859_1.INSTANCE) { diff --git a/test/jdk/java/lang/String/Encodings.java b/test/jdk/java/lang/String/Encodings.java index 4714815026e..7974157ede0 100644 --- a/test/jdk/java/lang/String/Encodings.java +++ b/test/jdk/java/lang/String/Encodings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -22,7 +22,7 @@ */ /* @test - * @bug 4085160 4139951 5005831 + * @bug 4085160 4139951 5005831 8372353 * @summary Test that required character encodings are supported */ @@ -106,6 +106,10 @@ public class Encodings { if (!equals(bs, bytes)) throw new Exception(charset + ": String.getBytes failed"); + /* String.encodedLength(Charset charset) */ + if (bs.length != str.encodedLength(charset)) + throw new Exception(charset + ": String.encodedLength failed"); + // Calls to String.getBytes(Charset) shouldn't automatically // use the cached thread-local encoder. if (charset.name().equals("UTF-16BE")) { diff --git a/test/jdk/java/lang/String/Exceptions.java b/test/jdk/java/lang/String/Exceptions.java index 3ba7792f424..15ffe3eac20 100644 --- a/test/jdk/java/lang/String/Exceptions.java +++ b/test/jdk/java/lang/String/Exceptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -23,7 +23,7 @@ /** * @test - * @bug 4472841 4703640 4705681 4705683 4833095 5005831 + * @bug 4472841 4703640 4705681 4705683 4833095 5005831 8372353 * @summary Verify that constructor exceptions are thrown as expected. */ @@ -397,6 +397,14 @@ public class Exceptions { }}); } + private static void encodedLength() { + System.out.println("encodedLength(Charset charset)"); + tryCatch(" null", NullPointerException.class, new Runnable() { + public void run() { + "foo".encodedLength((Charset)null); + }}); + } + private static void contentEquals() { System.out.println("contentEquals(StringBuffer sb)"); tryCatch(" null", NullPointerException.class, new Runnable() { @@ -640,6 +648,7 @@ public class Exceptions { // getBytes(Locale) // getBytes(String) // getBytes(Charset) + encodedLength(); // encodedLength(Charset) contentEquals(); // contentEquals(StringBuffer) compareTo(); // compareTo(String), compareTo(Object) compareToIgnoreCase();// compareToIgnoreCase(String) diff --git a/test/jdk/sun/nio/cs/TestStringCoding.java b/test/jdk/sun/nio/cs/TestStringCoding.java index d708ef180a2..b81ffb07d20 100644 --- a/test/jdk/sun/nio/cs/TestStringCoding.java +++ b/test/jdk/sun/nio/cs/TestStringCoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -22,7 +22,7 @@ */ /* @test - * @bug 6636323 6636319 7040220 7096080 7183053 8080248 8054307 + * @bug 6636323 6636319 7040220 7096080 7183053 8080248 8054307 8372353 * @summary Test if StringCoding and NIO result have the same de/encoding result * @library /test/lib * @modules java.base/sun.nio.cs @@ -169,6 +169,12 @@ public class TestStringCoding { if (!Arrays.equals(baSC, baNIO)) { throw new RuntimeException("getBytes(cs) failed -> " + cs.name()); } + //encodedLength(cs); + int encodedLength = str.encodedLength(cs); + if (baSC.length != encodedLength) { + throw new RuntimeException(String.format("encodedLength failed (%d != %d) -> %s", + baSC.length, encodedLength, cs.name())); + } return baSC; } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/StringLoopJmhBenchmark.java b/test/micro/org/openjdk/bench/java/lang/foreign/StringLoopJmhBenchmark.java new file mode 100644 index 00000000000..1733b73886e --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/StringLoopJmhBenchmark.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2026, Google LLC. 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 org.openjdk.bench.java.lang.foreign; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.annotations.State; + +@Warmup(time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Benchmark) +public class StringLoopJmhBenchmark { + @Param({"10", "100", "1000", "100000"}) + int stringLength; + + @Param({"ASCII", "LATIN1", "UTF16"}) + String encoding; + + String stringData; + + @Setup + public void setUp() { + stringData = ""; + + // Character at the _end_ to affect if we hit + // - ASCII = compact strings and compatible with UTF-8 + // - LATIN1 = compact strings but not compatible with UTF-8 + // - UTF16 = 2-byte char storage and not compatible with UTF-8 + String c; + if (encoding.equals("ASCII")) { + c = "a"; + } else if (encoding.equals("LATIN1")) { + c = "\u00C4"; + } else if (encoding.equals("UTF16")) { + c = "\u2603"; + } else { + throw new IllegalArgumentException("Unknown encoding: " + encoding); + } + + var stringDataBuilder = new StringBuilder(stringLength + 1); + while (stringDataBuilder.length() < stringLength) { + stringDataBuilder.append((char) (Math.random() * 26) + 'a'); + } + stringData = stringDataBuilder.append(c).toString(); + } + + @Benchmark + public int utf8LenByLoop() { + final String s = stringData; + final int len = s.length(); + + // ASCII prefix strings. + int idx = 0; + for (char c; idx < len && (c = s.charAt(idx)) < 0x80; ++idx) {} + + // Entire string was ASCII. + if (idx == len) { + return len; + } + + int utf8Len = len; + for (char c; idx < len; ++idx) { + c = s.charAt(idx); + if (c < 0x80) { + utf8Len++; + } else if (c < 0x800) { + utf8Len += 2; + } else { + utf8Len += 3; + if (Character.isSurrogate(c)) { + int cp = Character.codePointAt(s, idx); + if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + throw new RuntimeException("Unpaired surrogate"); + } + idx++; + } + } + } + return utf8Len; + } + + @Benchmark + public int getBytes() throws Exception { + return stringData.getBytes(StandardCharsets.UTF_8).length; + } + + @Benchmark + public int encodedLength() throws Exception { + return stringData.encodedLength(StandardCharsets.UTF_8); + } +} From c52d7b7cbc89548c3e9cd68a29ff0cec04888b09 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Wed, 4 Mar 2026 18:50:13 +0000 Subject: [PATCH 164/636] 8378878: Refactor java/nio/channels/AsynchronousSocketChannel test to use JUnit Reviewed-by: alanb --- .../CompletionHandlerRelease.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/test/jdk/java/nio/channels/AsynchronousSocketChannel/CompletionHandlerRelease.java b/test/jdk/java/nio/channels/AsynchronousSocketChannel/CompletionHandlerRelease.java index 7abbf064b40..962d7728a55 100644 --- a/test/jdk/java/nio/channels/AsynchronousSocketChannel/CompletionHandlerRelease.java +++ b/test/jdk/java/nio/channels/AsynchronousSocketChannel/CompletionHandlerRelease.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,7 +23,7 @@ /* @test * @bug 8202252 - * @run testng CompletionHandlerRelease + * @run junit CompletionHandlerRelease * @summary Verify that reference to CompletionHandler is cleared after use */ @@ -44,10 +44,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; public class CompletionHandlerRelease { @Test @@ -132,16 +134,16 @@ public class CompletionHandlerRelease { } } - private AsynchronousChannelGroup GROUP; + private static AsynchronousChannelGroup GROUP; - @BeforeTest - void setup() throws IOException { + @BeforeAll + static void setup() throws IOException { GROUP = AsynchronousChannelGroup.withFixedThreadPool(2, Executors.defaultThreadFactory()); } - @AfterTest - void cleanup() throws IOException { + @AfterAll + static void cleanup() throws IOException { GROUP.shutdownNow(); } @@ -199,13 +201,13 @@ public class CompletionHandlerRelease { } } - private void waitForRefToClear(Reference ref, ReferenceQueue queue) + private static void waitForRefToClear(Reference ref, ReferenceQueue queue) throws InterruptedException { Reference r; while ((r = queue.remove(20)) == null) { System.gc(); } - assertEquals(r, ref); + assertSame(ref, r); assertNull(r.get()); } } From 9d1d0c6f0553c5f042351d1def385589015fefd6 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 4 Mar 2026 19:46:06 +0000 Subject: [PATCH 165/636] 8379166: Upstream redundant diffs fixed in Valhalla - Part 1 Reviewed-by: rriggs --- .../classes/java/io/ObjectInputStream.java | 2 +- .../share/classes/java/lang/Boolean.java | 4 --- .../share/classes/java/lang/Byte.java | 1 - .../share/classes/java/lang/Class.java | 9 ++++--- .../share/classes/java/lang/Short.java | 1 - .../java/lang/doc-files/ValueBased.html | 22 ++++++++-------- .../classes/java/lang/invoke/MemberName.java | 25 ++++++++++--------- .../java/lang/invoke/MethodHandleProxies.java | 2 +- .../classes/java/lang/reflect/Proxy.java | 5 ++-- .../share/classes/java/util/WeakHashMap.java | 2 +- .../classfile/impl/DirectClassBuilder.java | 1 - .../impl/verifier/VerificationTable.java | 2 +- .../jdk/internal/jimage/ImageReader.java | 1 + src/java.base/share/classes/module-info.java | 9 +++---- .../include/classfile_constants.h.template | 2 +- 15 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index daed5f3cce5..cd6bd7683f7 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -2219,7 +2219,7 @@ public class ObjectInputStream * mechanism marks the record as having an exception. * Null is returned from readRecord and later the exception is thrown at * the exit of {@link #readObject(Class)}. - **/ + */ private Object readRecord(ObjectStreamClass desc) throws IOException { ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); if (slots.length != 1) { diff --git a/src/java.base/share/classes/java/lang/Boolean.java b/src/java.base/share/classes/java/lang/Boolean.java index 4c24e98a549..49ab80edfea 100644 --- a/src/java.base/share/classes/java/lang/Boolean.java +++ b/src/java.base/share/classes/java/lang/Boolean.java @@ -28,14 +28,10 @@ package java.lang; import jdk.internal.vm.annotation.IntrinsicCandidate; import java.lang.constant.Constable; -import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.DynamicConstantDesc; import java.util.Optional; -import static java.lang.constant.ConstantDescs.BSM_GET_STATIC_FINAL; -import static java.lang.constant.ConstantDescs.CD_Boolean; - /** * The {@code Boolean} class is the {@linkplain * java.lang##wrapperClass wrapper class} for values of the primitive diff --git a/src/java.base/share/classes/java/lang/Byte.java b/src/java.base/share/classes/java/lang/Byte.java index 0f3f7f40d05..c2c03e7a3c2 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -36,7 +36,6 @@ import java.util.Optional; import static java.lang.constant.ConstantDescs.BSM_EXPLICIT_CAST; import static java.lang.constant.ConstantDescs.CD_byte; -import static java.lang.constant.ConstantDescs.CD_int; import static java.lang.constant.ConstantDescs.DEFAULT_NAME; /** diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index eab1993a2b4..8a2f722f3dd 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -224,9 +224,9 @@ public final class Class implements java.io.Serializable, AnnotatedElement, TypeDescriptor.OfField>, Constable { - private static final int ANNOTATION= 0x00002000; - private static final int ENUM = 0x00004000; - private static final int SYNTHETIC = 0x00001000; + private static final int ANNOTATION = 0x00002000; + private static final int ENUM = 0x00004000; + private static final int SYNTHETIC = 0x00001000; private static native void registerNatives(); static { @@ -1390,6 +1390,7 @@ public final class Class implements java.io.Serializable, // INNER_CLASS forbids. INNER_CLASS allows PRIVATE, PROTECTED, // and STATIC, which are not allowed on Location.CLASS. // Use getClassFileAccessFlags to expose SUPER status. + // Arrays need to use PRIVATE/PROTECTED from its component modifiers. var location = (isMemberClass() || isLocalClass() || isAnonymousClass() || isArray()) ? AccessFlag.Location.INNER_CLASS : @@ -3779,7 +3780,7 @@ public final class Class implements java.io.Serializable, * @since 1.8 */ public AnnotatedType[] getAnnotatedInterfaces() { - return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this); + return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this); } private native Class getNestHost0(); diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index 920500a7fa3..14f7a267165 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -35,7 +35,6 @@ import java.lang.constant.DynamicConstantDesc; import java.util.Optional; import static java.lang.constant.ConstantDescs.BSM_EXPLICIT_CAST; -import static java.lang.constant.ConstantDescs.CD_int; import static java.lang.constant.ConstantDescs.CD_short; import static java.lang.constant.ConstantDescs.DEFAULT_NAME; diff --git a/src/java.base/share/classes/java/lang/doc-files/ValueBased.html b/src/java.base/share/classes/java/lang/doc-files/ValueBased.html index 6a935afe04b..3b860ce0534 100644 --- a/src/java.base/share/classes/java/lang/doc-files/ValueBased.html +++ b/src/java.base/share/classes/java/lang/doc-files/ValueBased.html @@ -30,35 +30,35 @@

{@index "Value-based Classes"}

-Some classes, such as java.lang.Integer and -java.time.LocalDate, are value-based. +Some classes, such as {@code java.lang.Integer} and +{@code java.time.LocalDate}, are value-based. A value-based class has the following properties:
  • the class declares only final instance fields (though these may contain references to mutable objects);
  • -
  • the class's implementations of equals, hashCode, - and toString compute their results solely from the values +
  • the class's implementations of {@code equals}, {@code hashCode}, + and {@code toString} compute their results solely from the values of the class's instance fields (and the members of the objects they reference), not from the instance's identity;
  • the class's methods treat instances as freely substitutable - when equal, meaning that interchanging any two instances x and - y that are equal according to equals() produces no + when equal, meaning that interchanging any two instances {@code x} and + {@code y} that are equal according to {@code equals()} produces no visible change in the behavior of the class's methods;
  • the class performs no synchronization using an instance's monitor;
  • -
  • the class does not declare (or has deprecated any) accessible constructors;
  • +
  • the class does not declare (or discourages use of) accessible constructors;
  • the class does not provide any instance creation mechanism that promises a unique identity on each method call—in particular, any factory method's contract must allow for the possibility that if two independently-produced - instances are equal according to equals(), they may also be - equal according to ==;
  • -
  • the class is final, and extends either Object or a hierarchy of + instances are equal according to {@code equals()}, they may also be + equal according to {@code ==};
  • +
  • the class is final, and extends either {@code Object} or a hierarchy of abstract classes that declare no instance fields or instance initializers and whose constructors are empty.

When two instances of a value-based class are equal (according to `equals`), a program should not attempt to distinguish between their identities, whether directly via reference - equality or indirectly via an appeal to synchronization, identity hashing, + equality {@code ==} or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism.

Synchronization on instances of value-based classes is strongly discouraged, diff --git a/src/java.base/share/classes/java/lang/invoke/MemberName.java b/src/java.base/share/classes/java/lang/invoke/MemberName.java index 918d1b10791..d320ad11ade 100644 --- a/src/java.base/share/classes/java/lang/invoke/MemberName.java +++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java @@ -408,11 +408,12 @@ final class MemberName implements Member, Cloneable { // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo // unofficial modifier flags, used by HotSpot: - static final int BRIDGE = 0x00000040; - static final int VARARGS = 0x00000080; - static final int SYNTHETIC = 0x00001000; - static final int ANNOTATION= 0x00002000; - static final int ENUM = 0x00004000; + static final int BRIDGE = 0x00000040; + static final int VARARGS = 0x00000080; + static final int SYNTHETIC = 0x00001000; + static final int ANNOTATION = 0x00002000; + static final int ENUM = 0x00004000; + /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */ public boolean isBridge() { return allFlagsSet(IS_METHOD | BRIDGE); @@ -426,19 +427,19 @@ final class MemberName implements Member, Cloneable { return allFlagsSet(SYNTHETIC); } - static final String CONSTRUCTOR_NAME = ""; // the ever-popular + static final String CONSTRUCTOR_NAME = ""; // modifiers exported by the JVM: static final int RECOGNIZED_MODIFIERS = 0xFFFF; // private flags, not part of RECOGNIZED_MODIFIERS: static final int - IS_METHOD = MN_IS_METHOD, // method (not constructor) - IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor - IS_FIELD = MN_IS_FIELD, // field - IS_TYPE = MN_IS_TYPE, // nested type - CALLER_SENSITIVE = MN_CALLER_SENSITIVE, // @CallerSensitive annotation detected - TRUSTED_FINAL = MN_TRUSTED_FINAL; // trusted final field + IS_METHOD = MN_IS_METHOD, // method (not constructor) + IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor + IS_FIELD = MN_IS_FIELD, // field + IS_TYPE = MN_IS_TYPE, // nested type + CALLER_SENSITIVE = MN_CALLER_SENSITIVE, // @CallerSensitive annotation detected + TRUSTED_FINAL = MN_TRUSTED_FINAL; // trusted final field static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED; static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE; diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index 16f5c7e59b8..8dac21c8968 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -344,7 +344,7 @@ public final class MethodHandleProxies { ClassLoaders.platformClassLoader() : loader))) .build(proxyDesc, clb -> { clb.withSuperclass(CD_Object) - .withFlags(ACC_FINAL | ACC_SYNTHETIC) + .withFlags(ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC) .withInterfaceSymbols(ifaceDesc) // static and instance fields .withField(TYPE_NAME, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL) diff --git a/src/java.base/share/classes/java/lang/reflect/Proxy.java b/src/java.base/share/classes/java/lang/reflect/Proxy.java index b811deb863d..6ce8e80cdca 100644 --- a/src/java.base/share/classes/java/lang/reflect/Proxy.java +++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,6 +25,7 @@ package java.lang.reflect; +import java.lang.classfile.ClassFile; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -467,7 +468,7 @@ public class Proxy implements java.io.Serializable { * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, - context.accessFlags() | Modifier.FINAL); + context.accessFlags() | Modifier.FINAL | ClassFile.ACC_SUPER); try { Class pc = JLA.defineClass(loader, proxyName, proxyClassFile, null, "__dynamic_proxy__"); diff --git a/src/java.base/share/classes/java/util/WeakHashMap.java b/src/java.base/share/classes/java/util/WeakHashMap.java index b5a27593840..1412d8f6ff9 100644 --- a/src/java.base/share/classes/java/util/WeakHashMap.java +++ b/src/java.base/share/classes/java/util/WeakHashMap.java @@ -25,8 +25,8 @@ package java.util; -import java.lang.ref.WeakReference; import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 0e82c545359..79c623bc31d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -168,7 +168,6 @@ public final class DirectClassBuilder this.sizeHint = sizeHint; } - public byte[] build() { // The logic of this is very carefully ordered. We want to avoid diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java index 04276b8eeb8..eb3f5ee913d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationTable.java @@ -321,7 +321,7 @@ class VerificationTable { return frame; } int offset_delta = _stream.get_u2(); - if (frame_type < SAME_LOCALS_1_STACK_ITEM_EXTENDED) { + if (frame_type <= RESERVED_END) { _verifier.classError("reserved frame type"); } if (frame_type == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java index c36e265ee2f..4c358820166 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java @@ -821,6 +821,7 @@ public final class ImageReader implements AutoCloseable { this.children = Collections.unmodifiableList(children); } } + /** * Resource node (e.g. a ".class" entry, or any other data resource). * diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index d20f6311bca..665b3a3b98d 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -135,7 +135,6 @@ module java.base { exports javax.security.auth.x500; exports javax.security.cert; - // additional qualified exports may be inserted at build time // see make/gensrc/GenModuleInfo.gmk @@ -147,11 +146,11 @@ module java.base { java.security.sasl; exports jdk.internal to jdk.incubator.vector; - // Note: all modules in the exported list participate in preview features - // and therefore if they use preview features they do not need to be - // compiled with "--enable-preview". + // Note: all modules in the exported list participate in preview features, + // normal or reflective. They do not need to be compiled with "--enable-preview" + // to use preview features and do not need to suppress "preview" warnings. // It is recommended for any modules that do participate that their - // module declaration be annotated with jdk.internal.javac.ParticipatesInPreview + // module declaration be annotated with jdk.internal.javac.ParticipatesInPreview. exports jdk.internal.javac to java.compiler, jdk.compiler; diff --git a/src/java.base/share/native/include/classfile_constants.h.template b/src/java.base/share/native/include/classfile_constants.h.template index fb022ec1fd4..4f96a0673ef 100644 --- a/src/java.base/share/native/include/classfile_constants.h.template +++ b/src/java.base/share/native/include/classfile_constants.h.template @@ -111,7 +111,7 @@ enum { JVM_CONSTANT_InvokeDynamic = 18, JVM_CONSTANT_Module = 19, JVM_CONSTANT_Package = 20, - JVM_CONSTANT_ExternalMax = 20 + JVM_CONSTANT_ExternalMax = 20 }; /* JVM_CONSTANT_MethodHandle subtypes */ From 08c8520b39083ec6354dc5df2f18c1f4c3588053 Mon Sep 17 00:00:00 2001 From: Patrick Strawderman Date: Wed, 4 Mar 2026 20:04:30 +0000 Subject: [PATCH 166/636] 8378698: Optimize Base64.Encoder#encodeToString Reviewed-by: liach, rriggs --- .../share/classes/java/util/Base64.java | 6 +- .../bench/java/util/Base64EncodeToString.java | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/util/Base64EncodeToString.java diff --git a/src/java.base/share/classes/java/util/Base64.java b/src/java.base/share/classes/java/util/Base64.java index ed1a4a8d638..fd714050149 100644 --- a/src/java.base/share/classes/java/util/Base64.java +++ b/src/java.base/share/classes/java/util/Base64.java @@ -32,6 +32,8 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import sun.nio.cs.ISO_8859_1; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -201,6 +203,7 @@ public final class Base64 { * @since 1.8 */ public static class Encoder { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private final byte[] newline; private final int linemax; @@ -344,10 +347,9 @@ public final class Base64 { * the byte array to encode * @return A String containing the resulting Base64 encoded characters */ - @SuppressWarnings("deprecation") public String encodeToString(byte[] src) { byte[] encoded = encode(src); - return new String(encoded, 0, 0, encoded.length); + return JLA.uncheckedNewStringWithLatin1Bytes(encoded); } /** diff --git a/test/micro/org/openjdk/bench/java/util/Base64EncodeToString.java b/test/micro/org/openjdk/bench/java/util/Base64EncodeToString.java new file mode 100644 index 00000000000..5b259230341 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/util/Base64EncodeToString.java @@ -0,0 +1,55 @@ +/* + * 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 org.openjdk.micro.bench.java.util; + +import org.openjdk.jmh.annotations.*; + +import java.util.Base64; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@State(Scope.Benchmark) +@Warmup(iterations = 5, time = 2) +@Measurement(iterations = 5, time = 2) +@Fork(value = 2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class Base64EncodeToString { + + private byte[] input; + + @Param({"10", "100", "1000", "10000"}) + private int inputSize; + + @Setup + public void setup() { + Random r = new Random(1123); + input = new byte[inputSize]; + r.nextBytes(input); + } + + @Benchmark + public String testEncodeToString() { + return Base64.getEncoder().encodeToString(input); + } +} + From 1f4a7bbb9d67fdaaf63a70d92df895aea41ad201 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 4 Mar 2026 22:42:19 +0000 Subject: [PATCH 167/636] 8379040: Remove inclusion of allocation.hpp from atomicAccess.hpp Reviewed-by: iwalulya, dholmes --- src/hotspot/share/opto/phasetype.hpp | 3 ++- src/hotspot/share/runtime/atomicAccess.hpp | 6 +++--- src/hotspot/share/utilities/bitMap.hpp | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index f388dc6cdc6..ce432fbfc01 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #ifndef SHARE_OPTO_PHASETYPE_HPP #define SHARE_OPTO_PHASETYPE_HPP +#include "memory/allocation.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/stringUtils.hpp" diff --git a/src/hotspot/share/runtime/atomicAccess.hpp b/src/hotspot/share/runtime/atomicAccess.hpp index c9a2dfb9383..46330cffdb2 100644 --- a/src/hotspot/share/runtime/atomicAccess.hpp +++ b/src/hotspot/share/runtime/atomicAccess.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ #define SHARE_RUNTIME_ATOMICACCESS_HPP #include "cppstdlib/type_traits.hpp" -#include "memory/allocation.hpp" +#include "memory/allStatic.hpp" #include "metaprogramming/enableIf.hpp" #include "metaprogramming/primitiveConversions.hpp" #include "runtime/orderAccess.hpp" @@ -829,7 +829,7 @@ class AtomicAccess::PlatformBitops {}; template -class ScopedFenceGeneral: public StackObj { +class ScopedFenceGeneral { public: void prefix() {} void postfix() {} diff --git a/src/hotspot/share/utilities/bitMap.hpp b/src/hotspot/share/utilities/bitMap.hpp index 5ee462bbe47..17bf437ecae 100644 --- a/src/hotspot/share/utilities/bitMap.hpp +++ b/src/hotspot/share/utilities/bitMap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -30,6 +30,7 @@ #include "utilities/globalDefinitions.hpp" // Forward decl; +class Arena; class BitMapClosure; // Operations for bitmaps represented as arrays of unsigned integers. From c9da76bf33a5e44eb238e3b29eda523e7754b7b6 Mon Sep 17 00:00:00 2001 From: Xiaohong Gong Date: Thu, 5 Mar 2026 01:57:03 +0000 Subject: [PATCH 168/636] 8377449: Strengthen vector IR validation in TestVectorAlgorithms.java for AArch64 Reviewed-by: mhaessig, epeter --- .../vectorization/TestVectorAlgorithms.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java index e02563c2fc2..785a6de30a0 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java @@ -272,8 +272,10 @@ public class TestVectorAlgorithms { @Test @IR(counts = {IRNode.ADD_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfCPUFeature = {"sse4.1", "true"}) - // Note: also works with NEON/asimd, but only with TieredCompilation. + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"TieredCompilation", "true"}) + // IR check only works with TieredCompilation. Needs to make it + // work with -XX:-TieredCompilation in future (see JDK-8378640). public Object iotaI_VectorAPI(int[] r) { return VectorAlgorithmsImpl.iotaI_VectorAPI(r); } @@ -360,7 +362,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.ADD_REDUCTION_V, "> 0", IRNode.MUL_VF, "> 0"}, - applyIfCPUFeature = {"sse4.1", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, applyIf = {"UseSuperWord", "true"}) public float dotProductF_VectorAPI_naive(float[] a, float[] b) { return VectorAlgorithmsImpl.dotProductF_VectorAPI_naive(a, b); @@ -370,7 +372,7 @@ public class TestVectorAlgorithms { @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", IRNode.ADD_REDUCTION_V, "> 0", IRNode.MUL_VF, "> 0"}, - applyIfCPUFeature = {"sse4.1", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, applyIf = {"UseSuperWord", "true"}) public float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { return VectorAlgorithmsImpl.dotProductF_VectorAPI_reduction_after_loop(a, b); @@ -392,7 +394,8 @@ public class TestVectorAlgorithms { IRNode.MUL_VI, IRNode.VECTOR_SIZE_8, "> 0", IRNode.ADD_VI, IRNode.VECTOR_SIZE_8, "> 0", IRNode.ADD_REDUCTION_VI, "> 0"}, - applyIfCPUFeature = {"avx2", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "sve", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) public int hashCodeB_VectorAPI_v1(byte[] a) { return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v1(a); } @@ -402,7 +405,7 @@ public class TestVectorAlgorithms { IRNode.MUL_VI, "> 0", IRNode.ADD_VI, "> 0", IRNode.ADD_REDUCTION_VI, "> 0"}, - applyIfCPUFeature = {"avx2", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) public int hashCodeB_VectorAPI_v2(byte[] a) { return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v2(a); } @@ -506,7 +509,7 @@ public class TestVectorAlgorithms { IRNode.VECTOR_TEST, "> 0", IRNode.COMPRESS_VI, "> 0", IRNode.STORE_VECTOR_MASKED, "> 0"}, - applyIfCPUFeature = {"avx2", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "sve", "true"}) public Object filterI_VectorAPI(int[] a, int[] r, int threshold) { return VectorAlgorithmsImpl.filterI_VectorAPI(a, r, threshold); } @@ -526,7 +529,10 @@ public class TestVectorAlgorithms { IRNode.OR_V_MASK, "> 0", IRNode.ADD_VI, "> 0", IRNode.ADD_REDUCTION_VI, "> 0"}, - applyIfCPUFeatureOr = {"avx512", "true", "sve", "true"}) + applyIfCPUFeatureOr = {"avx512", "true", "sve", "true"}, + applyIf = {"TieredCompilation", "true"}) + // IR check only works with TieredCompilation. Needs to make it + // work with -XX:-TieredCompilation in future (see JDK-8378640). public int reduceAddIFieldsX4_VectorAPI(int[] oops, int[] mem) { return VectorAlgorithmsImpl.reduceAddIFieldsX4_VectorAPI(oops, mem); } From c87ecaddfb32485b2ecaecc9284ce37b610ffffc Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Thu, 5 Mar 2026 09:23:21 +0000 Subject: [PATCH 169/636] 8379192: Use an initializer list in the ObjectWaiter constructor Reviewed-by: coleenp, dholmes, aartemov --- src/hotspot/share/runtime/objectMonitor.cpp | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 8c6994c2152..91771772e2c 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -2541,19 +2541,19 @@ bool ObjectMonitor::try_spin(JavaThread* current) { // ----------------------------------------------------------------------------- // wait_set management ... -ObjectWaiter::ObjectWaiter(JavaThread* current) { - _next = nullptr; - _prev = nullptr; - _thread = current; - _monitor = nullptr; - _notifier_tid = 0; - _recursions = 0; - TState = TS_RUN; - _is_wait = false; - _at_reenter = false; - _interrupted = false; - _do_timed_park = false; - _active = false; +ObjectWaiter::ObjectWaiter(JavaThread* current) + : _next(nullptr), + _prev(nullptr), + _thread(current), + _monitor(nullptr), + _notifier_tid(0), + _recursions(0), + TState(TS_RUN), + _is_wait(false), + _at_reenter(false), + _interrupted(false), + _do_timed_park(false), + _active(false) { } const char* ObjectWaiter::getTStateName(ObjectWaiter::TStates state) { From 28e8700f461cee6c80da15c71090eaf608b6f8fa Mon Sep 17 00:00:00 2001 From: David Briemann Date: Thu, 5 Mar 2026 10:08:57 +0000 Subject: [PATCH 170/636] 8378675: PPC64: increase instruction cache line size Reviewed-by: mdoerr --- src/hotspot/cpu/ppc/icache_ppc.cpp | 8 +++- src/hotspot/cpu/ppc/icache_ppc.hpp | 9 ++-- src/hotspot/cpu/ppc/vm_version_ppc.cpp | 39 +++++----------- src/hotspot/cpu/ppc/vm_version_ppc.hpp | 7 ++- .../os_cpu/aix_ppc/vm_version_aix_ppc.cpp | 36 +++++++++++++++ .../os_cpu/linux_ppc/vm_version_linux_ppc.cpp | 44 +++++++++++++++++++ 6 files changed, 105 insertions(+), 38 deletions(-) create mode 100644 src/hotspot/os_cpu/aix_ppc/vm_version_aix_ppc.cpp create mode 100644 src/hotspot/os_cpu/linux_ppc/vm_version_linux_ppc.cpp diff --git a/src/hotspot/cpu/ppc/icache_ppc.cpp b/src/hotspot/cpu/ppc/icache_ppc.cpp index 05ad3c7a30d..f3d51bad18c 100644 --- a/src/hotspot/cpu/ppc/icache_ppc.cpp +++ b/src/hotspot/cpu/ppc/icache_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2018 SAP SE. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ */ #include "runtime/icache.hpp" +#include "runtime/vm_version.hpp" // Use inline assembler to implement icache flush. int ICache::ppc64_flush_icache(address start, int lines, int magic) { @@ -67,6 +68,9 @@ int ICache::ppc64_flush_icache(address start, int lines, int magic) { void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) { + guarantee(VM_Version::get_icache_line_size() >= ICache::line_size, + "processors with smaller cache line size are no longer supported"); + *flush_icache_stub = (ICache::flush_icache_stub_t)ICache::ppc64_flush_icache; // First call to flush itself. diff --git a/src/hotspot/cpu/ppc/icache_ppc.hpp b/src/hotspot/cpu/ppc/icache_ppc.hpp index d348cad1c72..024f706182a 100644 --- a/src/hotspot/cpu/ppc/icache_ppc.hpp +++ b/src/hotspot/cpu/ppc/icache_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2013 SAP SE. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -35,9 +35,8 @@ class ICache : public AbstractICache { public: enum { - // Actually, cache line size is 64, but keeping it as it is to be - // on the safe side on ALL PPC64 implementations. - log2_line_size = 5, + // Cache line size is 128 on all supported PPC64 implementations. + log2_line_size = 7, line_size = 1 << log2_line_size }; diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index 75feb389298..e471f5a6e4f 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -475,19 +475,12 @@ void VM_Version::print_features() { void VM_Version::determine_features() { #if defined(ABI_ELFv2) - // 1 InstWord per call for the blr instruction. - const int code_size = (num_features+1+2*1)*BytesPerInstWord; + const int code_size = (num_features + 1 /*blr*/) * BytesPerInstWord; #else - // 7 InstWords for each call (function descriptor + blr instruction). - const int code_size = (num_features+1+2*7)*BytesPerInstWord; + const int code_size = (num_features + 1 /*blr*/ + 6 /* fd */) * BytesPerInstWord; #endif int features = 0; - // create test area - enum { BUFFER_SIZE = 2*4*K }; // Needs to be >=2* max cache line size (cache line size can't exceed min page size). - char test_area[BUFFER_SIZE]; - char *mid_of_test_area = &test_area[BUFFER_SIZE>>1]; - // Allocate space for the code. ResourceMark rm; CodeBuffer cb("detect_cpu_features", code_size, 0); @@ -497,20 +490,13 @@ void VM_Version::determine_features() { _features = VM_Version::all_features_m; // Emit code. - void (*test)(address addr, uint64_t offset)=(void(*)(address addr, uint64_t offset))(void *)a->function_entry(); + void (*test)() = (void(*)())(void *)a->function_entry(); uint32_t *code = (uint32_t *)a->pc(); - // Keep R3_ARG1 unmodified, it contains &field (see below). - // Keep R4_ARG2 unmodified, it contains offset = 0 (see below). a->mfdscr(R0); a->darn(R7); a->brw(R5, R6); a->blr(); - // Emit function to set one cache line to zero. Emit function descriptor and get pointer to it. - void (*zero_cacheline_func_ptr)(char*) = (void(*)(char*))(void *)a->function_entry(); - a->dcbz(R3_ARG1); // R3_ARG1 = addr - a->blr(); - uint32_t *code_end = (uint32_t *)a->pc(); a->flush(); _features = VM_Version::unknown_m; @@ -522,18 +508,9 @@ void VM_Version::determine_features() { Disassembler::decode((u_char*)code, (u_char*)code_end, tty); } - // Measure cache line size. - memset(test_area, 0xFF, BUFFER_SIZE); // Fill test area with 0xFF. - (*zero_cacheline_func_ptr)(mid_of_test_area); // Call function which executes dcbz to the middle. - int count = 0; // count zeroed bytes - for (int i = 0; i < BUFFER_SIZE; i++) if (test_area[i] == 0) count++; - guarantee(is_power_of_2(count), "cache line size needs to be a power of 2"); - _L1_data_cache_line_size = count; - // Execute code. Illegal instructions will be replaced by 0 in the signal handler. VM_Version::_is_determine_features_test_running = true; - // We must align the first argument to 16 bytes because of the lqarx check. - (*test)(align_up((address)mid_of_test_area, 16), 0); + (*test)(); VM_Version::_is_determine_features_test_running = false; // determine which instructions are legal. @@ -550,6 +527,10 @@ void VM_Version::determine_features() { } _features = features; + + _L1_data_cache_line_size = VM_Version::get_dcache_line_size(); + assert(_L1_data_cache_line_size >= DEFAULT_CACHE_LINE_SIZE, + "processors with smaller cache line size are no longer supported"); } // Power 8: Configure Data Stream Control Register. diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.hpp b/src/hotspot/cpu/ppc/vm_version_ppc.hpp index 11dce83bed0..0f4eb3593a3 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.hpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -81,6 +81,9 @@ public: static uint64_t _dscr_val; static void initialize_cpu_information(void); + + static int get_dcache_line_size(); + static int get_icache_line_size(); }; #endif // CPU_PPC_VM_VERSION_PPC_HPP diff --git a/src/hotspot/os_cpu/aix_ppc/vm_version_aix_ppc.cpp b/src/hotspot/os_cpu/aix_ppc/vm_version_aix_ppc.cpp new file mode 100644 index 00000000000..8cc8b715201 --- /dev/null +++ b/src/hotspot/os_cpu/aix_ppc/vm_version_aix_ppc.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 SAP SE. 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. + * + */ + +#include "runtime/vm_version.hpp" + +#include + +int VM_Version::get_dcache_line_size() { + return _system_configuration.dcache_line; +} + +int VM_Version::get_icache_line_size() { + return _system_configuration.icache_line; +} diff --git a/src/hotspot/os_cpu/linux_ppc/vm_version_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/vm_version_linux_ppc.cpp new file mode 100644 index 00000000000..d64340edf5c --- /dev/null +++ b/src/hotspot/os_cpu/linux_ppc/vm_version_linux_ppc.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 SAP SE. 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. + * + */ + +#include "runtime/vm_version.hpp" + +#include + +int VM_Version::get_dcache_line_size() { + // This should work on all modern linux versions: + int size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + // It may fail with very old linux / glibc versions. We use DEFAULT_CACHE_LINE_SIZE in this case. + // That is the correct value for all currently supported processors. + return (size <= 0) ? DEFAULT_CACHE_LINE_SIZE : size; +} + +int VM_Version::get_icache_line_size() { + // This should work on all modern linux versions: + int size = sysconf(_SC_LEVEL1_ICACHE_LINESIZE); + // It may fail with very old linux / glibc versions. We use DEFAULT_CACHE_LINE_SIZE in this case. + // That is the correct value for all currently supported processors. + return (size <= 0) ? DEFAULT_CACHE_LINE_SIZE : size; +} From c9a0e365083a7868c49f082623d722e721838642 Mon Sep 17 00:00:00 2001 From: Ivan Walulya Date: Thu, 5 Mar 2026 10:13:26 +0000 Subject: [PATCH 171/636] 8378376: DaCapo-h2-large regression after JDK-8238686 Reviewed-by: kbarrett, tschatzl --- src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp | 8 +++++++- src/hotspot/share/gc/shared/collectedHeap.hpp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp index 4dd0a509bcd..1b9704e8ad3 100644 --- a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -366,6 +366,12 @@ static size_t target_heap_capacity(size_t used_bytes, uintx free_ratio) { } size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand, size_t allocation_word_size) { + // User-requested Full GCs introduce GC load unrelated to heap size; reset CPU + // usage tracking so heap resizing heuristics are driven only by GC pressure. + if (GCCause::is_user_requested_gc(_g1h->gc_cause())) { + reset_cpu_usage_tracking_data(); + } + const size_t capacity_after_gc = _g1h->capacity(); // Capacity, free and used after the GC counted as full regions to // include the waste in the following calculations. diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index f13c3ab7b6e..100866bb528 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -289,7 +289,7 @@ protected: DEBUG_ONLY(bool is_in_or_null(const void* p) const { return p == nullptr || is_in(p); }) void set_gc_cause(GCCause::Cause v); - GCCause::Cause gc_cause() { return _gc_cause; } + GCCause::Cause gc_cause() const { return _gc_cause; } oop obj_allocate(Klass* klass, size_t size, TRAPS); virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS); From ec3b58b5e0c93c9e32a59dd1b65bd3b9dea6e12b Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 5 Mar 2026 10:37:23 +0000 Subject: [PATCH 172/636] 8379162: AggregateRequestBodyTest.java intermittent fails "Connection closed by client peer: APPLICATION_ERROR" Reviewed-by: jpai, syan, djelinski --- .../httpclient/AggregateRequestBodyTest.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/test/jdk/java/net/httpclient/AggregateRequestBodyTest.java b/test/jdk/java/net/httpclient/AggregateRequestBodyTest.java index 175e860c229..fb67cf02566 100644 --- a/test/jdk/java/net/httpclient/AggregateRequestBodyTest.java +++ b/test/jdk/java/net/httpclient/AggregateRequestBodyTest.java @@ -74,6 +74,7 @@ import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Version.HTTP_3; +import static java.net.http.HttpOption.H3_DISCOVERY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import org.junit.jupiter.api.AfterAll; @@ -81,7 +82,6 @@ import org.junit.jupiter.api.Assertions; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; @@ -219,7 +219,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { int i = 0; for (boolean sameClient : List.of(false, true)) { for (URI uri : uris()) { - HttpClient.Version version = null; + final HttpClient.Version version; if (uri.equals(http1URI) || uri.equals(https1URI)) version = HttpClient.Version.HTTP_1_1; else if (uri.equals(http2URI) || uri.equals(https2URI)) version = HttpClient.Version.HTTP_2; else if (uri.equals(http3URI)) version = HTTP_3; @@ -295,7 +295,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { static List lengths(long... lengths) { return LongStream.of(lengths) - .mapToObj(Long::valueOf) + .boxed() .collect(Collectors.toList()); } @@ -446,7 +446,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { @Override public void onComplete() { - resultCF.complete(items.stream().collect(Collectors.toUnmodifiableList())); + resultCF.complete(items.stream().toList()); } public ByteBuffer take() { @@ -552,7 +552,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { subscriber.subscriptionCF.thenAccept(s -> s.request(Long.MAX_VALUE)); if (errorPublisher.hasErrors()) { CompletionException ce = Assertions.assertThrows(CompletionException.class, - () -> subscriber.resultCF.join()); + subscriber.resultCF::join); out.println(description + ": got expected " + ce); assertEquals(Exception.class, ce.getCause().getClass()); assertEquals(result, stringFromBytes(subscriber.items.stream()) + ""); @@ -600,7 +600,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { description.replace("null", "length(-1)")); } - private static final Throwable completionCause(CompletionException x) { + private static Throwable completionCause(CompletionException x) { while (x.getCause() instanceof CompletionException) { x = (CompletionException)x.getCause(); } @@ -618,7 +618,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { publisher.subscribe(subscriber); Subscription subscription = subscriber.subscriptionCF.join(); subscription.request(n); - CompletionException expected = Assertions.assertThrows(CE, () -> subscriber.resultCF.join()); + CompletionException expected = Assertions.assertThrows(CE, subscriber.resultCF::join); Throwable cause = completionCause(expected); if (cause instanceof IllegalArgumentException) { System.out.printf("Got expected IAE for %d: %s%n", n, cause); @@ -655,7 +655,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { assertTrue(requestSubscriber1.resultCF().isDone()); String result1 = stringFromBytes(list1.stream()); assertEquals("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", result1); - System.out.println("Got expected sentence with one request: \"%s\"".formatted(result1)); + System.out.printf("Got expected sentence with one request: \"%s\"%n", result1); // Test that we can split our requests call any which way we want // (whether in the 'middle of a publisher' or at the boundaries. @@ -824,7 +824,7 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { .toArray(HttpRequest.BodyPublisher[]::new) ); - HttpRequest request = HttpRequest.newBuilder(uri) + HttpRequest request = newRequestBuilder(uri) .version(version) .POST(publisher) .build(); @@ -835,13 +835,21 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { HttpResponse response = client.send(request, BodyHandlers.ofString()); int expectedResponse = RESPONSE_CODE; if (response.statusCode() != expectedResponse) - throw new RuntimeException("wrong response code " + Integer.toString(response.statusCode())); + throw new RuntimeException("wrong response code " + response.statusCode()); assertEquals(BODIES.stream().collect(Collectors.joining()), response.body()); } if (!sameClient) client.close(); System.out.println("test: DONE"); } + private static HttpRequest.Builder newRequestBuilder(URI uri) { + var builder = HttpRequest.newBuilder(uri); + if (uri.getPath().contains("/http3/")) { + builder.setOption(H3_DISCOVERY, HTTP_3_URI_ONLY); + } + return builder; + } + private static URI buildURI(String scheme, String path, int port) { return URIBuilder.newBuilder() .scheme(scheme) @@ -882,11 +890,17 @@ public class AggregateRequestBodyTest implements HttpServerAdapters { http3TestServer.start(); } + private static void close(AutoCloseable closeable) throws Exception { + if (closeable == null) return; + out.println("Closing shared client " + closeable); + closeable.close(); + } + @AfterAll public static void teardown() throws Exception { String sharedClientName = sharedClient == null ? null : sharedClient.toString(); - sharedClient.close(); + close(sharedClient); sharedClient = null; Thread.sleep(100); AssertionError fail = TRACKER.check(500); From 8a9b63f76fd678c6883a51332aeb84846791ed5e Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Thu, 5 Mar 2026 11:05:01 +0000 Subject: [PATCH 173/636] 8378413: C2: Missed Ideal optimization opportunity in PhaseIterGVN for URShiftI still exists Reviewed-by: qamai, dlong --- src/hotspot/share/opto/mulnode.cpp | 18 ++- .../c2/gvn/MissedURShiftIAddILShiftIdeal.java | 134 ++++++++++++++++++ 2 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 9bdaa3b9f34..cac9f1dcc37 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -1539,15 +1539,20 @@ Node* URShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) { Node *add = in(1); if (in1_op == Op_AddI) { Node *lshl = add->in(1); + Node *y = add->in(2); + if (lshl->Opcode() != Op_LShiftI) { + lshl = add->in(2); + y = add->in(1); + } // Compare shift counts by value, not by node pointer, to also match a not-yet-normalized // negative constant (e.g. -1 vs 31) int lshl_con = 0; if (lshl->Opcode() == Op_LShiftI && const_shift_count(phase, lshl, &lshl_con) && (lshl_con & (BitsPerJavaInteger - 1)) == con) { - Node *y_z = phase->transform( new URShiftINode(add->in(2),in(2)) ); - Node *sum = phase->transform( new AddINode( lshl->in(1), y_z ) ); - return new AndINode( sum, phase->intcon(mask) ); + Node *y_z = phase->transform(new URShiftINode(y, in(2))); + Node *sum = phase->transform(new AddINode(lshl->in(1), y_z)); + return new AndINode(sum, phase->intcon(mask)); } } @@ -1699,13 +1704,18 @@ Node* URShiftLNode::Ideal(PhaseGVN* phase, bool can_reshape) { const TypeInt *t2 = phase->type(in(2))->isa_int(); if (add->Opcode() == Op_AddL) { Node *lshl = add->in(1); + Node *y = add->in(2); + if (lshl->Opcode() != Op_LShiftL) { + lshl = add->in(2); + y = add->in(1); + } // Compare shift counts by value, not by node pointer, to also match a not-yet-normalized // negative constant (e.g. -1 vs 63) int lshl_con = 0; if (lshl->Opcode() == Op_LShiftL && const_shift_count(phase, lshl, &lshl_con) && (lshl_con & (BitsPerJavaLong - 1)) == con) { - Node* y_z = phase->transform(new URShiftLNode(add->in(2), in(2))); + Node* y_z = phase->transform(new URShiftLNode(y, in(2))); Node* sum = phase->transform(new AddLNode(lshl->in(1), y_z)); return new AndLNode(sum, phase->longcon(mask)); } diff --git a/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java b/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java new file mode 100644 index 00000000000..a1f4f011466 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/gvn/MissedURShiftIAddILShiftIdeal.java @@ -0,0 +1,134 @@ +/* + * 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.c2.gvn; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import java.util.Random; + +/* + * @test + * @bug 8378413 + * @key randomness + * @summary Verify that URShift{I,L}Node::Ideal optimizes ((x << C) + y) >>> C + * regardless of Add input order, i.e. it is commutative w.r.t. the addition. + * @library /test/lib / + * @run driver ${test.main.class} + * @run driver ${test.main.class} -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:VerifyIterativeGVN=1110 -Xbatch -XX:CompileCommand=compileonly,${test.main.class}::* + */ +public class MissedURShiftIAddILShiftIdeal { + + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Run(test = {"testI", "testICommuted", "testIComputedY", + "testL", "testLCommuted", "testLComputedY"}) + public void runMethod() { + int xi = RANDOM.nextInt(); + int yi = RANDOM.nextInt(); + int ai = RANDOM.nextInt(); + int bi = RANDOM.nextInt(); + long xl = RANDOM.nextLong(); + long yl = RANDOM.nextLong(); + long al = RANDOM.nextLong(); + long bl = RANDOM.nextLong(); + + assertResultI(xi, yi, ai, bi); + assertResultL(xl, yl, al, bl); + } + + @DontCompile + public void assertResultI(int x, int y, int a, int b) { + Asserts.assertEQ(((x << 3) + y) >>> 3, testI(x, y)); + Asserts.assertEQ((y + (x << 5)) >>> 5, testICommuted(x, y)); + Asserts.assertEQ(((x << 7) + (a ^ b)) >>> 7, testIComputedY(x, a, b)); + } + + @DontCompile + public void assertResultL(long x, long y, long a, long b) { + Asserts.assertEQ(((x << 9) + y) >>> 9, testL(x, y)); + Asserts.assertEQ((y + (x << 11)) >>> 11, testLCommuted(x, y)); + Asserts.assertEQ(((x << 13) + (a ^ b)) >>> 13, testLComputedY(x, a, b)); + } + + @Test + // ((x << 3) + y) >>> 3 => (x + (y >>> 3)) & mask + @IR(counts = {IRNode.LSHIFT_I, "0", + IRNode.URSHIFT_I, "1", + IRNode.AND_I, "1"}) + static int testI(int x, int y) { + return ((x << 3) + y) >>> 3; + } + + @Test + // (y + (x << 5)) >>> 5 => (x + (y >>> 5)) & mask (commuted Add) + @IR(counts = {IRNode.LSHIFT_I, "0", + IRNode.URSHIFT_I, "1", + IRNode.AND_I, "1"}) + static int testICommuted(int x, int y) { + return (y + (x << 5)) >>> 5; + } + + @Test + // ((x << 7) + (a ^ b)) >>> 7 => (x + ((a ^ b) >>> 7)) & mask + // Computed y (a ^ b) has higher _idx than LShift, so LShift stays in Add's in(1). + @IR(counts = {IRNode.LSHIFT_I, "0", + IRNode.URSHIFT_I, "1", + IRNode.AND_I, "1"}) + static int testIComputedY(int x, int a, int b) { + return ((x << 7) + (a ^ b)) >>> 7; + } + + @Test + // ((x << 9) + y) >>> 9 => (x + (y >>> 9)) & mask + @IR(counts = {IRNode.LSHIFT_L, "0", + IRNode.URSHIFT_L, "1", + IRNode.AND_L, "1"}) + static long testL(long x, long y) { + return ((x << 9) + y) >>> 9; + } + + @Test + // (y + (x << 11)) >>> 11 => (x + (y >>> 11)) & mask (commuted Add) + @IR(counts = {IRNode.LSHIFT_L, "0", + IRNode.URSHIFT_L, "1", + IRNode.AND_L, "1"}) + static long testLCommuted(long x, long y) { + return (y + (x << 11)) >>> 11; + } + + @Test + // ((x << 13) + (a ^ b)) >>> 13 => (x + ((a ^ b) >>> 13)) & mask + // Computed y (a ^ b) has higher _idx than LShift, so LShift stays in Add's in(1). + @IR(counts = {IRNode.LSHIFT_L, "0", + IRNode.URSHIFT_L, "1", + IRNode.AND_L, "1"}) + static long testLComputedY(long x, long a, long b) { + return ((x << 13) + (a ^ b)) >>> 13; + } +} From 97b78f04230452b675a31d8e37f300a2d31e62ed Mon Sep 17 00:00:00 2001 From: Eric Fang Date: Thu, 5 Mar 2026 11:12:08 +0000 Subject: [PATCH 174/636] 8374349: [VectorAPI]: AArch64: Prefer merging mode SVE CPY instruction Reviewed-by: aph --- src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 8 +++---- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 21 +++++++++++++++++++ .../cpu/aarch64/c2_MacroAssembler_aarch64.hpp | 5 +++++ .../cpu/aarch64/vm_version_aarch64.hpp | 5 +++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 19b3bb1a65b..67cf77989d2 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -3814,8 +3814,8 @@ public: } private: - void sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, int imm8, - bool isMerge, bool isFloat) { + void _sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, int imm8, + bool isMerge, bool isFloat) { starti; assert(T != Q, "invalid size"); int sh = 0; @@ -3839,11 +3839,11 @@ private: public: // SVE copy signed integer immediate to vector elements (predicated) void sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, int imm8, bool isMerge) { - sve_cpy(Zd, T, Pg, imm8, isMerge, /*isFloat*/false); + _sve_cpy(Zd, T, Pg, imm8, isMerge, /*isFloat*/false); } // SVE copy floating-point immediate to vector elements (predicated) void sve_cpy(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, double d) { - sve_cpy(Zd, T, Pg, checked_cast(pack(d)), /*isMerge*/true, /*isFloat*/true); + _sve_cpy(Zd, T, Pg, checked_cast(pack(d)), /*isMerge*/true, /*isFloat*/true); } // SVE conditionally select elements from two vectors diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index dc0b9eb9546..7aab7d389e1 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -2875,3 +2875,24 @@ void C2_MacroAssembler::vector_expand_sve(FloatRegister dst, FloatRegister src, // dst = 00 87 00 65 00 43 00 21 sve_tbl(dst, size, src, dst); } + +// Optimized SVE cpy (imm, zeroing) instruction. +// +// `movi; cpy(imm, merging)` and `cpy(imm, zeroing)` have the same +// functionality, but test results show that `movi; cpy(imm, merging)` has +// higher throughput on some microarchitectures. This would depend on +// microarchitecture and so may vary between implementations. +void C2_MacroAssembler::sve_cpy(FloatRegister dst, SIMD_RegVariant T, + PRegister pg, int imm8, bool isMerge) { + if (VM_Version::prefer_sve_merging_mode_cpy() && !isMerge) { + // Generates a NEON instruction `movi V.2d, #0`. + // On AArch64, Z and V registers alias in the low 128 bits, so V is + // the low 128 bits of Z. A write to V also clears all bits of + // Z above 128, so this `movi` instruction effectively zeroes the + // entire Z register. According to the Arm Software Optimization + // Guide, `movi` is zero latency. + movi(dst, T2D, 0); + isMerge = true; + } + Assembler::sve_cpy(dst, T, pg, imm8, isMerge); +} diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 4f3a41da402..5c05832afbe 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -75,6 +75,8 @@ unsigned vector_length_in_bytes); public: + using Assembler::sve_cpy; + // jdk.internal.util.ArraysSupport.vectorizedHashCode address arrays_hashcode(Register ary, Register cnt, Register result, FloatRegister vdata0, FloatRegister vdata1, FloatRegister vdata2, FloatRegister vdata3, @@ -244,4 +246,7 @@ void vector_expand_sve(FloatRegister dst, FloatRegister src, PRegister pg, FloatRegister tmp1, FloatRegister tmp2, BasicType bt, int vector_length_in_bytes); + + void sve_cpy(FloatRegister dst, SIMD_RegVariant T, PRegister pg, int imm8, + bool isMerge); #endif // CPU_AARCH64_C2_MACROASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 0213872852b..e8681611234 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -55,6 +55,9 @@ protected: static int _max_supported_sve_vector_length; static bool _rop_protection; static uintptr_t _pac_mask; + // When _prefer_sve_merging_mode_cpy is true, `cpy (imm, zeroing)` is + // implemented as `movi; cpy(imm, merging)`. + static constexpr bool _prefer_sve_merging_mode_cpy = true; static SpinWait _spin_wait; @@ -242,6 +245,8 @@ public: static bool use_rop_protection() { return _rop_protection; } + static bool prefer_sve_merging_mode_cpy() { return _prefer_sve_merging_mode_cpy; } + // For common 64/128-bit unpredicated vector operations, we may prefer // emitting NEON instructions rather than the corresponding SVE instructions. static bool use_neon_for_vector(int vector_length_in_bytes) { From fc77e3e9a2bedd3cefc98ca28516eb85ceefe2e5 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 5 Mar 2026 11:12:33 +0000 Subject: [PATCH 175/636] 8378599: Refactor tests under test/jdk/java/net/httpclient/whitebox from TestNG to JUnit Reviewed-by: vyazici, syan --- .../httpclient/whitebox/AltSvcFrameTest.java | 36 +++--- .../whitebox/AltSvcRegistryTest.java | 21 +-- .../AuthenticationFilterTestDriver.java | 4 +- .../whitebox/DefaultProxyDriver.java | 4 +- .../httpclient/whitebox/DemandTestDriver.java | 4 +- .../httpclient/whitebox/FlowTestDriver.java | 4 +- .../whitebox/FramesDecoderTestDriver.java | 4 +- .../whitebox/Http1HeaderParserTestDriver.java | 4 +- .../whitebox/MinimalFutureTestDriver.java | 4 +- .../whitebox/RawChannelTestDriver.java | 6 +- .../whitebox/SSLEchoTubeTestDriver.java | 4 +- .../whitebox/SSLFlowDelegateTestDriver.java | 4 +- .../whitebox/SSLTubeTestDriver.java | 4 +- .../whitebox/SelectorTestDriver.java | 4 +- .../whitebox/WindowControllerTestDriver.java | 4 +- .../whitebox/WrapperTestDriver.java | 4 +- .../net/http/AbstractSSLTubeTest.java | 6 +- .../net/http/AuthenticationFilterTest.java | 61 ++++----- .../jdk/internal/net/http/DefaultProxy.java | 33 ++--- .../jdk/internal/net/http/FlowTest.java | 16 +-- .../net/http/Http1HeaderParserTest.java | 47 +++---- .../jdk/internal/net/http/RawChannelTest.java | 11 +- .../internal/net/http/SSLEchoTubeTest.java | 6 +- .../net/http/SSLFlowDelegateTest.java | 75 +++++------ .../jdk/internal/net/http/SSLTubeTest.java | 14 +- .../jdk/internal/net/http/SelectorTest.java | 6 +- .../net/http/WindowControllerTest.java | 121 +++++++++--------- .../jdk/internal/net/http/WrapperTest.java | 7 +- .../internal/net/http/common/DemandTest.java | 63 +++++---- .../net/http/common/MinimalFutureTest.java | 15 +-- .../net/http/frame/FramesDecoderTest.java | 32 ++--- 31 files changed, 324 insertions(+), 304 deletions(-) diff --git a/test/jdk/java/net/httpclient/whitebox/AltSvcFrameTest.java b/test/jdk/java/net/httpclient/whitebox/AltSvcFrameTest.java index 589e5409044..b9e5f2d58fe 100644 --- a/test/jdk/java/net/httpclient/whitebox/AltSvcFrameTest.java +++ b/test/jdk/java/net/httpclient/whitebox/AltSvcFrameTest.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 @@ -48,17 +48,19 @@ import jdk.internal.net.http.HttpClientAccess; import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.frame.AltSvcFrame; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.net.http.HttpResponse.BodyHandlers.ofString; import static jdk.internal.net.http.AltServicesRegistry.AltService; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test - * @summary This test verifies alt-svc registry updation for frames + * @summary This test verifies alt-svc registry updating for frames * @library /test/lib /test/jdk/java/net/httpclient/lib * @build java.net.http/jdk.internal.net.http.HttpClientAccess * jdk.httpclient.test.lib.http2.Http2TestServer @@ -82,7 +84,7 @@ import static org.testng.Assert.assertTrue; * java.base/sun.net.www.http * java.base/sun.net.www * java.base/sun.net - * @run testng/othervm + * @run junit/othervm * -Dtest.requiresHost=true * -Djdk.httpclient.HttpClient.log=headers * -Djdk.internal.httpclient.disableHostnameVerification @@ -90,7 +92,6 @@ import static org.testng.Assert.assertTrue; * AltSvcFrameTest */ - public class AltSvcFrameTest { private static final String IGNORED_HOST = "www.should-be-ignored.com"; @@ -109,18 +110,23 @@ public class AltSvcFrameTest { static HttpClient client; private static final SSLContext server = SimpleSSLContext.findSSLContext(); - @BeforeTest - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { getRegistry(); https2Server = new Http2TestServer("localhost", true, server); https2Server.addHandler(new AltSvcFrameTestHandler(), "/"); https2Server.setExchangeSupplier(AltSvcFrameTest.CFTHttp2TestExchange::new); https2Server.start(); https2URI = "https://" + https2Server.serverAuthority() + "/"; - - } + @AfterAll + public static void tearDown() { + if (client != null) client.close(); + if (https2Server != null) https2Server.stop(); + } + + static AltServicesRegistry getRegistry() { client = HttpClient.newBuilder() .sslContext(server) @@ -139,7 +145,7 @@ public class AltSvcFrameTest { .GET() .build(); HttpResponse response = client.send(request, ofString()); - assertEquals(response.statusCode(), 200, "unexpected response code"); + assertEquals(200, response.statusCode(), "unexpected response code"); final List services = registry.lookup(URI.create(https2URI), "h3").toList(); System.out.println("Alt services in registry for " + https2URI + " = " + services); final boolean hasExpectedAltSvc = services.stream().anyMatch( @@ -158,7 +164,7 @@ public class AltSvcFrameTest { .GET() .build(); HttpResponse response = client.send(request, ofString()); - assertEquals(response.statusCode(), 200, "unexpected response code"); + assertEquals(200, response.statusCode(), "unexpected response code"); final List services = registry.lookup( URI.create(FOO_BAR_ORIGIN), "h3").toList(); System.out.println("Alt services in registry for " + FOO_BAR_ORIGIN + " = " + services); diff --git a/test/jdk/java/net/httpclient/whitebox/AltSvcRegistryTest.java b/test/jdk/java/net/httpclient/whitebox/AltSvcRegistryTest.java index b853f715415..682082ba9f8 100644 --- a/test/jdk/java/net/httpclient/whitebox/AltSvcRegistryTest.java +++ b/test/jdk/java/net/httpclient/whitebox/AltSvcRegistryTest.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 @@ -26,8 +26,6 @@ import jdk.internal.net.http.AltServicesRegistry; import jdk.test.lib.net.SimpleSSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -46,6 +44,10 @@ import java.util.concurrent.Executors; import static jdk.internal.net.http.AltServicesRegistry.AltService; import static java.net.http.HttpResponse.BodyHandlers.ofString; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + /* * @test * @summary This test verifies alt-svc registry updates @@ -73,7 +75,7 @@ import static java.net.http.HttpResponse.BodyHandlers.ofString; * java.base/sun.net.www * java.base/sun.net * java.base/jdk.internal.util - * @run testng/othervm + * @run junit/othervm * -Dtest.requiresHost=true * -Djdk.httpclient.HttpClient.log=headers * -Djdk.internal.httpclient.disableHostnameVerification @@ -81,7 +83,6 @@ import static java.net.http.HttpResponse.BodyHandlers.ofString; * AltSvcRegistryTest */ - public class AltSvcRegistryTest implements HttpServerAdapters { static HttpTestServer https2Server; @@ -89,8 +90,8 @@ public class AltSvcRegistryTest implements HttpServerAdapters { static HttpClient client; private static final SSLContext server = SimpleSSLContext.findSSLContext(); - @BeforeTest - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { getRegistry(); final ExecutorService executor = Executors.newCachedThreadPool(); https2Server = HttpServerAdapters.HttpTestServer.of( @@ -98,8 +99,12 @@ public class AltSvcRegistryTest implements HttpServerAdapters { https2Server.addHandler(new AltSvcRegistryTestHandler("https", https2Server), "/"); https2Server.start(); https2URI = "https://" + https2Server.serverAuthority() + "/"; + } - + @AfterAll + public static void tearDown() { + if (client != null) client.close(); + if (https2Server != null) https2Server.stop(); } static AltServicesRegistry getRegistry() { diff --git a/test/jdk/java/net/httpclient/whitebox/AuthenticationFilterTestDriver.java b/test/jdk/java/net/httpclient/whitebox/AuthenticationFilterTestDriver.java index e932455e409..5808c380593 100644 --- a/test/jdk/java/net/httpclient/whitebox/AuthenticationFilterTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/AuthenticationFilterTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,5 +24,5 @@ /* * @test * @modules java.net.http/jdk.internal.net.http - * @run testng java.net.http/jdk.internal.net.http.AuthenticationFilterTest + * @run junit java.net.http/jdk.internal.net.http.AuthenticationFilterTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/DefaultProxyDriver.java b/test/jdk/java/net/httpclient/whitebox/DefaultProxyDriver.java index 5c18fd84a0f..54127855386 100644 --- a/test/jdk/java/net/httpclient/whitebox/DefaultProxyDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/DefaultProxyDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,7 +27,7 @@ * @summary Verifies that the HTTP Client, by default, uses the system-wide * proxy selector, and that that selector supports the standard HTTP proxy * system properties. - * @run testng/othervm + * @run junit/othervm * -Dhttp.proxyHost=foo.proxy.com * -Dhttp.proxyPort=9876 * -Dhttp.nonProxyHosts=*.direct.com diff --git a/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java b/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java index ea442878261..c8b793a289c 100644 --- a/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -24,5 +24,5 @@ /* * @test * @modules java.net.http/jdk.internal.net.http.common - * @run testng java.net.http/jdk.internal.net.http.common.DemandTest + * @run junit java.net.http/jdk.internal.net.http.common.DemandTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java b/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java index c24a733db3b..4468b5d6eb6 100644 --- a/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,5 +25,5 @@ * @test * @compile/module=java.net.http ../../../../../../lib/jdk/test/lib/net/SimpleSSLContext.java * @modules java.net.http/jdk.internal.net.http - * @run testng/timeout=480 java.net.http/jdk.internal.net.http.FlowTest + * @run junit/timeout=480 java.net.http/jdk.internal.net.http.FlowTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/FramesDecoderTestDriver.java b/test/jdk/java/net/httpclient/whitebox/FramesDecoderTestDriver.java index 9b3c8878669..7edc555d8a6 100644 --- a/test/jdk/java/net/httpclient/whitebox/FramesDecoderTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/FramesDecoderTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -25,7 +25,7 @@ * @test * @bug 8195823 * @modules java.net.http/jdk.internal.net.http.frame - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * java.net.http/jdk.internal.net.http.frame.FramesDecoderTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java b/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java index b18142601a5..f3b9d410b51 100644 --- a/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,5 +25,5 @@ * @test * @bug 8195138 * @modules java.net.http/jdk.internal.net.http - * @run testng java.net.http/jdk.internal.net.http.Http1HeaderParserTest + * @run junit java.net.http/jdk.internal.net.http.Http1HeaderParserTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/MinimalFutureTestDriver.java b/test/jdk/java/net/httpclient/whitebox/MinimalFutureTestDriver.java index 316ca29e2a6..90fe6816adc 100644 --- a/test/jdk/java/net/httpclient/whitebox/MinimalFutureTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/MinimalFutureTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,5 +24,5 @@ /* * @test * @modules java.net.http/jdk.internal.net.http.common - * @run testng java.net.http/jdk.internal.net.http.common.MinimalFutureTest + * @run junit java.net.http/jdk.internal.net.http.common.MinimalFutureTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/RawChannelTestDriver.java b/test/jdk/java/net/httpclient/whitebox/RawChannelTestDriver.java index ab44fba5ecd..7ca448b7892 100644 --- a/test/jdk/java/net/httpclient/whitebox/RawChannelTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/RawChannelTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,10 +25,10 @@ * @test * @bug 8151299 8164704 * @modules java.net.http/jdk.internal.net.http - * @run testng/othervm java.net.http/jdk.internal.net.http.RawChannelTest + * @run junit/othervm java.net.http/jdk.internal.net.http.RawChannelTest */ // use -// @run testng/othervm -Dseed=6434511950803022575 +// @run junit/othervm -Dseed=6434511950803022575 // java.net.http/jdk.internal.net.http.RawChannelTest // to reproduce a failure with a particular seed (e.g. 6434511950803022575) // if this test is observed failing with that seed diff --git a/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java index d641513b298..b4b3e59326d 100644 --- a/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,7 +25,7 @@ * @test * @compile/module=java.net.http ../../../../../../lib/jdk/test/lib/net/SimpleSSLContext.java * @modules java.net.http/jdk.internal.net.http - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * java.net.http/jdk.internal.net.http.SSLEchoTubeTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/SSLFlowDelegateTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SSLFlowDelegateTestDriver.java index a49f53a844a..cb8e9801ca8 100644 --- a/test/jdk/java/net/httpclient/whitebox/SSLFlowDelegateTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/SSLFlowDelegateTestDriver.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,7 +28,7 @@ * downReader doesn't request any * @compile/module=java.net.http ../../../../../../lib/jdk/test/lib/net/SimpleSSLContext.java * @modules java.net.http/jdk.internal.net.http - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djavax.net.debug=ssl:handshake * java.net.http/jdk.internal.net.http.SSLFlowDelegateTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java index b1994777b14..2c73de26648 100644 --- a/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,7 +25,7 @@ * @test * @compile/module=java.net.http ../../../../../../lib/jdk/test/lib/net/SimpleSSLContext.java * @modules java.net.http/jdk.internal.net.http - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * java.net.http/jdk.internal.net.http.SSLTubeTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/SelectorTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SelectorTestDriver.java index 226e644e903..54d66d04cff 100644 --- a/test/jdk/java/net/httpclient/whitebox/SelectorTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/SelectorTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,5 +25,5 @@ * @test * @bug 8151299 8164704 * @modules java.net.http/jdk.internal.net.http - * @run testng java.net.http/jdk.internal.net.http.SelectorTest + * @run junit java.net.http/jdk.internal.net.http.SelectorTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/WindowControllerTestDriver.java b/test/jdk/java/net/httpclient/whitebox/WindowControllerTestDriver.java index 7943bfceaa0..16ac4aaefb8 100644 --- a/test/jdk/java/net/httpclient/whitebox/WindowControllerTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/WindowControllerTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,5 +26,5 @@ * @bug 8207960 * @modules java.net.http/jdk.internal.net.http * @summary Non-negative WINDOW_UPDATE increments may leave the stream window size negative - * @run testng/othervm java.net.http/jdk.internal.net.http.WindowControllerTest + * @run junit/othervm java.net.http/jdk.internal.net.http.WindowControllerTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java b/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java index 7ea2b07e3a7..dfc808ff68b 100644 --- a/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java +++ b/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -24,5 +24,5 @@ /* * @test * @modules java.net.http/jdk.internal.net.http - * @run testng java.net.http/jdk.internal.net.http.WrapperTest + * @run junit java.net.http/jdk.internal.net.http.WrapperTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AbstractSSLTubeTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AbstractSSLTubeTest.java index 944f8ae0872..699b537c980 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AbstractSSLTubeTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AbstractSSLTubeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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,17 +26,13 @@ package jdk.internal.net.http; import jdk.internal.net.http.common.FlowTube; import jdk.internal.net.http.common.SSLTube; import jdk.internal.net.http.common.Utils; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; import java.util.List; -import java.util.StringTokenizer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java index 415caeb1196..2e9672e4725 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,9 +24,6 @@ package jdk.internal.net.http; import jdk.internal.net.http.common.HttpHeadersBuilder; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.annotations.AfterClass; import java.lang.ref.Reference; import java.net.Authenticator; @@ -55,12 +52,15 @@ import static java.util.stream.Collectors.joining; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; public class AuthenticationFilterTest { - @DataProvider(name = "uris") - public Object[][] responses() { + public static Object[][] responses() { return new Object[][] { { "http://foo.com", HTTP_1_1, null }, { "http://foo.com", HTTP_2, null }, @@ -135,7 +135,8 @@ public class AuthenticationFilterTest { return s == null || s.isEmpty(); } - @Test(dataProvider = "uris") + @ParameterizedTest + @MethodSource("responses") public void testAuthentication(String uri, Version v, String proxy) throws Exception { String test = format("testAuthentication: {\"%s\", %s, \"%s\"}", uri, v, proxy); try { @@ -146,8 +147,8 @@ public class AuthenticationFilterTest { } } - @AfterClass - public void printDiagnostic() { + @AfterAll + public static void printDiagnostic() { if (FAILED.isEmpty()) { out.println("All tests passed"); return; @@ -183,7 +184,7 @@ public class AuthenticationFilterTest { HttpClientImpl client = facade.impl; AuthenticationFilter filter = new AuthenticationFilter(); - assertEquals(authenticator.COUNTER.get(), 0); + assertEquals(0, authenticator.COUNTER.get()); // Creates the first HttpRequestImpl, and call filter.request() with // it. The expectation is that the filter will not add any credentials, @@ -202,7 +203,7 @@ public class AuthenticationFilterTest { HttpHeaders hdrs = req.getSystemHeadersBuilder().build(); assertFalse(hdrs.firstValue(authorization(true)).isPresent()); assertFalse(hdrs.firstValue(authorization(false)).isPresent()); - assertEquals(authenticator.COUNTER.get(), 0); + assertEquals(0, authenticator.COUNTER.get()); // Creates the Response to the first request, and call filter.response // with it. That response has a 401 or 407 status code. @@ -221,9 +222,9 @@ public class AuthenticationFilterTest { out.println("Checking filter's response to " + unauthorized + " from " + uri); - assertTrue(next != null, "next should not be null"); + assertNotNull(next, "next should not be null"); String[] up = check(reqURI, next.getSystemHeadersBuilder().build(), proxy); - assertEquals(authenticator.COUNTER.get(), 1); + assertEquals(1, authenticator.COUNTER.get()); // Now simulate a new successful exchange to get the credentials in the cache // We first call filter.request with the request that was previously @@ -241,8 +242,8 @@ public class AuthenticationFilterTest { HttpHeaders h = HttpHeaders.of(Collections.emptyMap(), ACCEPT_ALL); response = new Response(next, exchange,h, null, 200, v); next = filter.response(response); - assertTrue(next == null, "next should be null"); - assertEquals(authenticator.COUNTER.get(), 1); + assertNull(next, "next should be null"); + assertEquals(1, authenticator.COUNTER.get()); // Now verify that the cache is used for the next request to the same server. // We're going to create a request to the same server by appending "/bar" to @@ -270,7 +271,7 @@ public class AuthenticationFilterTest { + " with proxy " + req2.proxy()); String[] up2 = check(reqURI, req2.getSystemHeadersBuilder().build(), proxy); assertTrue(Arrays.deepEquals(up, up2), format("%s:%s != %s:%s", up2[0], up2[1], up[0], up[1])); - assertEquals(authenticator.COUNTER.get(), 1); + assertEquals(1, authenticator.COUNTER.get()); // Now verify that the cache is not used if we send a request to a different server. // We're going to append ".bar" to the original request host name, and feed that @@ -316,7 +317,7 @@ public class AuthenticationFilterTest { java.util.stream.Stream.of(getAuthorization(h3, false)) .collect(joining(":"))); assertFalse(h3.firstValue(authorization(false)).isPresent()); - assertEquals(authenticator.COUNTER.get(), 1); + assertEquals(1, authenticator.COUNTER.get()); // Now we will verify that credentials for proxies are not used for servers and // conversely. @@ -365,7 +366,7 @@ public class AuthenticationFilterTest { String[] up4 = check(reqURI, h4, proxy); assertTrue(Arrays.deepEquals(up, up4), format("%s:%s != %s:%s", up4[0], up4[1], up[0], up[1])); } - assertEquals(authenticator.COUNTER.get(), 1); + assertEquals(1, authenticator.COUNTER.get()); if (proxy != null) { // Now if we were using a proxy, we're going to send the same request than @@ -380,7 +381,7 @@ public class AuthenticationFilterTest { MultiExchange multi5 = new MultiExchange(origReq5, req5, client, HttpResponse.BodyHandlers.replacing(null), null); out.println("Simulating new request to " + reqURI + " with a proxy " + req5.proxy()); - assertTrue(req5.proxy() == null, "req5.proxy() should be null"); + assertNull(req5.proxy(), "req5.proxy() should be null"); Exchange exchange5 = new Exchange<>(req5, multi5); filter.request(req5, multi5); out.println("Check that filter has not added server credentials from cache for " @@ -398,7 +399,7 @@ public class AuthenticationFilterTest { java.util.stream.Stream.of(getAuthorization(h5, true)) .collect(joining(":"))); assertFalse(h5.firstValue(authorization(true)).isPresent()); - assertEquals(authenticator.COUNTER.get(), 1); + assertEquals(1, authenticator.COUNTER.get()); // Now simulate a 401 response from the server HttpHeadersBuilder headers5Builder = new HttpHeadersBuilder(); @@ -410,11 +411,11 @@ public class AuthenticationFilterTest { out.println("Simulating " + unauthorized + " response from " + uri); HttpRequestImpl next5 = filter.response(response5); - assertEquals(authenticator.COUNTER.get(), 2); + assertEquals(2, authenticator.COUNTER.get()); out.println("Checking filter's response to " + unauthorized + " from " + uri); - assertTrue(next5 != null, "next5 should not be null"); + assertNotNull(next5, "next5 should not be null"); String[] up5 = check(reqURI, next5.getSystemHeadersBuilder().build(), null); // now simulate a 200 response from the server @@ -423,7 +424,7 @@ public class AuthenticationFilterTest { h = HttpHeaders.of(Map.of(), ACCEPT_ALL); response5 = new Response(next5, exchange5, h, null, 200, v); filter.response(response5); - assertEquals(authenticator.COUNTER.get(), 2); + assertEquals(2, authenticator.COUNTER.get()); // now send the request again, with proxy this time, and it should have both // server auth and proxy auth @@ -433,7 +434,7 @@ public class AuthenticationFilterTest { MultiExchange multi6 = new MultiExchange(origReq6, req6, client, HttpResponse.BodyHandlers.replacing(null), null); out.println("Simulating new request to " + reqURI + " with a proxy " + req6.proxy()); - assertTrue(req6.proxy() != null, "req6.proxy() should not be null"); + assertNotNull(req6.proxy(), "req6.proxy() should not be null"); Exchange exchange6 = new Exchange<>(req6, multi6); filter.request(req6, multi6); out.println("Check that filter has added server credentials from cache for " @@ -444,7 +445,7 @@ public class AuthenticationFilterTest { + reqURI + " (proxy: " + req6.proxy() + ")"); String[] up6 = check(reqURI, h6, proxy); assertTrue(Arrays.deepEquals(up, up6), format("%s:%s != %s:%s", up6[0], up6[1], up[0], up[1])); - assertEquals(authenticator.COUNTER.get(), 2); + assertEquals(2, authenticator.COUNTER.get()); } if (proxy == null && uri.contains("x/y/z")) { @@ -456,7 +457,7 @@ public class AuthenticationFilterTest { MultiExchange multi7 = new MultiExchange(origReq7, req7, client, HttpResponse.BodyHandlers.replacing(null), null); out.println("Simulating new request to " + reqURI7 + " with a proxy " + req7.proxy()); - assertTrue(req7.proxy() == null, "req7.proxy() should be null"); + assertNull(req7.proxy(), "req7.proxy() should be null"); Exchange exchange7 = new Exchange<>(req7, multi7); filter.request(req7, multi7); out.println("Check that filter has not added server credentials from cache for " @@ -475,7 +476,7 @@ public class AuthenticationFilterTest { java.util.stream.Stream.of(getAuthorization(h7, true)) .collect(joining(":"))); assertFalse(h7.firstValue(authorization(true)).isPresent()); - assertEquals(authenticator.COUNTER.get(), 1); + assertEquals(1, authenticator.COUNTER.get()); } @@ -516,7 +517,7 @@ public class AuthenticationFilterTest { out.println("user:password: " + u + ":" + p); String protocol = proxy != null ? "http" : reqURI.getScheme(); String expectedUser = "u." + protocol; - assertEquals(u, expectedUser); + assertEquals(expectedUser, u); String host = proxy == null ? reqURI.getHost() : proxy.substring(0, proxy.lastIndexOf(':')); int port = proxy == null ? reqURI.getPort() @@ -524,7 +525,7 @@ public class AuthenticationFilterTest { String expectedPw = concat(requestorType(proxy!=null), "basic", protocol, host, port, "earth", reqURI.toURL()); - assertEquals(p, expectedPw); + assertEquals(expectedPw, p); return new String[] {u, p}; } diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/DefaultProxy.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/DefaultProxy.java index 96d08ae2c34..819b02e7fc7 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/DefaultProxy.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/DefaultProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -29,8 +29,9 @@ import java.net.ProxySelector; import java.net.URI; import java.net.http.HttpClient; import java.util.List; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DefaultProxy { @@ -42,7 +43,7 @@ public class DefaultProxy { public void testDefault() { ProxySelector ps = getProxySelector(); System.out.println("HttpClientImpl proxySelector:" + ps); - assertEquals(ps, ProxySelector.getDefault()); + assertEquals(ProxySelector.getDefault(), ps); } // From the test driver @@ -56,21 +57,21 @@ public class DefaultProxy { URI uri = URI.create("http://foo.com/example.html"); List plist = ps.select(uri); System.out.println("proxy list for " + uri + " : " + plist); - assertEquals(plist.size(), 1); + assertEquals(1, plist.size()); Proxy proxy = plist.get(0); - assertEquals(proxy.type(), Proxy.Type.HTTP); + assertEquals(Proxy.Type.HTTP, proxy.type()); InetSocketAddress expectedAddr = InetSocketAddress.createUnresolved("foo.proxy.com", 9876); - assertEquals(proxy.address(), expectedAddr); + assertEquals(expectedAddr, proxy.address()); // nonProxyHosts uri = URI.create("http://foo.direct.com/example.html"); plist = ps.select(uri); System.out.println("proxy list for " + uri + " : " + plist); - assertEquals(plist.size(), 1); + assertEquals(1, plist.size()); proxy = plist.get(0); - assertEquals(proxy.type(), Proxy.Type.DIRECT); - assertEquals(proxy.address(), null); + assertEquals(Proxy.Type.DIRECT, proxy.type()); + assertEquals(null, proxy.address()); } // From the test driver @@ -83,21 +84,21 @@ public class DefaultProxy { URI uri = URI.create("https://foo.com/example.html"); List plist = ps.select(uri); System.out.println("proxy list for " + uri + " : " + plist); - assertEquals(plist.size(), 1); + assertEquals(1, plist.size()); Proxy proxy = plist.get(0); - assertEquals(proxy.type(), Proxy.Type.HTTP); + assertEquals(Proxy.Type.HTTP, proxy.type()); InetSocketAddress expectedAddr = InetSocketAddress.createUnresolved("secure.proxy.com", 5443); - assertEquals(proxy.address(), expectedAddr); + assertEquals(expectedAddr, proxy.address()); // nonProxyHosts uri = URI.create("https://foo.direct.com/example.html"); plist = ps.select(uri); System.out.println("proxy list for " + uri + " : " + plist); - assertEquals(plist.size(), 1); + assertEquals(1, plist.size()); proxy = plist.get(0); - assertEquals(proxy.type(), Proxy.Type.DIRECT); - assertEquals(proxy.address(), null); + assertEquals(Proxy.Type.DIRECT, proxy.type()); + assertEquals(null, proxy.address()); } } diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/FlowTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/FlowTest.java index 3e9e967c061..cd05196b653 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/FlowTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/FlowTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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,8 +32,6 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.util.List; -import java.util.Random; -import java.util.StringTokenizer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -47,10 +45,10 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.net.ssl.*; import jdk.internal.net.http.common.Utils; -import org.testng.annotations.Test; import jdk.internal.net.http.common.SSLFlowDelegate; -@Test +import org.junit.jupiter.api.Test; + public class FlowTest extends AbstractRandomTest { private final SubmissionPublisher> srcPublisher; @@ -241,7 +239,7 @@ public class FlowTest extends AbstractRandomTest { private void clientReader() { try { InputStream is = clientSock.getInputStream(); - final int bufsize = FlowTest.randomRange(512, 16 * 1024); + final int bufsize = AbstractRandomTest.randomRange(512, 16 * 1024); println("clientReader: bufsize = " + bufsize); while (true) { byte[] buf = new byte[bufsize]; @@ -315,8 +313,8 @@ public class FlowTest extends AbstractRandomTest { private final AtomicInteger loopCount = new AtomicInteger(); public String monitor() { - return "serverLoopback: loopcount = " + loopCount.toString() - + " clientRead: count = " + readCount.toString(); + return "serverLoopback: loopcount = " + loopCount.get() + + " clientRead: count = " + readCount.get(); } // thread2 @@ -324,7 +322,7 @@ public class FlowTest extends AbstractRandomTest { try { InputStream is = serverSock.getInputStream(); OutputStream os = serverSock.getOutputStream(); - final int bufsize = FlowTest.randomRange(512, 16 * 1024); + final int bufsize = AbstractRandomTest.randomRange(512, 16 * 1024); println("serverLoopback: bufsize = " + bufsize); byte[] bb = new byte[bufsize]; while (true) { diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java index 9bde9109095..a4c7b76aba8 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -36,22 +36,22 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; import sun.net.www.MessageHeader; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; import static java.lang.System.out; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.util.stream.Collectors.toList; -import static org.testng.Assert.*; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; // Mostly verifies the "new" Http1HeaderParser returns the same results as the // tried and tested sun.net.www.MessageHeader. public class Http1HeaderParserTest { - @DataProvider(name = "responses") - public Object[][] responses() { + public static Object[][] responses() { List responses = new ArrayList<>(); String[] basic = @@ -316,7 +316,8 @@ public class Http1HeaderParserTest { } - @Test(dataProvider = "responses") + @ParameterizedTest + @MethodSource("responses") public void verifyHeaders(String respString) throws Exception { System.out.println("\ntesting:\n\t" + respString .replace("\r\n", "") @@ -339,7 +340,7 @@ public class Http1HeaderParserTest { String statusLine1 = messageHeaderMap.get(null).get(0); String statusLine2 = decoder.statusLine(); if (statusLine1.startsWith("HTTP")) {// skip the case where MH's messes up the status-line - assertEquals(statusLine2, statusLine1, "Status-line not equal"); + assertEquals(statusLine1, statusLine2, "Status-line not equal"); } else { assertTrue(statusLine2.startsWith("HTTP/1."), "Status-line not HTTP/1."); } @@ -356,7 +357,7 @@ public class Http1HeaderParserTest { assertHeadersEqual(messageHeaderMap, decoderMap1, "messageHeaderMap not equal to decoderMap1"); - assertEquals(availableBytes, b.remaining(), + assertEquals(b.remaining(), availableBytes, String.format("stream available (%d) not equal to remaining (%d)", availableBytes, b.remaining())); // byte at a time @@ -366,14 +367,13 @@ public class Http1HeaderParserTest { .collect(toList()); while (decoder.parse(buffers.remove(0)) != true); Map> decoderMap2 = decoder.headers().map(); - assertEquals(availableBytes, buffers.size(), + assertEquals(buffers.size(), availableBytes, "stream available not equals to remaining buffers"); - assertEquals(decoderMap1, decoderMap2, "decoder maps not equal"); + assertEquals(decoderMap2, decoderMap1, "decoder maps not equal"); } - @DataProvider(name = "errors") - public Object[][] errors() { + public static Object[][] errors() { List responses = new ArrayList<>(); // These responses are parsed, somewhat, by MessageHeaders but give @@ -451,12 +451,15 @@ public class Http1HeaderParserTest { return responses.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); } - @Test(dataProvider = "errors", expectedExceptions = ProtocolException.class) + @ParameterizedTest + @MethodSource("errors") public void errors(String respString) throws ProtocolException { - byte[] bytes = respString.getBytes(US_ASCII); - Http1HeaderParser decoder = new Http1HeaderParser(); - ByteBuffer b = ByteBuffer.wrap(bytes); - decoder.parse(b); + assertThrows(ProtocolException.class, () -> { + byte[] bytes = respString.getBytes(US_ASCII); + Http1HeaderParser decoder = new Http1HeaderParser(); + ByteBuffer b = ByteBuffer.wrap(bytes); + decoder.parse(b); + }); } void assertHeadersEqual(Map> expected, @@ -466,7 +469,7 @@ public class Http1HeaderParserTest { if (expected.equals(actual)) return; - assertEquals(expected.size(), actual.size(), + assertEquals(actual.size(), expected.size(), format("%s. Expected size %d, actual size %s. %nexpected= %s,%n actual=%s.", msg, expected.size(), actual.size(), mapToString(expected), mapToString(actual))); @@ -479,7 +482,7 @@ public class Http1HeaderParserTest { if (key.equalsIgnoreCase(other.getKey())) { found = true; List otherValues = other.getValue(); - assertEquals(values.size(), otherValues.size(), + assertEquals(otherValues.size(), values.size(), format("%s. Expected list size %d, actual size %s", msg, values.size(), otherValues.size())); if (!(values.containsAll(otherValues) && otherValues.containsAll(values))) @@ -508,11 +511,11 @@ public class Http1HeaderParserTest { public static void main(String... args) throws Exception { Http1HeaderParserTest test = new Http1HeaderParserTest(); int count = 0; - for (Object[] objs : test.responses()) { + for (Object[] objs : Http1HeaderParserTest.responses()) { out.println("Testing " + count++ + ", " + objs[0]); test.verifyHeaders((String) objs[0]); } - for (Object[] objs : test.errors()) { + for (Object[] objs : Http1HeaderParserTest.errors()) { out.println("Testing " + count++ + ", " + objs[0]); try { test.errors((String) objs[0]); diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/RawChannelTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/RawChannelTest.java index f6bcdcb4d33..62ca237d2f1 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/RawChannelTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/RawChannelTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -47,10 +47,11 @@ import java.net.http.HttpResponse; import java.util.concurrent.atomic.AtomicReference; import jdk.internal.net.http.websocket.RawChannel; -import org.testng.annotations.Test; import static java.net.http.HttpResponse.BodyHandlers.discarding; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; /* * This test exercises mechanics of _independent_ reads and writes on the @@ -222,8 +223,8 @@ public class RawChannelTest { closeChannel(chan); }); exit.await(); // All done, we need to compare results: - assertEquals(clientRead.get(), serverWritten.get()); - assertEquals(serverRead.get(), clientWritten.get()); + assertEquals(serverWritten.get(), clientRead.get()); + assertEquals(clientWritten.get(), serverRead.get()); Throwable serverError = testServer.failed.get(); if (serverError != null) { throw new AssertionError("TestServer failed: " diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java index 2884b74eee5..4316a46a25c 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLEchoTubeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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,7 +28,6 @@ import jdk.internal.net.http.common.FlowTube; import jdk.internal.net.http.common.SSLTube; import jdk.internal.net.http.common.SequentialScheduler; import jdk.internal.net.http.common.Utils; -import org.testng.annotations.Test; import java.io.IOException; import java.nio.ByteBuffer; @@ -44,7 +43,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -@Test +import org.junit.jupiter.api.Test; + public class SSLEchoTubeTest extends AbstractSSLTubeTest { @Test diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLFlowDelegateTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLFlowDelegateTest.java index 6b031bdfa58..32511de173b 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLFlowDelegateTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLFlowDelegateTest.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 @@ -58,10 +58,11 @@ import jdk.internal.net.http.common.Logger; import jdk.internal.net.http.common.SSLFlowDelegate; import jdk.internal.net.http.common.SubscriberWrapper; import jdk.internal.net.http.common.Utils; -import org.testng.Assert; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; // jtreg test definition for this test resides in SSLFlowDelegateTestDriver.java public class SSLFlowDelegateTest { @@ -70,38 +71,38 @@ public class SSLFlowDelegateTest { private static final Random random = new Random(); private static final byte DATA_BYTE = (byte) random.nextInt(); - private ExecutorService executor; - private SSLParameters sslParams; - private SSLServerSocket sslServerSocket; - private SSLEngine clientEngine; - private CompletableFuture testCompletion; + private static ExecutorService executor; + private static SSLParameters sslParams; + private static SSLServerSocket sslServerSocket; + private static SSLEngine clientEngine; + private static CompletableFuture testCompletion; - @BeforeTest - public void beforeTest() throws Exception { - this.executor = Executors.newCachedThreadPool(); - this.testCompletion = new CompletableFuture<>(); + @BeforeAll + public static void beforeTest() throws Exception { + executor = Executors.newCachedThreadPool(); + testCompletion = new CompletableFuture<>(); final SSLParameters sp = new SSLParameters(); sp.setApplicationProtocols(new String[]{ALPN}); - this.sslParams = sp; + sslParams = sp; var sslContext = SimpleSSLContextWhiteboxAdapter.findSSLContext(); - this.sslServerSocket = startServer(sslContext); - println(debugTag, "Server started at " + this.sslServerSocket.getInetAddress() + ":" - + this.sslServerSocket.getLocalPort()); + sslServerSocket = startServer(sslContext); + println(debugTag, "Server started at " + sslServerSocket.getInetAddress() + ":" + + sslServerSocket.getLocalPort()); - this.clientEngine = createClientEngine(sslContext); + clientEngine = createClientEngine(sslContext); } - @AfterTest - public void afterTest() throws Exception { - if (this.sslServerSocket != null) { - println(debugTag, "Closing server socket " + this.sslServerSocket); - this.sslServerSocket.close(); + @AfterAll + public static void afterTest() throws Exception { + if (sslServerSocket != null) { + println(debugTag, "Closing server socket " + sslServerSocket); + sslServerSocket.close(); } - if (this.executor != null) { - println(debugTag, "Shutting down the executor " + this.executor); - this.executor.shutdownNow(); + if (executor != null) { + println(debugTag, "Shutting down the executor " + executor); + executor.shutdownNow(); } } @@ -117,30 +118,30 @@ public class SSLFlowDelegateTest { } } - private SSLServerSocket createSSLServerSocket( + private static SSLServerSocket createSSLServerSocket( final SSLContext ctx, final InetSocketAddress bindAddr) throws IOException { final SSLServerSocketFactory fac = ctx.getServerSocketFactory(); final SSLServerSocket sslServerSocket = (SSLServerSocket) fac.createServerSocket(); sslServerSocket.setReuseAddress(false); - sslServerSocket.setSSLParameters(this.sslParams); + sslServerSocket.setSSLParameters(sslParams); sslServerSocket.bind(bindAddr); return sslServerSocket; } - private SSLServerSocket startServer(final SSLContext ctx) throws Exception { + private static SSLServerSocket startServer(final SSLContext ctx) throws Exception { final SSLServerSocket sslServerSocket = createSSLServerSocket(ctx, new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); final Runnable serverResponsePusher = new ServerResponsePusher(sslServerSocket, - this.testCompletion); + testCompletion); final Thread serverThread = new Thread(serverResponsePusher, "serverResponsePusher"); // start the thread which will accept() a socket connection and send data over it serverThread.start(); return sslServerSocket; } - private SSLEngine createClientEngine(final SSLContext ctx) { + private static SSLEngine createClientEngine(final SSLContext ctx) { final SSLEngine clientEngine = ctx.createSSLEngine(); - clientEngine.setSSLParameters(this.sslParams); + clientEngine.setSSLParameters(sslParams); clientEngine.setUseClientMode(true); return clientEngine; } @@ -170,7 +171,7 @@ public class SSLFlowDelegateTest { // in various places in this test. If the "testCompletion" completes before // the "soleExpectedAppData" completes (typically due to some exception), // then we complete the "soleExpectedAppData" too. - this.testCompletion.whenComplete((r, t) -> { + testCompletion.whenComplete((r, t) -> { if (soleExpectedAppData.isDone()) { return; } @@ -221,7 +222,7 @@ public class SSLFlowDelegateTest { println(debugTag, "Waiting for handshake to complete"); final String negotiatedALPN = sslFlowDelegate.alpn().join(); println(debugTag, "handshake completed, with negotiated ALPN: " + negotiatedALPN); - Assert.assertEquals(negotiatedALPN, ALPN, "unexpected ALPN negotiated"); + assertEquals(ALPN, negotiatedALPN, "unexpected ALPN negotiated"); try { // now wait for the initial (and the only) chunk of application data to be // received by the AppResponseReceiver @@ -254,7 +255,7 @@ public class SSLFlowDelegateTest { private void failTest(final CompletionException ce) { final Throwable cause = ce.getCause(); - Assert.fail(cause.getMessage() == null ? "test failed" : cause.getMessage(), cause); + fail(cause.getMessage() == null ? "test failed" : cause.getMessage(), cause); } // uses reflection to get hold of the SSLFlowDelegate.reader.outputQ member field, @@ -288,7 +289,7 @@ public class SSLFlowDelegateTest { } println(debugTag, "num unsolicited bytes so far = " + numUnsolicitated); } - Assert.assertEquals(numUnsolicitated, 0, + assertEquals(0, numUnsolicitated, "SSLFlowDelegate has accumulated " + numUnsolicitated + " unsolicited bytes"); } diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java index ac6a235b8e2..bb8d583e07b 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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,6 @@ package jdk.internal.net.http; import jdk.internal.net.http.common.FlowTube; import jdk.internal.net.http.common.SSLFlowDelegate; import jdk.internal.net.http.common.Utils; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; @@ -51,7 +50,8 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.SubmissionPublisher; import java.util.concurrent.atomic.AtomicInteger; -@Test +import org.junit.jupiter.api.Test; + public class SSLTubeTest extends AbstractSSLTubeTest { @Test @@ -125,7 +125,7 @@ public class SSLTubeTest extends AbstractSSLTubeTest { private void clientReader() { try { InputStream is = clientSock.getInputStream(); - final int bufsize = randomRange(512, 16 * 1024); + final int bufsize = AbstractRandomTest.randomRange(512, 16 * 1024); System.out.println("clientReader: bufsize = " + bufsize); while (true) { byte[] buf = new byte[bufsize]; @@ -137,7 +137,7 @@ public class SSLTubeTest extends AbstractSSLTubeTest { allBytesReceived.await(); System.out.println("clientReader: closing publisher"); publisher.close(); - sleep(2000); + AbstractSSLTubeTest.sleep(2000); Utils.close(is, clientSock); return; } @@ -206,13 +206,13 @@ public class SSLTubeTest extends AbstractSSLTubeTest { try { InputStream is = serverSock.getInputStream(); OutputStream os = serverSock.getOutputStream(); - final int bufsize = randomRange(512, 16 * 1024); + final int bufsize = AbstractRandomTest.randomRange(512, 16 * 1024); System.out.println("serverLoopback: bufsize = " + bufsize); byte[] bb = new byte[bufsize]; while (true) { int n = is.read(bb); if (n == -1) { - sleep(2000); + AbstractSSLTubeTest.sleep(2000); is.close(); os.close(); serverSock.close(); diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SelectorTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SelectorTest.java index 5b8da3b9979..3027df0436e 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SelectorTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SelectorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -31,20 +31,20 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.net.http.HttpClient; import java.net.http.HttpResponse; -import org.testng.annotations.Test; import jdk.internal.net.http.websocket.RawChannel; import static java.lang.System.out; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.util.concurrent.TimeUnit.SECONDS; import static java.net.http.HttpResponse.BodyHandlers.discarding; +import org.junit.jupiter.api.Test; + /** * Whitebox test of selector mechanics. Currently only a simple test * setting one read and one write event is done. It checks that the * write event occurs first, followed by the read event and then no * further events occur despite the conditions actually still existing. */ -@Test public class SelectorTest { AtomicInteger counter = new AtomicInteger(); diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WindowControllerTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WindowControllerTest.java index 0a94f471575..5df3b4227e6 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WindowControllerTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WindowControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,92 +23,93 @@ package jdk.internal.net.http; -import org.testng.annotations.Test; import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_INITIAL_WINDOW_SIZE; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class WindowControllerTest { @Test public void testConnectionWindowOverflow() { WindowController wc = new WindowController(); - assertEquals(wc.connectionWindowSize(), DEFAULT_INITIAL_WINDOW_SIZE); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.connectionWindowSize(), DEFAULT_INITIAL_WINDOW_SIZE); + assertEquals(DEFAULT_INITIAL_WINDOW_SIZE, wc.connectionWindowSize()); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(DEFAULT_INITIAL_WINDOW_SIZE, wc.connectionWindowSize()); wc.registerStream(1, DEFAULT_INITIAL_WINDOW_SIZE); wc.tryAcquire(DEFAULT_INITIAL_WINDOW_SIZE - 1, 1, null); - assertEquals(wc.connectionWindowSize(), 1); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.connectionWindowSize(), 1); + assertEquals(1, wc.connectionWindowSize()); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(1, wc.connectionWindowSize()); wc.increaseConnectionWindow(Integer.MAX_VALUE - 1 -1); - assertEquals(wc.connectionWindowSize(), Integer.MAX_VALUE - 1); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.connectionWindowSize(), Integer.MAX_VALUE - 1); + assertEquals(Integer.MAX_VALUE - 1, wc.connectionWindowSize()); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(Integer.MAX_VALUE - 1, wc.connectionWindowSize()); wc.increaseConnectionWindow(1); - assertEquals(wc.connectionWindowSize(), Integer.MAX_VALUE); - assertEquals(wc.increaseConnectionWindow(1), false); - assertEquals(wc.increaseConnectionWindow(100), false); - assertEquals(wc.increaseConnectionWindow(Integer.MAX_VALUE), false); - assertEquals(wc.connectionWindowSize(), Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, wc.connectionWindowSize()); + assertEquals(false, wc.increaseConnectionWindow(1)); + assertEquals(false, wc.increaseConnectionWindow(100)); + assertEquals(false, wc.increaseConnectionWindow(Integer.MAX_VALUE)); + assertEquals(Integer.MAX_VALUE, wc.connectionWindowSize()); } @Test public void testStreamWindowOverflow() { WindowController wc = new WindowController(); wc.registerStream(1, DEFAULT_INITIAL_WINDOW_SIZE); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 1), false); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 1), false); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 1), false); + assertEquals(false, wc.increaseStreamWindow(Integer.MAX_VALUE, 1)); + assertEquals(false, wc.increaseStreamWindow(Integer.MAX_VALUE, 1)); + assertEquals(false, wc.increaseStreamWindow(Integer.MAX_VALUE, 1)); wc.registerStream(3, DEFAULT_INITIAL_WINDOW_SIZE); - assertEquals(wc.increaseStreamWindow(100, 3), true); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 3), false); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 3), false); + assertEquals(true, wc.increaseStreamWindow(100, 3)); + assertEquals(false, wc.increaseStreamWindow(Integer.MAX_VALUE, 3)); + assertEquals(false, wc.increaseStreamWindow(Integer.MAX_VALUE, 3)); wc.registerStream(5, 0); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 5), true); - assertEquals(wc.increaseStreamWindow(1, 5), false); - assertEquals(wc.increaseStreamWindow(1, 5), false); - assertEquals(wc.increaseStreamWindow(10, 5), false); + assertEquals(true, wc.increaseStreamWindow(Integer.MAX_VALUE, 5)); + assertEquals(false, wc.increaseStreamWindow(1, 5)); + assertEquals(false, wc.increaseStreamWindow(1, 5)); + assertEquals(false, wc.increaseStreamWindow(10, 5)); wc.registerStream(7, -1); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 7), true); - assertEquals(wc.increaseStreamWindow(1, 7), true); - assertEquals(wc.increaseStreamWindow(1, 7), false); - assertEquals(wc.increaseStreamWindow(10, 7), false); + assertEquals(true, wc.increaseStreamWindow(Integer.MAX_VALUE, 7)); + assertEquals(true, wc.increaseStreamWindow(1, 7)); + assertEquals(false, wc.increaseStreamWindow(1, 7)); + assertEquals(false, wc.increaseStreamWindow(10, 7)); wc.registerStream(9, -1); - assertEquals(wc.increaseStreamWindow(1, 9), true); - assertEquals(wc.increaseStreamWindow(1, 9), true); - assertEquals(wc.increaseStreamWindow(1, 9), true); - assertEquals(wc.increaseStreamWindow(10, 9), true); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 9), false); + assertEquals(true, wc.increaseStreamWindow(1, 9)); + assertEquals(true, wc.increaseStreamWindow(1, 9)); + assertEquals(true, wc.increaseStreamWindow(1, 9)); + assertEquals(true, wc.increaseStreamWindow(10, 9)); + assertEquals(false, wc.increaseStreamWindow(Integer.MAX_VALUE, 9)); wc.registerStream(11, -10); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.increaseStreamWindow(1, 11), true); - assertEquals(wc.streamWindowSize(11), 1); - assertEquals(wc.increaseStreamWindow(Integer.MAX_VALUE, 11), false); - assertEquals(wc.streamWindowSize(11), 1); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(true, wc.increaseStreamWindow(1, 11)); + assertEquals(1, wc.streamWindowSize(11)); + assertEquals(false, wc.increaseStreamWindow(Integer.MAX_VALUE, 11)); + assertEquals(1, wc.streamWindowSize(11)); } @Test @@ -125,9 +126,9 @@ public class WindowControllerTest { wc.tryAcquire(51, 5 , null); wc.adjustActiveStreams(-200); - assertEquals(wc.streamWindowSize(1), -149); - assertEquals(wc.streamWindowSize(3), -150); - assertEquals(wc.streamWindowSize(5), -151); + assertEquals(-149, wc.streamWindowSize(1)); + assertEquals(-150, wc.streamWindowSize(3)); + assertEquals(-151, wc.streamWindowSize(5)); } static final Class IE = InternalError.class; diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WrapperTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WrapperTest.java index 6dad2cc75b5..bc69c425678 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WrapperTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/WrapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -27,11 +27,10 @@ import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import org.testng.annotations.Test; import jdk.internal.net.http.common.SubscriberWrapper; -@Test +import org.junit.jupiter.api.Test; + public class WrapperTest { static final int LO_PRI = 1; static final int HI_PRI = 2; diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/DemandTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/DemandTest.java index 4ea604fd488..1ea72f472e9 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/DemandTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/DemandTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -23,15 +23,16 @@ package jdk.internal.net.http.common; -import org.testng.annotations.Test; - import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.atomic.AtomicReference; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; public class DemandTest { @@ -59,53 +60,61 @@ public class DemandTest { public void test03() { Demand d = new Demand(); d.increase(3); - assertEquals(d.decreaseAndGet(3), 3); + assertEquals(3, d.decreaseAndGet(3)); } @Test public void test04() { Demand d = new Demand(); d.increase(3); - assertEquals(d.decreaseAndGet(5), 3); + assertEquals(3, d.decreaseAndGet(5)); } @Test public void test05() { Demand d = new Demand(); d.increase(7); - assertEquals(d.decreaseAndGet(4), 4); + assertEquals(4, d.decreaseAndGet(4)); } @Test public void test06() { Demand d = new Demand(); - assertEquals(d.decreaseAndGet(3), 0); + assertEquals(0, d.decreaseAndGet(3)); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test07() { - Demand d = new Demand(); - d.increase(0); + assertThrows(IllegalArgumentException.class, () -> { + Demand d = new Demand(); + d.increase(0); + }); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test08() { - Demand d = new Demand(); - d.increase(-1); + assertThrows(IllegalArgumentException.class, () -> { + Demand d = new Demand(); + d.increase(-1); + }); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test09() { - Demand d = new Demand(); - d.increase(10); - d.decreaseAndGet(0); + assertThrows(IllegalArgumentException.class, () -> { + Demand d = new Demand(); + d.increase(10); + d.decreaseAndGet(0); + }); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test10() { - Demand d = new Demand(); - d.increase(13); - d.decreaseAndGet(-3); + assertThrows(IllegalArgumentException.class, () -> { + Demand d = new Demand(); + d.increase(13); + d.decreaseAndGet(-3); + }); } @Test @@ -169,7 +178,7 @@ public class DemandTest { assertTrue(d.isFulfilled()); } - @Test(invocationCount = 32) + @Test public void test15() throws InterruptedException { int N = Math.max(2, Runtime.getRuntime().availableProcessors() + 1); int M = ((N + 1) * N) / 2; // 1 + 2 + 3 + ... N @@ -187,7 +196,7 @@ public class DemandTest { error.compareAndSet(null, e); } try { - assertEquals(d.decreaseAndGet(j), j); + assertEquals(j, d.decreaseAndGet(j)); } catch (Throwable t) { error.compareAndSet(null, t); } finally { @@ -197,6 +206,6 @@ public class DemandTest { } stop.await(); assertTrue(d.isFulfilled()); - assertEquals(error.get(), null); + assertNull(error.get()); } } diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java index 01798a645a2..2c33f6f0018 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/common/MinimalFutureTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,19 +23,19 @@ package jdk.internal.net.http.common; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import static org.testng.Assert.assertThrows; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertThrows; public class MinimalFutureTest { - @Test(dataProvider = "futures") + @ParameterizedTest + @MethodSource("futures") public void test(CompletableFuture mf) { ExecutorService executor = Executors.newSingleThreadExecutor(); try { @@ -134,8 +134,7 @@ public class MinimalFutureTest { } - @DataProvider(name = "futures") - public Object[][] futures() { + public static Object[][] futures() { MinimalFuture mf = new MinimalFuture<>(); mf.completeExceptionally(new Throwable()); diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/frame/FramesDecoderTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/frame/FramesDecoderTest.java index 3db439d1c00..e8acc95c1d2 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/frame/FramesDecoderTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/frame/FramesDecoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,15 +27,15 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import org.testng.Assert; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class FramesDecoderTest { - abstract class TestFrameProcessor implements FramesDecoder.FrameProcessor { + abstract static class TestFrameProcessor implements FramesDecoder.FrameProcessor { protected volatile int count; public int numberOfFramesDecoded() { return count; } } @@ -69,17 +69,17 @@ public class FramesDecoderTest { assertTrue(frame instanceof DataFrame); DataFrame dataFrame = (DataFrame) frame; List list = dataFrame.getData(); - assertEquals(list.size(), 1); + assertEquals(1, list.size()); ByteBuffer data = list.get(0); byte[] bytes = new byte[data.remaining()]; data.get(bytes); if (count == 0) { - assertEquals(new String(bytes, UTF_8), "XXXX"); + assertEquals("XXXX", new String(bytes, UTF_8)); out.println("First data received:" + data); - assertEquals(data.position(), data.limit()); // since bytes read - assertEquals(data.limit(), data.capacity()); + assertEquals(data.limit(), data.position()); // since bytes read + assertEquals(data.capacity(), data.limit()); } else { - assertEquals(new String(bytes, UTF_8), "YYYY"); + assertEquals("YYYY", new String(bytes, UTF_8)); out.println("Second data received:" + data); } count++; @@ -89,7 +89,7 @@ public class FramesDecoderTest { out.println("Sending " + combined + " to decoder: "); decoder.decode(combined); - Assert.assertEquals(testFrameProcessor.numberOfFramesDecoded(), 2); + assertEquals(2, testFrameProcessor.numberOfFramesDecoded()); } @@ -119,15 +119,15 @@ public class FramesDecoderTest { assertTrue(frame instanceof DataFrame); DataFrame dataFrame = (DataFrame) frame; List list = dataFrame.getData(); - assertEquals(list.size(), 1); + assertEquals(1, list.size()); ByteBuffer data = list.get(0); byte[] bytes = new byte[data.remaining()]; data.get(bytes); - assertEquals(new String(bytes, UTF_8), "XXXX"); + assertEquals("XXXX", new String(bytes, UTF_8)); out.println("First data received:" + data); - assertEquals(data.position(), data.limit()); // since bytes read + assertEquals(data.limit(), data.position()); // since bytes read //assertNotEquals(data.limit(), data.capacity()); - assertEquals(data.capacity(), 1024 - 9 /*frame header*/); + assertEquals(1024 - 9 /*frame header*/, data.capacity()); count++; } }; @@ -135,6 +135,6 @@ public class FramesDecoderTest { out.println("Sending " + combined + " to decoder: "); decoder.decode(combined); - Assert.assertEquals(testFrameProcessor.numberOfFramesDecoded(), 1); + assertEquals(1, testFrameProcessor.numberOfFramesDecoded()); } } From a0c0a3617936bb024ee7a9325b98a49ccaab5041 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 5 Mar 2026 11:12:58 +0000 Subject: [PATCH 176/636] 8378600: Refactor tests under test/jdk/java/net/httpclient/http2 from TestNG to JUnit Reviewed-by: vyazici --- .../java/net/httpclient/http2/ErrorTest.java | 4 +- .../httpclient/http2/FixedThreadPoolTest.java | 6 +- .../httpclient/http2/H2SelectorVTTest.java | 10 +-- .../httpclient/http2/ImplicitPushCancel.java | 35 +++++---- .../java/net/httpclient/http2/NoBodyTest.java | 10 +-- .../net/httpclient/http2/PostPutTest.java | 36 ++++----- .../http2/PushPromiseContinuation.java | 49 ++++++------ .../net/httpclient/http2/RedirectTest.java | 7 +- .../java/net/httpclient/http2/ServerPush.java | 61 ++++++++------- .../http2/ServerPushWithDiffTypes.java | 15 ++-- .../java/net/httpclient/http2/SimpleGet.java | 21 ++--- .../http2/StreamFlowControlTest.java | 78 ++++++++++--------- .../httpclient/http2/TrailingHeadersTest.java | 49 ++++++------ .../net/http/hpack/HeaderTableTest.java | 6 +- 14 files changed, 196 insertions(+), 191 deletions(-) diff --git a/test/jdk/java/net/httpclient/http2/ErrorTest.java b/test/jdk/java/net/httpclient/http2/ErrorTest.java index 6b36529b38f..0f3bafa571d 100644 --- a/test/jdk/java/net/httpclient/http2/ErrorTest.java +++ b/test/jdk/java/net/httpclient/http2/ErrorTest.java @@ -45,7 +45,7 @@ * java.net.http/jdk.internal.net.http.qpack.writers * java.security.jgss * @modules java.base/jdk.internal.util - * @run testng/othervm/timeout=60 -Djavax.net.debug=ssl -Djdk.httpclient.HttpClient.log=all ErrorTest + * @run junit/othervm/timeout=60 -Djavax.net.debug=ssl -Djdk.httpclient.HttpClient.log=all ErrorTest * @summary check exception thrown when bad TLS parameters selected */ @@ -67,7 +67,7 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.test.lib.net.SimpleSSLContext; import static java.net.http.HttpClient.Version.HTTP_2; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; /** * When selecting an unacceptable cipher suite the TLS handshake will fail. diff --git a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java index 2378b0b6982..d788d39b441 100644 --- a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java +++ b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java @@ -30,7 +30,7 @@ * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors FixedThreadPoolTest + * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors FixedThreadPoolTest */ import java.net.*; @@ -51,7 +51,7 @@ import static jdk.test.lib.Asserts.assertFileContentsEqual; import static jdk.test.lib.Utils.createTempFile; import static jdk.test.lib.Utils.createTempFileOfSize; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class FixedThreadPoolTest implements HttpServerAdapters { @@ -92,7 +92,7 @@ public class FixedThreadPoolTest implements HttpServerAdapters { } @Test - public static void test() throws Exception { + public void test() throws Exception { try { initialize(); simpleTest(false); diff --git a/test/jdk/java/net/httpclient/http2/H2SelectorVTTest.java b/test/jdk/java/net/httpclient/http2/H2SelectorVTTest.java index 85d4012494a..7adfd57319c 100644 --- a/test/jdk/java/net/httpclient/http2/H2SelectorVTTest.java +++ b/test/jdk/java/net/httpclient/http2/H2SelectorVTTest.java @@ -38,7 +38,7 @@ import jdk.test.lib.net.SimpleSSLContext; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Version.HTTP_2; @@ -166,21 +166,21 @@ class H2SelectorVTTest implements HttpServerAdapters { final HttpRequest req1 = reqBuilder.copy().GET().build(); System.out.println("\nIssuing request: " + req1); final HttpResponse resp1 = client.send(req1, BodyHandlers.ofString()); - Assertions.assertEquals(200, resp1.statusCode(), "unexpected response code for GET request"); + assertEquals(200, resp1.statusCode(), "unexpected response code for GET request"); assertSelectorThread(client); // POST final HttpRequest req2 = reqBuilder.copy().POST(BodyPublishers.ofString("foo")).build(); System.out.println("\nIssuing request: " + req2); final HttpResponse resp2 = client.send(req2, BodyHandlers.ofString()); - Assertions.assertEquals(200, resp2.statusCode(), "unexpected response code for POST request"); + assertEquals(200, resp2.statusCode(), "unexpected response code for POST request"); assertSelectorThread(client); // HEAD final HttpRequest req3 = reqBuilder.copy().HEAD().build(); System.out.println("\nIssuing request: " + req3); final HttpResponse resp3 = client.send(req3, BodyHandlers.ofString()); - Assertions.assertEquals(200, resp3.statusCode(), "unexpected response code for HEAD request"); + assertEquals(200, resp3.statusCode(), "unexpected response code for HEAD request"); assertSelectorThread(client); } } @@ -219,6 +219,6 @@ class H2SelectorVTTest implements HttpServerAdapters { msg = "%s not found in %s".formatted(name, threads); System.out.printf("%s: %s%n", status, msg); } - Assertions.assertEquals(!isTCPSelectorThreadVirtual(), found, msg); + assertEquals(!isTCPSelectorThreadVirtual(), found, msg); } } diff --git a/test/jdk/java/net/httpclient/http2/ImplicitPushCancel.java b/test/jdk/java/net/httpclient/http2/ImplicitPushCancel.java index c75a62d0f42..5bcaf95c6fc 100644 --- a/test/jdk/java/net/httpclient/http2/ImplicitPushCancel.java +++ b/test/jdk/java/net/httpclient/http2/ImplicitPushCancel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -25,7 +25,7 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,responses,trace * ImplicitPushCancel @@ -51,15 +51,16 @@ import java.util.concurrent.ConcurrentMap; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ImplicitPushCancel { - static Map PUSH_PROMISES = Map.of( + static final Map PUSH_PROMISES = Map.of( "/x/y/z/1", "the first push promise body", "/x/y/z/2", "the second push promise body", "/x/y/z/3", "the third push promise body", @@ -72,11 +73,11 @@ public class ImplicitPushCancel { ); static final String MAIN_RESPONSE_BODY = "the main response body"; - Http2TestServer server; - URI uri; + private static Http2TestServer server; + private static URI uri; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { server = new Http2TestServer(false, 0); Http2Handler handler = new ServerPushHandler(MAIN_RESPONSE_BODY, PUSH_PROMISES); @@ -87,13 +88,13 @@ public class ImplicitPushCancel { uri = new URI("http://localhost:" + port + "/foo/a/b/c"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { server.stop(); } static final HttpResponse assert200ResponseCode(HttpResponse response) { - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); return response; } @@ -128,11 +129,11 @@ public class ImplicitPushCancel { promises.entrySet().stream().forEach(entry -> { HttpRequest request = entry.getKey(); HttpResponse response = entry.getValue().join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); if (PUSH_PROMISES.containsKey(request.uri().getPath())) { - assertEquals(response.body(), PUSH_PROMISES.get(request.uri().getPath())); + assertEquals(PUSH_PROMISES.get(request.uri().getPath()), response.body()); } else { - assertEquals(response.body(), MAIN_RESPONSE_BODY); + assertEquals(MAIN_RESPONSE_BODY, response.body()); } } ); diff --git a/test/jdk/java/net/httpclient/http2/NoBodyTest.java b/test/jdk/java/net/httpclient/http2/NoBodyTest.java index 3d30c54654a..2459c8eba4b 100644 --- a/test/jdk/java/net/httpclient/http2/NoBodyTest.java +++ b/test/jdk/java/net/httpclient/http2/NoBodyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 8087112 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors + * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors * -Djdk.internal.httpclient.debug=true * NoBodyTest */ @@ -47,10 +47,10 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; -@Test +import org.junit.jupiter.api.Test; + public class NoBodyTest { static int httpPort, httpsPort; static Http2TestServer httpServer, httpsServer; @@ -86,7 +86,7 @@ public class NoBodyTest { } @Test - public static void runtest() throws Exception { + public void runtest() throws Exception { try { initialize(); warmup(false); diff --git a/test/jdk/java/net/httpclient/http2/PostPutTest.java b/test/jdk/java/net/httpclient/http2/PostPutTest.java index 89b192f6171..84e86b556ca 100644 --- a/test/jdk/java/net/httpclient/http2/PostPutTest.java +++ b/test/jdk/java/net/httpclient/http2/PostPutTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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,7 +28,7 @@ * does not process any data. The client should read all data from the server and close the connection. * @library /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm/timeout=50 -Djdk.httpclient.HttpClient.log=all + * @run junit/othervm/timeout=50 -Djdk.httpclient.HttpClient.log=all * PostPutTest */ @@ -36,11 +36,6 @@ import jdk.httpclient.test.lib.http2.Http2Handler; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2TestServer; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.IOException; import java.io.PrintStream; import java.net.URI; @@ -51,19 +46,24 @@ import java.net.http.HttpResponse; import static java.net.http.HttpClient.Version.HTTP_2; import static java.net.http.HttpRequest.BodyPublishers.ofByteArray; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PostPutTest { - Http2TestServer http2TestServer; - URI warmupURI, testHandlerBasicURI, testHandlerCloseBosURI, testHandleNegativeContentLengthURI; + private static Http2TestServer http2TestServer; + private static URI warmupURI, testHandlerBasicURI, testHandlerCloseBosURI, testHandleNegativeContentLengthURI; static PrintStream testLog = System.err; // As per jdk.internal.net.http.WindowController.DEFAULT_INITIAL_WINDOW_SIZE - final int DEFAULT_INITIAL_WINDOW_SIZE = (64 * 1024) - 1; + private static final int DEFAULT_INITIAL_WINDOW_SIZE = (64 * 1024) - 1; // Add on a small amount of arbitrary bytes to see if client hangs when receiving RST_STREAM - byte[] data = new byte[DEFAULT_INITIAL_WINDOW_SIZE + 10]; + private static byte[] data = new byte[DEFAULT_INITIAL_WINDOW_SIZE + 10]; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { http2TestServer = new Http2TestServer(false, 0); http2TestServer.addHandler(new WarmupHandler(), "/Warmup"); http2TestServer.addHandler(new TestHandlerBasic(), "/TestHandlerBasic"); @@ -81,15 +81,14 @@ public class PostPutTest { testLog.println("PostPutTest.setup(): testHandleNegativeContentLengthURI: " + testHandleNegativeContentLengthURI); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { testLog.println("PostPutTest.teardown(): Stopping server"); http2TestServer.stop(); data = null; } - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { HttpRequest over64kPost, over64kPut, over64kPostCloseBos, over64kPutCloseBos, over64kPostNegativeContentLength, over64kPutNegativeContentLength; over64kPost = HttpRequest.newBuilder().version(HTTP_2).POST(ofByteArray(data)).uri(testHandlerBasicURI).build(); over64kPut = HttpRequest.newBuilder().version(HTTP_2).PUT(ofByteArray(data)).uri(testHandlerBasicURI).build(); @@ -117,7 +116,8 @@ public class PostPutTest { .build(); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void testOver64kPUT(HttpRequest req, String testMessage) { testLog.println("PostPutTest: Performing test: " + testMessage); HttpClient hc = HttpClient.newBuilder().version(HTTP_2).build(); diff --git a/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java b/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java index e9c6447e600..2d5b5dd4f4e 100644 --- a/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java +++ b/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -31,10 +31,9 @@ * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer * jdk.httpclient.test.lib.http2.BodyOutputStream * jdk.httpclient.test.lib.http2.OutgoingPushPromise - * @run testng/othervm PushPromiseContinuation + * @run junit/othervm PushPromiseContinuation */ - import javax.net.ssl.SSLSession; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -68,14 +67,13 @@ import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.frame.ContinuationFrame; import jdk.internal.net.http.frame.HeaderFrame; -import org.testng.TestException; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; - import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class PushPromiseContinuation { @@ -85,8 +83,8 @@ public class PushPromiseContinuation { static volatile int continuationCount; static final String mainPromiseBody = "Main Promise Body"; static final String mainResponseBody = "Main Response Body"; - Http2TestServer server; - URI uri; + private static Http2TestServer server; + private static URI uri; // Set up simple client-side push promise handler ConcurrentMap>> pushPromiseMap = new ConcurrentHashMap<>(); @@ -95,13 +93,13 @@ public class PushPromiseContinuation { pushPromiseMap.put(pushRequest, acceptor.apply(s)); }; - @BeforeMethod + @BeforeEach public void beforeMethod() { pushPromiseMap = new ConcurrentHashMap<>(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { server = new Http2TestServer(false, 0); server.addHandler(new ServerPushHandler(), "/"); @@ -115,9 +113,8 @@ public class PushPromiseContinuation { uri = new URI("http://localhost:" + port + "/"); } - @AfterTest - public void teardown() { - pushPromiseMap = null; + @AfterAll + public static void teardown() { server.stop(); } @@ -195,29 +192,29 @@ public class PushPromiseContinuation { CompletableFuture> cf = client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph); - CompletionException t = expectThrows(CompletionException.class, () -> cf.join()); - assertEquals(t.getCause().getClass(), ProtocolException.class, + CompletionException t = assertThrows(CompletionException.class, () -> cf.join()); + assertEquals(ProtocolException.class, t.getCause().getClass(), "Expected a ProtocolException but got " + t.getCause()); System.err.println("Client received the following expected exception: " + t.getCause()); faultyServer.stop(); } private void verify(HttpResponse resp) { - assertEquals(resp.statusCode(), 200); - assertEquals(resp.body(), mainResponseBody); + assertEquals(200, resp.statusCode()); + assertEquals(mainResponseBody, resp.body()); if (pushPromiseMap.size() > 1) { System.err.println(pushPromiseMap.entrySet()); - throw new TestException("Results map size is greater than 1"); + fail("Results map size is greater than 1"); } else { // This will only iterate once for (HttpRequest r : pushPromiseMap.keySet()) { HttpResponse serverPushResp = pushPromiseMap.get(r).join(); // Received headers should be the same as the combined PushPromise // frame headers combined with the Continuation frame headers - assertEquals(testHeaders, r.headers()); + assertEquals(r.headers(), testHeaders); // Check status code and push promise body are as expected - assertEquals(serverPushResp.statusCode(), 200); - assertEquals(serverPushResp.body(), mainPromiseBody); + assertEquals(200, serverPushResp.statusCode()); + assertEquals(mainPromiseBody, serverPushResp.body()); } } } diff --git a/test/jdk/java/net/httpclient/http2/RedirectTest.java b/test/jdk/java/net/httpclient/http2/RedirectTest.java index e2acd807bd5..201b56513f6 100644 --- a/test/jdk/java/net/httpclient/http2/RedirectTest.java +++ b/test/jdk/java/net/httpclient/http2/RedirectTest.java @@ -30,7 +30,7 @@ * jdk.httpclient.test.lib.http2.Http2RedirectHandler * jdk.test.lib.Asserts * jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=frames,ssl,requests,responses,errors * -Djdk.internal.httpclient.debug=true * RedirectTest @@ -52,9 +52,10 @@ import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2RedirectHandler; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; +import org.junit.jupiter.api.Test; + public class RedirectTest implements HttpServerAdapters { static int httpPort; static HttpTestServer httpServer; @@ -135,7 +136,7 @@ public class RedirectTest implements HttpServerAdapters { } @Test - public static void test() throws Exception { + public void test() throws Exception { try { initialize(); simpleTest(); diff --git a/test/jdk/java/net/httpclient/http2/ServerPush.java b/test/jdk/java/net/httpclient/http2/ServerPush.java index 7f9c82fb28b..d38b867132b 100644 --- a/test/jdk/java/net/httpclient/http2/ServerPush.java +++ b/test/jdk/java/net/httpclient/http2/ServerPush.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -29,7 +29,7 @@ * @build jdk.httpclient.test.lib.http2.Http2TestServer * jdk.httpclient.test.lib.http2.PushHandler * jdk.test.lib.Utils - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=errors,requests,responses * ServerPush */ @@ -48,13 +48,14 @@ import java.util.concurrent.*; import java.util.function.Consumer; import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.PushHandler; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import static java.nio.charset.StandardCharsets.UTF_8; import static jdk.test.lib.Utils.createTempFileOfSize; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class ServerPush { @@ -66,11 +67,11 @@ public class ServerPush { static Path tempFile; - Http2TestServer server; - URI uri; + private static Http2TestServer server; + private static URI uri; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { tempFile = createTempFileOfSize(TEMP_FILE_PREFIX, null, FILE_SIZE); server = new Http2TestServer(false, 0); server.addHandler(new PushHandler(tempFile, LOOPS), "/"); @@ -82,8 +83,8 @@ public class ServerPush { uri = new URI("http://localhost:" + port + "/foo/a/b/c"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { server.stop(); } @@ -109,10 +110,10 @@ public class ServerPush { System.err.println("results.size: " + resultMap.size()); for (HttpRequest r : resultMap.keySet()) { HttpResponse response = resultMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), tempFileAsString); + assertEquals(200, response.statusCode()); + assertEquals(tempFileAsString, response.body()); } - assertEquals(resultMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultMap.size()); } // Test 2 - of(...) populating the given Map, everything as a String @@ -135,10 +136,10 @@ public class ServerPush { System.err.println("results.size: " + resultMap.size()); for (HttpRequest r : resultMap.keySet()) { HttpResponse response = resultMap.get(r).join(); - assertEquals(response.statusCode(), 200); - assertEquals(response.body(), tempFileAsString); + assertEquals(200, response.statusCode()); + assertEquals(tempFileAsString, response.body()); } - assertEquals(resultMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultMap.size()); } // --- Path --- @@ -177,11 +178,11 @@ public class ServerPush { for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); String fileAsString = new String(Files.readAllBytes(response.body()), UTF_8); - assertEquals(fileAsString, tempFileAsString); + assertEquals(tempFileAsString, fileAsString); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } // Test 4 - of(...) populating the given Map, everything as a Path @@ -204,11 +205,11 @@ public class ServerPush { for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); String fileAsString = new String(Files.readAllBytes(response.body()), UTF_8); - assertEquals(fileAsString, tempFileAsString); + assertEquals(tempFileAsString, fileAsString); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } // --- Consumer --- @@ -263,12 +264,12 @@ public class ServerPush { for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes(); String result = new String(ba, UTF_8); - assertEquals(result, tempFileAsString); + assertEquals(tempFileAsString, result); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } // Test 6 - of(...) populating the given Map, everything as a consumer of optional byte[] @@ -301,11 +302,11 @@ public class ServerPush { for (HttpRequest r : resultsMap.keySet()) { HttpResponse response = resultsMap.get(r).join(); - assertEquals(response.statusCode(), 200); + assertEquals(200, response.statusCode()); byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes(); String result = new String(ba, UTF_8); - assertEquals(result, tempFileAsString); + assertEquals(tempFileAsString, result); } - assertEquals(resultsMap.size(), LOOPS + 1); + assertEquals(LOOPS + 1, resultsMap.size()); } } diff --git a/test/jdk/java/net/httpclient/http2/ServerPushWithDiffTypes.java b/test/jdk/java/net/httpclient/http2/ServerPushWithDiffTypes.java index 3e12a20c15e..9cf2a3f7ae2 100644 --- a/test/jdk/java/net/httpclient/http2/ServerPushWithDiffTypes.java +++ b/test/jdk/java/net/httpclient/http2/ServerPushWithDiffTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -25,7 +25,7 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,responses * ServerPushWithDiffTypes @@ -47,9 +47,10 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.http2.Http2Handler; -import org.testng.annotations.Test; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerPushWithDiffTypes { @@ -66,7 +67,7 @@ public class ServerPushWithDiffTypes { ); @Test - public static void test() throws Exception { + public void test() throws Exception { Http2TestServer server = null; try { server = new Http2TestServer(false, 0); @@ -93,7 +94,7 @@ public class ServerPushWithDiffTypes { results.put(request, cf); cf.join(); - assertEquals(results.size(), PUSH_PROMISES.size() + 1); + assertEquals(PUSH_PROMISES.size() + 1, results.size()); for (HttpRequest r : results.keySet()) { URI u = r.uri(); @@ -116,7 +117,7 @@ public class ServerPushWithDiffTypes { String expected = PUSH_PROMISES.get(r.uri().getPath()); if (expected == null) expected = "the main response body"; - assertEquals(result, expected); + assertEquals(expected, result); } } finally { server.stop(); diff --git a/test/jdk/java/net/httpclient/http2/SimpleGet.java b/test/jdk/java/net/httpclient/http2/SimpleGet.java index 692a6583b63..5df3174f820 100644 --- a/test/jdk/java/net/httpclient/http2/SimpleGet.java +++ b/test/jdk/java/net/httpclient/http2/SimpleGet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -27,11 +27,11 @@ * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestUtil * jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm -XX:+CrashOnOutOfMemoryError SimpleGet - * @run testng/othervm -XX:+CrashOnOutOfMemoryError + * @run junit/othervm -XX:+CrashOnOutOfMemoryError SimpleGet + * @run junit/othervm -XX:+CrashOnOutOfMemoryError * -Dsimpleget.repeat=1 -Dsimpleget.chunks=1 -Dsimpleget.requests=1000 * SimpleGet - * @run testng/othervm -Dsimpleget.requests=150 + * @run junit/othervm -Dsimpleget.requests=150 * -Dsimpleget.chunks=16384 * -Djdk.httpclient.redirects.retrylimit=5 * -Djdk.httpclient.HttpClient.log=errors @@ -62,11 +62,12 @@ import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.Assert; -import org.testng.annotations.Test; import static java.net.http.HttpClient.Version.HTTP_2; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class SimpleGet implements HttpServerAdapters { static HttpTestServer httpsServer; static HttpClient client = null; @@ -117,11 +118,11 @@ public class SimpleGet implements HttpServerAdapters { } public static void main(String[] args) throws Exception { - test(); + new SimpleGet().test(); } @Test - public static void test() throws Exception { + public void test() throws Exception { try { long prestart = System.nanoTime(); initialize(); @@ -132,7 +133,7 @@ public class SimpleGet implements HttpServerAdapters { .GET().build(); long start = System.nanoTime(); var resp = client.send(request, BodyHandlers.ofByteArrayConsumer(b -> {})); - Assert.assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); long elapsed = System.nanoTime() - start; System.out.println("Stat: First request took: " + elapsed + " nanos (" + TimeUnit.NANOSECONDS.toMillis(elapsed) + " ms)"); final int max = property("simpleget.requests", 50); @@ -163,7 +164,7 @@ public class SimpleGet implements HttpServerAdapters { + connections.size() + " connections"); } } - list.forEach((cf) -> Assert.assertEquals(cf.join().statusCode(), 200)); + list.forEach((cf) -> assertEquals(200, cf.join().statusCode())); } catch (Throwable tt) { System.err.println("tt caught"); tt.printStackTrace(); diff --git a/test/jdk/java/net/httpclient/http2/StreamFlowControlTest.java b/test/jdk/java/net/httpclient/http2/StreamFlowControlTest.java index 3edf0b71305..a36fe29813c 100644 --- a/test/jdk/java/net/httpclient/http2/StreamFlowControlTest.java +++ b/test/jdk/java/net/httpclient/http2/StreamFlowControlTest.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 @@ -26,7 +26,7 @@ * @bug 8342075 8343855 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm -Djdk.internal.httpclient.debug=true + * @run junit/othervm -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.connectionWindowSize=65535 * -Djdk.httpclient.windowsize=16384 * StreamFlowControlTest @@ -65,30 +65,30 @@ import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.frame.SettingsFrame; import jdk.test.lib.Utils; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.util.concurrent.TimeUnit.NANOSECONDS; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class StreamFlowControlTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpTestServer http2TestServer; // HTTP/2 ( h2c ) - HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - String http2URI; - String https2URI; - final AtomicInteger reqid = new AtomicInteger(); + private static HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + private static HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + private static String http2URI; + private static String https2URI; + private static final AtomicInteger reqid = new AtomicInteger(); final static int WINDOW = Integer.getInteger("jdk.httpclient.windowsize", 2 * 16 * 1024); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][] { { http2URI, false }, { https2URI, false }, @@ -111,7 +111,8 @@ public class StreamFlowControlTest { } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void test(String uri, boolean sameClient) throws Exception @@ -142,7 +143,7 @@ public class StreamFlowControlTest { if (sameClient) { String key = response.headers().firstValue("X-Connection-Key").get(); if (label == null) label = key; - assertEquals(key, label, "Unexpected key for " + query); + assertEquals(label, key, "Unexpected key for " + query); } sent.join(); // we have to pull to get the exception, but slow enough @@ -175,7 +176,8 @@ public class StreamFlowControlTest { } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") void testAsync(String uri, boolean sameClient) { @@ -207,7 +209,7 @@ public class StreamFlowControlTest { if (sameClient) { String key = response.headers().firstValue("X-Connection-Key").get(); if (label == null) label = key; - assertEquals(key, label, "Unexpected key for " + query); + assertEquals(label, key, "Unexpected key for " + query); } sent.join(); long wait = uri.startsWith("https://") ? 800 : 350; @@ -264,38 +266,38 @@ public class StreamFlowControlTest { } } - @BeforeTest - public void setup() throws Exception { - var http2TestServer = new Http2TestServer("localhost", false, 0); - http2TestServer.addHandler(new Http2TestHandler(), "/http2/"); - this.http2TestServer = HttpTestServer.of(http2TestServer); - http2URI = "http://" + this.http2TestServer.serverAuthority() + "/http2/x"; + @BeforeAll + public static void setup() throws Exception { + var http2TestServerImpl = new Http2TestServer("localhost", false, 0); + http2TestServerImpl.addHandler(new Http2TestHandler(), "/http2/"); + http2TestServer = HttpTestServer.of(http2TestServerImpl); + http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/x"; - var https2TestServer = new Http2TestServer("localhost", true, sslContext); - https2TestServer.addHandler(new Http2TestHandler(), "/https2/"); - this.https2TestServer = HttpTestServer.of(https2TestServer); - this.https2TestServer.addHandler(new HttpHeadOrGetHandler(), "/https2/head/"); - https2URI = "https://" + this.https2TestServer.serverAuthority() + "/https2/x"; - String h2Head = "https://" + this.https2TestServer.serverAuthority() + "/https2/head/z"; + var https2TestServerImpl = new Http2TestServer("localhost", true, sslContext); + https2TestServerImpl.addHandler(new Http2TestHandler(), "/https2/"); + https2TestServer = HttpTestServer.of(https2TestServerImpl); + https2TestServer.addHandler(new HttpHeadOrGetHandler(), "/https2/head/"); + https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/x"; + String h2Head = "https://" + https2TestServer.serverAuthority() + "/https2/head/z"; // Override the default exchange supplier with a custom one to enable // particular test scenarios - http2TestServer.setExchangeSupplier(FCHttp2TestExchange::new); - https2TestServer.setExchangeSupplier(FCHttp2TestExchange::new); + http2TestServerImpl.setExchangeSupplier(FCHttp2TestExchange::new); + https2TestServerImpl.setExchangeSupplier(FCHttp2TestExchange::new); - this.http2TestServer.start(); - this.https2TestServer.start(); + http2TestServer.start(); + https2TestServer.start(); // warmup to eliminate delay due to SSL class loading and initialization. try (var client = HttpClient.newBuilder().sslContext(sslContext).build()) { var request = HttpRequest.newBuilder(URI.create(h2Head)).HEAD().build(); var resp = client.send(request, BodyHandlers.discarding()); - assertEquals(resp.statusCode(), 200); + assertEquals(200, resp.statusCode()); } } - @AfterTest - public void teardown() throws Exception { + @AfterAll + public static void teardown() throws Exception { http2TestServer.stop(); https2TestServer.stop(); } diff --git a/test/jdk/java/net/httpclient/http2/TrailingHeadersTest.java b/test/jdk/java/net/httpclient/http2/TrailingHeadersTest.java index 9fa6f8b728e..9ea331d2d84 100644 --- a/test/jdk/java/net/httpclient/http2/TrailingHeadersTest.java +++ b/test/jdk/java/net/httpclient/http2/TrailingHeadersTest.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,7 +28,7 @@ * @bug 8296410 * @library /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer - * @run testng/othervm -Djdk.httpclient.HttpClient.log=all TrailingHeadersTest + * @run junit/othervm -Djdk.httpclient.HttpClient.log=all TrailingHeadersTest */ import jdk.httpclient.test.lib.http2.OutgoingPushPromise; @@ -36,11 +36,6 @@ import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.frame.DataFrame; import jdk.internal.net.http.frame.HeaderFrame; import jdk.internal.net.http.frame.HeadersFrame; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLSession; import java.io.ByteArrayInputStream; @@ -74,24 +69,30 @@ import jdk.httpclient.test.lib.http2.BodyOutputStream; import static java.net.http.HttpClient.Version.HTTP_2; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TrailingHeadersTest { - Http2TestServer http2TestServer; - URI trailingURI, trailng1xxURI, trailingPushPromiseURI, warmupURI; + private static Http2TestServer http2TestServer; + private static URI trailingURI, trailng1xxURI, trailingPushPromiseURI, warmupURI; static PrintStream testLog = System.err; // Set up simple client-side push promise handler - ConcurrentMap>> pushPromiseMap = new ConcurrentHashMap<>(); + private static ConcurrentMap>> pushPromiseMap = new ConcurrentHashMap<>(); - @BeforeMethod + @BeforeEach public void beforeMethod() { pushPromiseMap = new ConcurrentHashMap<>(); } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { Properties props = new Properties(); // For triggering trailing headers to send after Push Promise Response headers are sent props.setProperty("sendTrailingHeadersAfterPushPromise", "1"); @@ -119,12 +120,13 @@ public class TrailingHeadersTest { warmupURI = URI.create("http://" + http2TestServer.serverAuthority() + "/WarmupHandler"); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { http2TestServer.stop(); } - @Test(dataProvider = "httpRequests") + @ParameterizedTest + @MethodSource("uris") public void testTrailingHeaders(String description, HttpRequest hRequest, HttpResponse.PushPromiseHandler pph) { testLog.println("testTrailingHeaders(): " + description); HttpClient httpClient = HttpClient.newBuilder().build(); @@ -134,7 +136,7 @@ public class TrailingHeadersTest { testLog.println("testTrailingHeaders(): Performing request: " + hRequest); HttpResponse resp = cf.join(); - assertEquals(resp.statusCode(), 200, "Status code of response should be 200"); + assertEquals(200, resp.statusCode(), "Status code of response should be 200"); // Verify Push Promise was successful if necessary if (pph != null) @@ -144,14 +146,14 @@ public class TrailingHeadersTest { } private void verifyPushPromise() { - assertEquals(pushPromiseMap.size(), 1, "Push Promise should not be greater than 1"); + assertEquals(1, pushPromiseMap.size(), "Push Promise should not be greater than 1"); // This will only iterate once for (HttpRequest r : pushPromiseMap.keySet()) { CompletableFuture> serverPushResp = pushPromiseMap.get(r); // Get the push promise HttpResponse result if present HttpResponse resp = serverPushResp.join(); - assertEquals(resp.body(), "Sample_Push_Data", "Unexpected Push Promise response body"); - assertEquals(resp.statusCode(), 200, "Status code of Push Promise response should be 200"); + assertEquals("Sample_Push_Data", resp.body(), "Unexpected Push Promise response body"); + assertEquals(200, resp.statusCode(), "Status code of Push Promise response should be 200"); } } @@ -162,11 +164,10 @@ public class TrailingHeadersTest { httpClient.sendAsync(warmupReq, BodyHandlers.discarding()).join(); } - @DataProvider(name = "httpRequests") - public Object[][] uris() { + public static Object[][] uris() { HttpResponse.PushPromiseHandler pph = (initial, pushRequest, acceptor) -> { HttpResponse.BodyHandler s = HttpResponse.BodyHandlers.ofString(UTF_8); - pushPromiseMap.put(pushRequest, acceptor.apply(s)); + TrailingHeadersTest.pushPromiseMap.put(pushRequest, acceptor.apply(s)); }; HttpRequest httpGetTrailing = HttpRequest.newBuilder(trailingURI).version(HTTP_2) diff --git a/test/jdk/java/net/httpclient/http2/java.net.http/jdk/internal/net/http/hpack/HeaderTableTest.java b/test/jdk/java/net/httpclient/http2/java.net.http/jdk/internal/net/http/hpack/HeaderTableTest.java index 0eb1714da35..07e27a7f2fa 100644 --- a/test/jdk/java/net/httpclient/http2/java.net.http/jdk/internal/net/http/hpack/HeaderTableTest.java +++ b/test/jdk/java/net/httpclient/http2/java.net.http/jdk/internal/net/http/hpack/HeaderTableTest.java @@ -29,9 +29,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import org.junit.jupiter.api.Assertions; -import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HeaderTableTest extends SimpleHeaderTableTest { @@ -76,7 +76,7 @@ public class HeaderTableTest extends SimpleHeaderTableTest { Set expectedIndexes = indexes.get(hName); int actualMinimalIndex = table.indexOf(hName, "blah-blah"); - Assertions.assertTrue(expectedIndexes.contains(-actualMinimalIndex)); + assertTrue(expectedIndexes.contains(-actualMinimalIndex)); }); } From dfea6eb9f84142aaa3e51181ea345e8575729ea2 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 5 Mar 2026 11:13:15 +0000 Subject: [PATCH 177/636] 8378598: Refactor tests under test/jdk/java/net/httpclient/websocket from TestNG to JUnit Reviewed-by: vyazici --- .../java/net/httpclient/websocket/Abort.java | 31 ++++---- .../httpclient/websocket/AutomaticPong.java | 32 +++++---- .../websocket/BlowupOutputQueue.java | 9 ++- .../websocket/HandshakeUrlEncodingTest.java | 50 ++++++------- .../websocket/HeaderWriterDriver.java | 4 +- .../httpclient/websocket/MaskerDriver.java | 4 +- .../websocket/MessageQueueDriver.java | 4 +- .../websocket/PendingBinaryPingClose.java | 12 ++-- .../websocket/PendingBinaryPongClose.java | 12 ++-- .../websocket/PendingOperations.java | 12 ++-- .../websocket/PendingPingBinaryClose.java | 12 ++-- .../websocket/PendingPingTextClose.java | 12 ++-- .../websocket/PendingPongBinaryClose.java | 12 ++-- .../websocket/PendingPongTextClose.java | 12 ++-- .../websocket/PendingTextPingClose.java | 12 ++-- .../websocket/PendingTextPongClose.java | 12 ++-- .../httpclient/websocket/ReaderDriver.java | 6 +- .../httpclient/websocket/SecureSupport.java | 11 +-- .../net/httpclient/websocket/SendTest.java | 16 ++--- .../net/httpclient/websocket/Support.java | 6 +- .../websocket/WSHandshakeExceptionTest.java | 55 +++++++-------- .../websocket/WebSocketBuilderTest.java | 32 ++++----- .../websocket/WebSocketExtendedTest.java | 70 ++++++++++--------- .../websocket/WebSocketProxyTest.java | 28 ++++---- .../httpclient/websocket/WebSocketTest.java | 42 ++++++----- .../net/http/websocket/HeaderWriterTest.java | 9 +-- .../net/http/websocket/MaskerTest.java | 20 +++--- .../net/http/websocket/MessageQueueTest.java | 44 ++++++------ .../net/http/websocket/ReaderTest.java | 25 +++---- .../websocket/security/WSSanityTest.java | 37 +++++----- 30 files changed, 333 insertions(+), 310 deletions(-) diff --git a/test/jdk/java/net/httpclient/websocket/Abort.java b/test/jdk/java/net/httpclient/websocket/Abort.java index 99c94de83f0..a6088f8cce2 100644 --- a/test/jdk/java/net/httpclient/websocket/Abort.java +++ b/test/jdk/java/net/httpclient/websocket/Abort.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,12 +24,11 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.websocket.debug=true * Abort */ -import org.testng.annotations.Test; import java.io.IOException; import java.net.ProtocolException; @@ -44,10 +43,12 @@ import java.util.concurrent.TimeoutException; import static java.net.http.HttpClient.newHttpClient; import static java.net.http.WebSocket.NORMAL_CLOSURE; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; public class Abort { @@ -79,7 +80,7 @@ public class Abort { TimeUnit.SECONDS.sleep(5); List inv = listener.invocationsSoFar(); // no more invocations after onOpen as WebSocket was aborted - assertEquals(inv, List.of(MockListener.Invocation.onOpen(webSocket))); + assertEquals(List.of(MockListener.Invocation.onOpen(webSocket)), inv); } finally { webSocket.abort(); } @@ -119,7 +120,7 @@ public class Abort { List expected = List.of( MockListener.Invocation.onOpen(webSocket), MockListener.Invocation.onText(webSocket, "", true)); - assertEquals(inv, expected); + assertEquals(expected, inv); } finally { webSocket.abort(); } @@ -159,7 +160,7 @@ public class Abort { List expected = List.of( MockListener.Invocation.onOpen(webSocket), MockListener.Invocation.onBinary(webSocket, ByteBuffer.allocate(0), true)); - assertEquals(inv, expected); + assertEquals(expected, inv); } finally { webSocket.abort(); } @@ -198,7 +199,7 @@ public class Abort { List expected = List.of( MockListener.Invocation.onOpen(webSocket), MockListener.Invocation.onPing(webSocket, ByteBuffer.allocate(0))); - assertEquals(inv, expected); + assertEquals(expected, inv); } finally { webSocket.abort(); } @@ -237,7 +238,7 @@ public class Abort { List expected = List.of( MockListener.Invocation.onOpen(webSocket), MockListener.Invocation.onPong(webSocket, ByteBuffer.allocate(0))); - assertEquals(inv, expected); + assertEquals(expected, inv); } finally { webSocket.abort(); } @@ -277,7 +278,7 @@ public class Abort { List expected = List.of( MockListener.Invocation.onOpen(webSocket), MockListener.Invocation.onClose(webSocket, 1005, "")); - assertEquals(inv, expected); + assertEquals(expected, inv); } finally { webSocket.abort(); } @@ -318,7 +319,7 @@ public class Abort { MockListener.Invocation.onOpen(webSocket), MockListener.Invocation.onError(webSocket, ProtocolException.class)); System.out.println("actual invocations:" + Arrays.toString(inv.toArray())); - assertEquals(inv, expected); + assertEquals(expected, inv); } finally { webSocket.abort(); } @@ -396,7 +397,7 @@ public class Abort { ws.abort(); assertTrue(ws.isInputClosed()); assertTrue(ws.isOutputClosed()); - assertEquals(ws.getSubprotocol(), ""); + assertEquals("", ws.getSubprotocol()); } // at this point valid requests MUST be a no-op: for (int j = 0; j < 3; j++) { diff --git a/test/jdk/java/net/httpclient/websocket/AutomaticPong.java b/test/jdk/java/net/httpclient/websocket/AutomaticPong.java index 1999781b82f..b438cb8e728 100644 --- a/test/jdk/java/net/httpclient/websocket/AutomaticPong.java +++ b/test/jdk/java/net/httpclient/websocket/AutomaticPong.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,13 +24,11 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.websocket.debug=true * AutomaticPong */ import jdk.internal.net.http.websocket.Frame; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.net.http.WebSocket; @@ -39,10 +37,14 @@ import java.nio.charset.StandardCharsets; import java.util.List; import static java.net.http.HttpClient.newHttpClient; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class AutomaticPong { /* @@ -86,7 +88,7 @@ public class AutomaticPong { MockListener.Invocation.onPing(webSocket, hello), MockListener.Invocation.onClose(webSocket, 1005, "") ); - assertEquals(actual, expected); + assertEquals(expected, actual); } finally { webSocket.abort(); } @@ -104,7 +106,8 @@ public class AutomaticPong { * b) the last Pong corresponds to the last Ping * c) there are no unrelated Pongs */ - @Test(dataProvider = "nPings") + @ParameterizedTest + @MethodSource("nPings") public void automaticPongs(int nPings) throws Exception { // big enough to not bother with resize ByteBuffer buffer = ByteBuffer.allocate(65536); @@ -133,7 +136,7 @@ public class AutomaticPong { .join(); try { List inv = listener.invocations(); - assertEquals(inv.size(), nPings + 2); // n * onPing + onOpen + onClose + assertEquals(nPings + 2, inv.size()); // n * onPing + onOpen + onClose ByteBuffer data = server.read(); Frame.Reader reader = new Frame.Reader(); @@ -171,7 +174,7 @@ public class AutomaticPong { closed = true; return; } - assertEquals(value, Frame.Opcode.PONG); + assertEquals(Frame.Opcode.PONG, value); } @Override @@ -182,7 +185,7 @@ public class AutomaticPong { @Override public void payloadLen(long value) { if (!closed) - assertEquals(value, 4); + assertEquals(4, value); } @Override @@ -222,8 +225,7 @@ public class AutomaticPong { } - @DataProvider(name = "nPings") - public Object[][] nPings() { + public static Object[][] nPings() { return new Object[][]{{1}, {2}, {4}, {8}, {9}, {256}}; } } diff --git a/test/jdk/java/net/httpclient/websocket/BlowupOutputQueue.java b/test/jdk/java/net/httpclient/websocket/BlowupOutputQueue.java index 7bf2564a04f..1865ca05e4f 100644 --- a/test/jdk/java/net/httpclient/websocket/BlowupOutputQueue.java +++ b/test/jdk/java/net/httpclient/websocket/BlowupOutputQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,14 +24,12 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.internal.httpclient.websocket.debug=true * BlowupOutputQueue */ -import org.testng.annotations.Test; - import java.io.IOException; import java.net.http.WebSocket; import java.nio.ByteBuffer; @@ -43,7 +41,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.testng.Assert.assertFalse; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; public class BlowupOutputQueue extends PendingOperations { diff --git a/test/jdk/java/net/httpclient/websocket/HandshakeUrlEncodingTest.java b/test/jdk/java/net/httpclient/websocket/HandshakeUrlEncodingTest.java index 25a6893ff13..6854cacbcc2 100644 --- a/test/jdk/java/net/httpclient/websocket/HandshakeUrlEncodingTest.java +++ b/test/jdk/java/net/httpclient/websocket/HandshakeUrlEncodingTest.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 @@ -29,7 +29,7 @@ * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestServerConfigurator * @modules java.net.http/jdk.internal.net.http.common * jdk.httpserver - * @run testng/othervm -Djdk.internal.httpclient.debug=true HandshakeUrlEncodingTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true HandshakeUrlEncodingTest */ import com.sun.net.httpserver.HttpHandler; @@ -39,10 +39,6 @@ import com.sun.net.httpserver.HttpExchange; import jdk.httpclient.test.lib.common.TestServerConfigurator; import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.io.IOException; @@ -58,20 +54,25 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; - import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; import static java.lang.System.out; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + + public class HandshakeUrlEncodingTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; - HttpsServer httpsTestServer; - String httpURI; - String httpsURI; + private static HttpServer httpTestServer; + private static HttpsServer httpsTestServer; + private static String httpURI; + private static String httpsURI; static String queryPart; @@ -79,8 +80,7 @@ public class HandshakeUrlEncodingTest { // a shared executor helps reduce the amount of threads created by the test static final ExecutorService executor = Executors.newCachedThreadPool(); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI, false }, { httpsURI, false }, @@ -97,7 +97,8 @@ public class HandshakeUrlEncodingTest { .build(); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void test(String uri, boolean sameClient) { HttpClient client = null; out.println("The url is " + uri); @@ -119,22 +120,21 @@ public class HandshakeUrlEncodingTest { final WebSocketHandshakeException wse = (WebSocketHandshakeException) t; assertNotNull(wse.getResponse()); assertNotNull(wse.getResponse().uri()); - assertNotNull(wse.getResponse().statusCode()); final String rawQuery = wse.getResponse().uri().getRawQuery(); final String expectedRawQuery = "&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz"; - assertEquals(rawQuery, expectedRawQuery); + assertEquals(expectedRawQuery, rawQuery); final String body = (String) wse.getResponse().body(); final String expectedBody = "/?" + expectedRawQuery; - assertEquals(body, expectedBody); + assertEquals(expectedBody, body); out.println("Status code is " + wse.getResponse().statusCode()); out.println("Response is " + wse.getResponse()); - assertEquals(wse.getResponse().statusCode(), 400); + assertEquals(400, wse.getResponse().statusCode()); } } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); queryPart = "?&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz"; httpTestServer = HttpServer.create(sa, 10); @@ -164,8 +164,8 @@ public class HandshakeUrlEncodingTest { httpsTestServer.start(); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { httpTestServer.stop(0); httpsTestServer.stop(0); executor.shutdownNow(); diff --git a/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java b/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java index 6293b1b6c71..75ae5423f00 100644 --- a/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java +++ b/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,7 +25,7 @@ * @test * @bug 8159053 * @modules java.net.http/jdk.internal.net.http.websocket:open - * @run testng/othervm + * @run junit/othervm * --add-reads java.net.http=ALL-UNNAMED * java.net.http/jdk.internal.net.http.websocket.HeaderWriterTest */ diff --git a/test/jdk/java/net/httpclient/websocket/MaskerDriver.java b/test/jdk/java/net/httpclient/websocket/MaskerDriver.java index b84ea1fff5c..e81417c25ef 100644 --- a/test/jdk/java/net/httpclient/websocket/MaskerDriver.java +++ b/test/jdk/java/net/httpclient/websocket/MaskerDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,7 +25,7 @@ * @test * @bug 8159053 * @modules java.net.http/jdk.internal.net.http.websocket:open - * @run testng/othervm + * @run junit/othervm * --add-reads java.net.http=ALL-UNNAMED * java.net.http/jdk.internal.net.http.websocket.MaskerTest */ diff --git a/test/jdk/java/net/httpclient/websocket/MessageQueueDriver.java b/test/jdk/java/net/httpclient/websocket/MessageQueueDriver.java index 9831bb59297..68a23d0be88 100644 --- a/test/jdk/java/net/httpclient/websocket/MessageQueueDriver.java +++ b/test/jdk/java/net/httpclient/websocket/MessageQueueDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -25,7 +25,7 @@ * @test * @bug 8159053 * @modules java.net.http/jdk.internal.net.http.websocket:open - * @run testng/othervm + * @run junit/othervm * --add-reads java.net.http=ALL-UNNAMED * java.net.http/jdk.internal.net.http.websocket.MessageQueueTest */ diff --git a/test/jdk/java/net/httpclient/websocket/PendingBinaryPingClose.java b/test/jdk/java/net/httpclient/websocket/PendingBinaryPingClose.java index 5a410251593..4a5ae315e91 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingBinaryPingClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingBinaryPingClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,28 +24,30 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.sendBufferSize=8192 * -Djdk.internal.httpclient.debug=true * -Djdk.internal.httpclient.websocket.debug=true * PendingBinaryPingClose */ -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingBinaryPingClose extends PendingOperations { CompletableFuture cfBinary; CompletableFuture cfPing; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingBinaryPingClose(boolean last) throws Exception { repeatable(() -> { server = Support.notReadingServer(); diff --git a/test/jdk/java/net/httpclient/websocket/PendingBinaryPongClose.java b/test/jdk/java/net/httpclient/websocket/PendingBinaryPongClose.java index bef3d258850..a8330faeb3f 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingBinaryPongClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingBinaryPongClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,28 +24,30 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.sendBufferSize=8192 * -Djdk.internal.httpclient.debug=true * -Djdk.internal.httpclient.websocket.debug=true * PendingBinaryPongClose */ -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingBinaryPongClose extends PendingOperations { CompletableFuture cfBinary; CompletableFuture cfPong; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingBinaryPongClose(boolean last) throws Exception { repeatable(() -> { server = Support.notReadingServer(); diff --git a/test/jdk/java/net/httpclient/websocket/PendingOperations.java b/test/jdk/java/net/httpclient/websocket/PendingOperations.java index b4d7d7e2222..32350a1ab41 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingOperations.java +++ b/test/jdk/java/net/httpclient/websocket/PendingOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -21,9 +21,6 @@ * questions. */ -import org.testng.annotations.AfterMethod; -import org.testng.annotations.DataProvider; - import java.io.IOException; import java.net.http.HttpClient; import java.net.http.WebSocket; @@ -35,6 +32,8 @@ import java.util.function.BooleanSupplier; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.newBuilder; +import org.junit.jupiter.api.AfterEach; + /* Common infrastructure for tests that check pending operations */ public class PendingOperations { @@ -54,7 +53,7 @@ public class PendingOperations { return newBuilder().proxy(NO_PROXY).build(); } - @AfterMethod + @AfterEach public void cleanup() { // make sure we have a trace both on System.out and System.err // to help with diagnosis. @@ -85,8 +84,7 @@ public class PendingOperations { Support.assertNotDone(future); } - @DataProvider(name = "booleans") - public Object[][] booleans() { + public static Object[][] booleans() { return new Object[][]{{Boolean.TRUE}, {Boolean.FALSE}}; } diff --git a/test/jdk/java/net/httpclient/websocket/PendingPingBinaryClose.java b/test/jdk/java/net/httpclient/websocket/PendingPingBinaryClose.java index c1c314395e8..e07ebe0d03a 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingPingBinaryClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingPingBinaryClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,7 +24,7 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.sendBufferSize=8192 * PendingPingBinaryClose */ @@ -33,21 +33,23 @@ // * -Djdk.internal.httpclient.debug=true // * -Djdk.internal.httpclient.websocket.debug=true -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingPingBinaryClose extends PendingOperations { CompletableFuture cfBinary; CompletableFuture cfPing; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingPingBinaryClose(boolean last) throws Exception { repeatable( () -> { server = Support.notReadingServer(); diff --git a/test/jdk/java/net/httpclient/websocket/PendingPingTextClose.java b/test/jdk/java/net/httpclient/websocket/PendingPingTextClose.java index 82666fafe67..eae01f804a4 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingPingTextClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingPingTextClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,7 +24,7 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.sendBufferSize=8192 * PendingPingTextClose */ @@ -33,14 +33,15 @@ // * -Djdk.internal.httpclient.debug=true // * -Djdk.internal.httpclient.websocket.debug=true -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingPingTextClose extends PendingOperations { static boolean debug = false; // avoid too verbose output @@ -48,7 +49,8 @@ public class PendingPingTextClose extends PendingOperations { CompletableFuture cfPing; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingPingTextClose(boolean last) throws Exception { try { repeatable(() -> { diff --git a/test/jdk/java/net/httpclient/websocket/PendingPongBinaryClose.java b/test/jdk/java/net/httpclient/websocket/PendingPongBinaryClose.java index 0aa2f24c660..32640853b00 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingPongBinaryClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingPongBinaryClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,7 +24,7 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.sendBufferSize=8192 * PendingPongBinaryClose */ @@ -33,21 +33,23 @@ // * -Djdk.internal.httpclient.debug=true // * -Djdk.internal.httpclient.websocket.debug=true -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingPongBinaryClose extends PendingOperations { CompletableFuture cfBinary; CompletableFuture cfPong; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingPongBinaryClose(boolean last) throws Exception { repeatable( () -> { server = Support.notReadingServer(); diff --git a/test/jdk/java/net/httpclient/websocket/PendingPongTextClose.java b/test/jdk/java/net/httpclient/websocket/PendingPongTextClose.java index f919a6706ad..f4d6c84c2f5 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingPongTextClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingPongTextClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,7 +24,7 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.sendBufferSize=8192 * PendingPongTextClose */ @@ -33,21 +33,23 @@ // * -Djdk.internal.httpclient.debug=true // * -Djdk.internal.httpclient.websocket.debug=true -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingPongTextClose extends PendingOperations { CompletableFuture cfText; CompletableFuture cfPong; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingPongTextClose(boolean last) throws Exception { repeatable( () -> { server = Support.notReadingServer(); diff --git a/test/jdk/java/net/httpclient/websocket/PendingTextPingClose.java b/test/jdk/java/net/httpclient/websocket/PendingTextPingClose.java index 39c8bbdc444..76f12430804 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingTextPingClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingTextPingClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,15 +24,13 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.internal.httpclient.websocket.debug=true * -Djdk.httpclient.sendBufferSize=8192 * PendingTextPingClose */ -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -40,13 +38,17 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingTextPingClose extends PendingOperations { CompletableFuture cfText; CompletableFuture cfPing; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingTextPingClose(boolean last) throws Exception { repeatable(() -> { server = Support.notReadingServer(); diff --git a/test/jdk/java/net/httpclient/websocket/PendingTextPongClose.java b/test/jdk/java/net/httpclient/websocket/PendingTextPongClose.java index 8fa90400760..a2a41a73d7b 100644 --- a/test/jdk/java/net/httpclient/websocket/PendingTextPongClose.java +++ b/test/jdk/java/net/httpclient/websocket/PendingTextPongClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,15 +24,13 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.internal.httpclient.websocket.debug=true * -Djdk.httpclient.sendBufferSize=8192 * PendingTextPongClose */ -import org.testng.annotations.Test; - import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -40,13 +38,17 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class PendingTextPongClose extends PendingOperations { CompletableFuture cfText; CompletableFuture cfPong; CompletableFuture cfClose; - @Test(dataProvider = "booleans") + @ParameterizedTest + @MethodSource("booleans") public void pendingTextPongClose(boolean last) throws Exception { repeatable(() -> { server = Support.notReadingServer(); diff --git a/test/jdk/java/net/httpclient/websocket/ReaderDriver.java b/test/jdk/java/net/httpclient/websocket/ReaderDriver.java index 0b874792b4f..3e195be046d 100644 --- a/test/jdk/java/net/httpclient/websocket/ReaderDriver.java +++ b/test/jdk/java/net/httpclient/websocket/ReaderDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,6 +25,8 @@ * @test * @bug 8159053 * @modules java.net.http/jdk.internal.net.http.websocket:open - * @run testng/othervm/timeout=240 --add-reads java.net.http=ALL-UNNAMED java.net.http/jdk.internal.net.http.websocket.ReaderTest + * @run junit/othervm/timeout=240 + * --add-reads java.net.http=ALL-UNNAMED + * java.net.http/jdk.internal.net.http.websocket.ReaderTest */ public final class ReaderDriver { } diff --git a/test/jdk/java/net/httpclient/websocket/SecureSupport.java b/test/jdk/java/net/httpclient/websocket/SecureSupport.java index 8b565768c29..41709d2fedb 100644 --- a/test/jdk/java/net/httpclient/websocket/SecureSupport.java +++ b/test/jdk/java/net/httpclient/websocket/SecureSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 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 @@ -22,18 +22,9 @@ */ import java.io.IOException; -import java.net.Socket; import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.testng.Assert.assertThrows; /** * Helper class to create instances of DummySecureWebSocketServer which diff --git a/test/jdk/java/net/httpclient/websocket/SendTest.java b/test/jdk/java/net/httpclient/websocket/SendTest.java index 39021131156..b3a433b5c29 100644 --- a/test/jdk/java/net/httpclient/websocket/SendTest.java +++ b/test/jdk/java/net/httpclient/websocket/SendTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,22 +24,22 @@ /* * @test * @build DummyWebSocketServer - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.websocket.debug=true * SendTest */ -import org.testng.annotations.Test; - import java.io.IOException; import java.net.http.WebSocket; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.newBuilder; import static java.net.http.WebSocket.NORMAL_CLOSURE; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SendTest { @@ -90,7 +90,7 @@ public class SendTest { try { webSocket.sendClose(NORMAL_CLOSURE, "").join(); assertTrue(webSocket.isOutputClosed()); - assertEquals(webSocket.getSubprotocol(), ""); + assertEquals("", webSocket.getSubprotocol()); webSocket.request(1); // No exceptions must be thrown } finally { webSocket.abort(); diff --git a/test/jdk/java/net/httpclient/websocket/Support.java b/test/jdk/java/net/httpclient/websocket/Support.java index 1323ae35105..73f840ff7eb 100644 --- a/test/jdk/java/net/httpclient/websocket/Support.java +++ b/test/jdk/java/net/httpclient/websocket/Support.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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,8 +32,8 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertFalse; public class Support { diff --git a/test/jdk/java/net/httpclient/websocket/WSHandshakeExceptionTest.java b/test/jdk/java/net/httpclient/websocket/WSHandshakeExceptionTest.java index 90db79dc8ef..f28d84b2f20 100644 --- a/test/jdk/java/net/httpclient/websocket/WSHandshakeExceptionTest.java +++ b/test/jdk/java/net/httpclient/websocket/WSHandshakeExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -29,7 +29,7 @@ * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.TestServerConfigurator * @modules java.net.http/jdk.internal.net.http.common * jdk.httpserver - * @run testng/othervm -Djdk.internal.httpclient.debug=true WSHandshakeExceptionTest + * @run junit/othervm -Djdk.internal.httpclient.debug=true WSHandshakeExceptionTest */ import com.sun.net.httpserver.HttpHandler; @@ -37,8 +37,6 @@ import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsServer; import com.sun.net.httpserver.HttpExchange; - - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -49,10 +47,6 @@ import java.net.http.WebSocketHandshakeException; import jdk.httpclient.test.lib.common.TestServerConfigurator; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import java.net.InetSocketAddress; import java.net.URI; @@ -62,29 +56,33 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; import static java.lang.System.out; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + public class WSHandshakeExceptionTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - HttpServer httpTestServer; // HTTP/1.1 [ 2 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - String httpURI; - String httpsURI; - String httpNonUtf8URI; - String httpsNonUtf8URI; - HttpClient sharedClient; + private static HttpServer httpTestServer; // HTTP/1.1 [ 2 servers ] + private static HttpsServer httpsTestServer; // HTTPS/1.1 + private static String httpURI; + private static String httpsURI; + private static String httpNonUtf8URI; + private static String httpsNonUtf8URI; + private static HttpClient sharedClient; static final int ITERATION_COUNT = 4; // a shared executor helps reduce the amount of threads created by the test static final ExecutorService executor = Executors.newCachedThreadPool(); - @DataProvider(name = "variants") - public Object[][] variants() { + public static Object[][] variants() { return new Object[][]{ { httpURI, false }, { httpsURI, false }, @@ -106,7 +104,8 @@ public class WSHandshakeExceptionTest { .build(); } - @Test(dataProvider = "variants") + @ParameterizedTest + @MethodSource("variants") public void test(String uri, boolean sameClient) { HttpClient client = sharedClient; boolean pause; @@ -132,7 +131,7 @@ public class WSHandshakeExceptionTest { WebSocketHandshakeException wse = (WebSocketHandshakeException) t; assertNotNull(wse.getResponse()); assertNotNull(wse.getResponse().body()); - assertEquals(wse.getResponse().body().getClass(), String.class); + assertEquals(String.class, wse.getResponse().body().getClass()); String body = (String)wse.getResponse().body(); out.println("Status code is " + wse.getResponse().statusCode()); out.println("Response is " + body); @@ -145,7 +144,7 @@ public class WSHandshakeExceptionTest { // default HttpServer 404 body expected assertTrue(body.contains("404")); } - assertEquals(wse.getResponse().statusCode(), 404); + assertEquals(404, wse.getResponse().statusCode()); } } } @@ -159,8 +158,8 @@ public class WSHandshakeExceptionTest { } } - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { // HTTP/1.1 InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); httpTestServer = HttpServer.create(sa, 0); @@ -178,8 +177,8 @@ public class WSHandshakeExceptionTest { httpsTestServer.start(); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { sharedClient = null; gc(100); httpTestServer.stop(0); diff --git a/test/jdk/java/net/httpclient/websocket/WebSocketBuilderTest.java b/test/jdk/java/net/httpclient/websocket/WebSocketBuilderTest.java index b28b8f59875..beef8cb42a4 100644 --- a/test/jdk/java/net/httpclient/websocket/WebSocketBuilderTest.java +++ b/test/jdk/java/net/httpclient/websocket/WebSocketBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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,12 +26,9 @@ * @bug 8159053 * @build DummyWebSocketServer * Support - * @run testng/othervm WebSocketBuilderTest + * @run junit/othervm WebSocketBuilderTest */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.net.URI; import java.net.http.HttpClient; import java.net.http.WebSocket; @@ -42,7 +39,10 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.testng.Assert.assertThrows; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * In some places in this test a new String is created out of a string literal. @@ -98,7 +98,8 @@ public final class WebSocketBuilderTest { .connectTimeout(null)); } - @Test(dataProvider = "badURIs") + @ParameterizedTest + @MethodSource("badURIs") void illegalURI(URI uri) { WebSocket.Builder b = HttpClient.newHttpClient().newWebSocketBuilder(); assertFails(IllegalArgumentException.class, @@ -129,7 +130,8 @@ public final class WebSocketBuilderTest { // TODO: test for bad syntax headers // TODO: test for overwrites (subprotocols) and additions (headers) - @Test(dataProvider = "badSubprotocols") + @ParameterizedTest + @MethodSource("badSubprotocols") public void illegalSubprotocolsSyntax(String s) { WebSocket.Builder b = HttpClient.newHttpClient() .newWebSocketBuilder() @@ -138,7 +140,8 @@ public final class WebSocketBuilderTest { b.buildAsync(VALID_URI, listener())); } - @Test(dataProvider = "duplicatingSubprotocols") + @ParameterizedTest + @MethodSource("duplicatingSubprotocols") public void illegalSubprotocolsDuplicates(String mostPreferred, String[] lesserPreferred) { WebSocket.Builder b = HttpClient.newHttpClient() @@ -148,7 +151,8 @@ public final class WebSocketBuilderTest { b.buildAsync(VALID_URI, listener())); } - @Test(dataProvider = "badConnectTimeouts") + @ParameterizedTest + @MethodSource("badConnectTimeouts") public void illegalConnectTimeout(Duration d) { WebSocket.Builder b = HttpClient.newHttpClient() .newWebSocketBuilder() @@ -157,8 +161,7 @@ public final class WebSocketBuilderTest { b.buildAsync(VALID_URI, listener())); } - @DataProvider - public Object[][] badURIs() { + public static Object[][] badURIs() { return new Object[][]{ {URI.create("http://example.com")}, {URI.create("ftp://example.com")}, @@ -167,8 +170,7 @@ public final class WebSocketBuilderTest { }; } - @DataProvider - public Object[][] badConnectTimeouts() { + public static Object[][] badConnectTimeouts() { return new Object[][]{ {Duration.ofDays(0)}, {Duration.ofDays(-1)}, @@ -188,7 +190,6 @@ public final class WebSocketBuilderTest { // https://tools.ietf.org/html/rfc7230#section-3.2.6 // https://tools.ietf.org/html/rfc20 - @DataProvider public static Object[][] badSubprotocols() { return new Object[][]{ {""}, @@ -215,7 +216,6 @@ public final class WebSocketBuilderTest { }; } - @DataProvider public static Object[][] duplicatingSubprotocols() { return new Object[][]{ {"a.b.c", new String[]{"a.b.c"}}, diff --git a/test/jdk/java/net/httpclient/websocket/WebSocketExtendedTest.java b/test/jdk/java/net/httpclient/websocket/WebSocketExtendedTest.java index 0099104e872..56474555235 100644 --- a/test/jdk/java/net/httpclient/websocket/WebSocketExtendedTest.java +++ b/test/jdk/java/net/httpclient/websocket/WebSocketExtendedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,7 +24,7 @@ /* * @test * @bug 8159053 - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.websocket.debug=true * -Djdk.internal.httpclient.debug=true * -Djdk.httpclient.websocket.writeBufferSize=1024 @@ -32,8 +32,6 @@ */ import jdk.internal.net.http.websocket.Frame; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.net.http.WebSocket; @@ -43,8 +41,11 @@ import java.util.List; import java.util.Random; import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.newBuilder; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /* @@ -52,7 +53,7 @@ import static org.testng.Assert.assertTrue; * possible fragmentation. */ public class WebSocketExtendedTest { -// * run testng/othervm +// * run junit/othervm // * -Djdk.httpclient.websocket.writeBufferSize=16 // * -Djdk.httpclient.sendBufferSize=32 WebSocketTextTest @@ -65,7 +66,8 @@ public class WebSocketExtendedTest { // FIXME ensure subsequent (sendText/Binary, false) only CONTINUATIONs - @Test(dataProvider = "binary") + @ParameterizedTest + @MethodSource("binary") public void binary(ByteBuffer expected) throws IOException, InterruptedException { try (DummyWebSocketServer server = new DummyWebSocketServer()) { server.open(); @@ -76,15 +78,16 @@ public class WebSocketExtendedTest { ws.sendBinary(expected.duplicate(), true).join(); ws.abort(); List frames = server.readFrames(); - assertEquals(frames.size(), 1); + assertEquals(1, frames.size()); DummyWebSocketServer.DecodedFrame f = frames.get(0); assertTrue(f.last()); - assertEquals(f.opcode(), Frame.Opcode.BINARY); - assertEquals(f.data(), expected); + assertEquals(Frame.Opcode.BINARY, f.opcode()); + assertEquals(expected, f.data()); } } - @Test(dataProvider = "pingPong") + @ParameterizedTest + @MethodSource("pingPongSizes") public void ping(ByteBuffer expected) throws Exception { try (DummyWebSocketServer server = new DummyWebSocketServer()) { server.open(); @@ -95,17 +98,18 @@ public class WebSocketExtendedTest { ws.sendPing(expected.duplicate()).join(); ws.abort(); List frames = server.readFrames(); - assertEquals(frames.size(), 1); + assertEquals(1, frames.size()); DummyWebSocketServer.DecodedFrame f = frames.get(0); - assertEquals(f.opcode(), Frame.Opcode.PING); + assertEquals(Frame.Opcode.PING, f.opcode()); ByteBuffer actual = ByteBuffer.allocate(expected.remaining()); actual.put(f.data()); actual.flip(); - assertEquals(actual, expected); + assertEquals(expected, actual); } } - @Test(dataProvider = "pingPong") + @ParameterizedTest + @MethodSource("pingPongSizes") public void pong(ByteBuffer expected) throws Exception { try (DummyWebSocketServer server = new DummyWebSocketServer()) { server.open(); @@ -116,17 +120,18 @@ public class WebSocketExtendedTest { ws.sendPong(expected.duplicate()).join(); ws.abort(); List frames = server.readFrames(); - assertEquals(frames.size(), 1); + assertEquals(1, frames.size()); DummyWebSocketServer.DecodedFrame f = frames.get(0); - assertEquals(f.opcode(), Frame.Opcode.PONG); + assertEquals(Frame.Opcode.PONG, f.opcode()); ByteBuffer actual = ByteBuffer.allocate(expected.remaining()); actual.put(f.data()); actual.flip(); - assertEquals(actual, expected); + assertEquals(expected, actual); } } - @Test(dataProvider = "close") + @ParameterizedTest + @MethodSource("closeArguments") public void close(int statusCode, String reason) throws Exception { try (DummyWebSocketServer server = new DummyWebSocketServer()) { server.open(); @@ -137,18 +142,19 @@ public class WebSocketExtendedTest { ws.sendClose(statusCode, reason).join(); ws.abort(); List frames = server.readFrames(); - assertEquals(frames.size(), 1); + assertEquals(1, frames.size()); DummyWebSocketServer.DecodedFrame f = frames.get(0); - assertEquals(f.opcode(), Frame.Opcode.CLOSE); + assertEquals(Frame.Opcode.CLOSE, f.opcode()); ByteBuffer actual = ByteBuffer.allocate(Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH); actual.put(f.data()); actual.flip(); - assertEquals(actual.getChar(), statusCode); - assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), reason); + assertEquals(statusCode, actual.getChar()); + assertEquals(reason, StandardCharsets.UTF_8.decode(actual).toString()); } } - @Test(dataProvider = "text") + @ParameterizedTest + @MethodSource("texts") public void text(String expected) throws Exception { try (DummyWebSocketServer server = new DummyWebSocketServer()) { server.open(); @@ -163,12 +169,11 @@ public class WebSocketExtendedTest { ByteBuffer actual = ByteBuffer.allocate(maxBytes); frames.stream().forEachOrdered(f -> actual.put(f.data())); actual.flip(); - assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), expected); + assertEquals(expected, StandardCharsets.UTF_8.decode(actual).toString()); } } - @DataProvider(name = "pingPong") - public Object[][] pingPongSizes() { + public static Object[][] pingPongSizes() { return new Object[][]{ {bytes( 0)}, {bytes( 1)}, @@ -177,8 +182,7 @@ public class WebSocketExtendedTest { }; } - @DataProvider(name = "close") - public Object[][] closeArguments() { + public static Object[][] closeArguments() { return new Object[][]{ {WebSocket.NORMAL_CLOSURE, utf8String( 0)}, {WebSocket.NORMAL_CLOSURE, utf8String( 1)}, @@ -216,16 +220,14 @@ public class WebSocketExtendedTest { return str.toString(); } - @DataProvider(name = "text") - public Object[][] texts() { + public static Object[][] texts() { return new Object[][]{ {utf8String( 0)}, {utf8String(1024)}, }; } - @DataProvider(name = "binary") - public Object[][] binary() { + public static Object[][] binary() { return new Object[][]{ {bytes( 0)}, {bytes(1024)}, diff --git a/test/jdk/java/net/httpclient/websocket/WebSocketProxyTest.java b/test/jdk/java/net/httpclient/websocket/WebSocketProxyTest.java index 46758b2fe6e..a410aa9fe75 100644 --- a/test/jdk/java/net/httpclient/websocket/WebSocketProxyTest.java +++ b/test/jdk/java/net/httpclient/websocket/WebSocketProxyTest.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 @@ -28,7 +28,7 @@ * @library /test/lib * @compile SecureSupport.java DummySecureWebSocketServer.java ../ProxyServer.java * @build jdk.test.lib.net.SimpleSSLContext WebSocketProxyTest - * @run testng/othervm + * @run junit/othervm * -Djdk.internal.httpclient.debug=true * -Djdk.internal.httpclient.websocket.debug=true * -Djdk.httpclient.HttpClient.log=errors,requests,headers @@ -61,16 +61,18 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import javax.net.ssl.SSLContext; import static java.net.http.HttpClient.newBuilder; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.FileAssert.fail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class WebSocketProxyTest { @@ -132,8 +134,7 @@ public class WebSocketProxyTest { @Override public String toString() { return "AUTH_TUNNELING_PROXY_SERVER"; } }; - @DataProvider(name = "servers") - public Object[][] servers() { + public static Object[][] servers() { return new Object[][] { { SERVER_WITH_CANNED_DATA, TUNNELING_PROXY_SERVER }, { SERVER_WITH_CANNED_DATA, AUTH_TUNNELING_PROXY_SERVER }, @@ -175,7 +176,8 @@ public class WebSocketProxyTest { return "%s and %s %s".formatted(actual, expected, message); } - @Test(dataProvider = "servers") + @ParameterizedTest + @MethodSource("servers") public void simpleAggregatingBinaryMessages (Function serverSupplier, Supplier proxyServerSupplier) @@ -263,7 +265,7 @@ public class WebSocketProxyTest { .join(); List a = actual.join(); - assertEquals(ofBytes(a), ofBytes(expected), diagnose(a, expected)); + assertEquals(ofBytes(expected), ofBytes(a), diagnose(a, expected)); } } @@ -359,7 +361,7 @@ public class WebSocketProxyTest { } catch (CompletionException expected) { WebSocketHandshakeException e = (WebSocketHandshakeException)expected.getCause(); HttpResponse response = e.getResponse(); - assertEquals(response.statusCode(), 407); + assertEquals(407, response.statusCode()); } } } @@ -398,7 +400,7 @@ public class WebSocketProxyTest { } } - @BeforeMethod + @BeforeEach public void breakBetweenTests() { System.gc(); try {Thread.sleep(100); } catch (InterruptedException x) { /* OK */ } diff --git a/test/jdk/java/net/httpclient/websocket/WebSocketTest.java b/test/jdk/java/net/httpclient/websocket/WebSocketTest.java index 43bcb054b7d..2b1df3d7e87 100644 --- a/test/jdk/java/net/httpclient/websocket/WebSocketTest.java +++ b/test/jdk/java/net/httpclient/websocket/WebSocketTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -27,12 +27,10 @@ * @library ../access * @build DummyWebSocketServer * java.net.http/jdk.internal.net.http.HttpClientTimerAccess - * @run testng/othervm + * @run junit/othervm * WebSocketTest */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.net.Authenticator; @@ -63,9 +61,13 @@ import static java.net.http.HttpClient.newBuilder; import static java.net.http.WebSocket.NORMAL_CLOSURE; import static java.nio.charset.StandardCharsets.UTF_8; import static jdk.internal.net.http.HttpClientTimerAccess.assertNoResponseTimerEventRegistrations; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.fail; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; public class WebSocketTest { @@ -264,8 +266,7 @@ public class WebSocketTest { } } - @DataProvider(name = "sequence") - public Object[][] data1() { + public static Object[][] data1() { int[] CLOSE = { 0x81, 0x00, // "" 0x82, 0x00, // [] @@ -292,7 +293,8 @@ public class WebSocketTest { }; } - @Test(dataProvider = "sequence") + @ParameterizedTest + @MethodSource("data1") public void listenerSequentialOrder(int[] binary, long requestSize) throws IOException { @@ -470,8 +472,7 @@ public class WebSocketTest { @Override public String toString() { return "AUTH_SERVER_WITH_CANNED_DATA"; } }; - @DataProvider(name = "servers") - public Object[][] servers() { + public static Object[][] servers() { return new Object[][] { { SERVER_WITH_CANNED_DATA }, { AUTH_SERVER_WITH_CANNED_DATA }, @@ -507,7 +508,8 @@ public class WebSocketTest { return "%s and %s %s".formatted(actual, expected, message); } - @Test(dataProvider = "servers") + @ParameterizedTest + @MethodSource("servers") public void simpleAggregatingBinaryMessages (Function serverSupplier) throws IOException @@ -600,14 +602,15 @@ public class WebSocketTest { .join(); try { List a = actual.join(); - assertEquals(ofBytes(a), ofBytes(expected), diagnose(a, expected)); + assertEquals(ofBytes(expected), ofBytes(a), diagnose(a, expected)); } finally { webSocket.abort(); } } } - @Test(dataProvider = "servers") + @ParameterizedTest + @MethodSource("servers") public void simpleAggregatingTextMessages (Function serverSupplier) throws IOException @@ -683,7 +686,7 @@ public class WebSocketTest { .join(); try { List a = actual.join(); - assertEquals(a, expected); + assertEquals(expected, a); } finally { webSocket.abort(); } @@ -694,7 +697,8 @@ public class WebSocketTest { * Exercises the scenario where requests for more messages are made prior to * completing the returned CompletionStage instances. */ - @Test(dataProvider = "servers") + @ParameterizedTest + @MethodSource("servers") public void aggregatingTextMessages (Function serverSupplier) throws IOException @@ -784,7 +788,7 @@ public class WebSocketTest { .join(); try { List a = actual.join(); - assertEquals(a, expected); + assertEquals(expected, a); } finally { webSocket.abort(); } @@ -853,7 +857,7 @@ public class WebSocketTest { } catch (CompletionException expected) { WebSocketHandshakeException e = (WebSocketHandshakeException)expected.getCause(); HttpResponse response = e.getResponse(); - assertEquals(response.statusCode(), 401); + assertEquals(401, response.statusCode()); } } } diff --git a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/HeaderWriterTest.java b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/HeaderWriterTest.java index 0e55eaf521f..59a66591c93 100644 --- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/HeaderWriterTest.java +++ b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/HeaderWriterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -23,7 +23,6 @@ package jdk.internal.net.http.websocket; -import org.testng.annotations.Test; import jdk.internal.net.http.websocket.Frame.HeaderWriter; import jdk.internal.net.http.websocket.Frame.Opcode; @@ -32,10 +31,12 @@ import java.util.OptionalInt; import static java.util.OptionalInt.empty; import static java.util.OptionalInt.of; -import static org.testng.Assert.assertEquals; import static jdk.internal.net.http.websocket.TestSupport.assertThrows; import static jdk.internal.net.http.websocket.TestSupport.forEachPermutation; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class HeaderWriterTest { private long cases, frames; @@ -109,7 +110,7 @@ public class HeaderWriterTest { ByteBuffer actual = ByteBuffer.allocate(Frame.MAX_HEADER_SIZE_BYTES + 2); writer.write(actual); actual.flip(); - assertEquals(actual, expected); + assertEquals(expected, actual); }); } } diff --git a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MaskerTest.java b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MaskerTest.java index 7eb892f12ea..cb59b9f7213 100644 --- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MaskerTest.java +++ b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MaskerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -23,17 +23,17 @@ package jdk.internal.net.http.websocket; -import org.testng.annotations.Test; - import java.nio.ByteBuffer; import java.security.SecureRandom; import java.util.stream.IntStream; -import static org.testng.Assert.assertEquals; import static jdk.internal.net.http.websocket.Frame.Masker.applyMask; import static jdk.internal.net.http.websocket.TestSupport.forEachBufferPartition; import static jdk.internal.net.http.websocket.TestSupport.fullCopy; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class MaskerTest { private static final SecureRandom random = new SecureRandom(); @@ -98,14 +98,14 @@ public class MaskerTest { int dstRemaining = dstCopy.remaining(); int masked = Math.min(srcRemaining, dstRemaining); // 1. position check - assertEquals(src.position(), srcCopy.position() + masked); - assertEquals(dst.position(), dstCopy.position() + masked); + assertEquals(srcCopy.position() + masked, src.position()); + assertEquals(dstCopy.position() + masked, dst.position()); // 2. masking check src.position(srcCopy.position()); dst.position(dstCopy.position()); for (; src.hasRemaining() && dst.hasRemaining(); offset = (offset + 1) & 3) { - assertEquals(dst.get(), src.get() ^ maskBytes[offset]); + assertEquals(src.get() ^ maskBytes[offset], dst.get()); } // 3. corruption check // 3.1 src contents haven't changed @@ -113,7 +113,7 @@ public class MaskerTest { int srcLimit = src.limit(); src.clear(); srcCopy.clear(); - assertEquals(src, srcCopy); + assertEquals(srcCopy, src); src.limit(srcLimit).position(srcPosition); // restore src // 3.2 dst leading and trailing regions' contents haven't changed int dstPosition = dst.position(); @@ -122,11 +122,11 @@ public class MaskerTest { // leading dst.position(0).limit(dstInitialPosition); dstCopy.position(0).limit(dstInitialPosition); - assertEquals(dst, dstCopy); + assertEquals(dstCopy, dst); // trailing dst.limit(dst.capacity()).position(dstLimit); dstCopy.limit(dst.capacity()).position(dstLimit); - assertEquals(dst, dstCopy); + assertEquals(dstCopy, dst); // restore dst dst.position(dstPosition).limit(dstLimit); return offset; diff --git a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MessageQueueTest.java b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MessageQueueTest.java index 61503f0e235..73cab76df28 100644 --- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MessageQueueTest.java +++ b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MessageQueueTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,9 +23,6 @@ package jdk.internal.net.http.websocket; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -46,10 +43,14 @@ import java.util.function.BiConsumer; import java.util.function.Supplier; import static jdk.internal.net.http.websocket.MessageQueue.effectiveCapacityOf; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * A unit test for MessageQueue. The test is aware of the details of the queue @@ -59,7 +60,6 @@ public class MessageQueueTest { private static final Random r = new SecureRandom(); - @DataProvider(name = "illegalCapacities") public static Object[][] illegalCapacities() { return new Object[][]{ new Object[]{Integer.MIN_VALUE}, @@ -69,17 +69,20 @@ public class MessageQueueTest { }; } - @Test(dataProvider = "illegalCapacities") + @ParameterizedTest + @MethodSource("illegalCapacities") public void illegalCapacity(int n) { assertThrows(IllegalArgumentException.class, () -> new MessageQueue(n)); } - @Test(dataProvider = "capacities") + @ParameterizedTest + @MethodSource("capacities") public void emptiness(int n) { assertTrue(new MessageQueue(n).isEmpty()); } - @Test(dataProvider = "capacities") + @ParameterizedTest + @MethodSource("capacities") public void fullness(int n) throws IOException { MessageQueue q = new MessageQueue(n); int cap = effectiveCapacityOf(n); @@ -97,7 +100,7 @@ public class MessageQueueTest { for (int i = 0; i < cap; i++) { Message expected = referenceQueue.remove(); Message actual = new Remover().removeFrom(q); - assertEquals(actual, expected); + assertEquals(expected, actual); } } @@ -144,7 +147,8 @@ public class MessageQueueTest { action, future); } - @Test(dataProvider = "capacities") + @ParameterizedTest + @MethodSource("capacities") public void caterpillarWalk(int n) throws IOException { // System.out.println("n: " + n); int cap = effectiveCapacityOf(n); @@ -164,7 +168,7 @@ public class MessageQueueTest { for (int i = 0; i < p; i++) { Message expected = referenceQueue.remove(); Message actual = remover.removeFrom(q); - assertEquals(actual, expected); + assertEquals(expected, actual); } assertTrue(q.isEmpty()); } @@ -243,7 +247,7 @@ public class MessageQueueTest { f.get(); // Just to check for exceptions } consumer.get(); // Waiting for consumer to collect all the messages - assertEquals(actualList.size(), expectedList.size()); + assertEquals(expectedList.size(), actualList.size()); for (Message m : expectedList) { assertTrue(actualList.remove(m)); } @@ -257,7 +261,8 @@ public class MessageQueueTest { } } - @Test(dataProvider = "capacities") + @ParameterizedTest + @MethodSource("capacities") public void testSingleThreaded(int n) throws IOException { Queue referenceQueue = new LinkedList<>(); MessageQueue q = new MessageQueue(n); @@ -271,13 +276,12 @@ public class MessageQueueTest { for (int i = 0; i < cap; i++) { Message expected = referenceQueue.remove(); Message actual = new Remover().removeFrom(q); - assertEquals(actual, expected); + assertEquals(expected, actual); } assertTrue(q.isEmpty()); } - @DataProvider(name = "capacities") - public Object[][] capacities() { + public static Object[][] capacities() { return new Object[][]{ new Object[]{ 1}, new Object[]{ 2}, diff --git a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/ReaderTest.java b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/ReaderTest.java index c53ac88f69c..c7b1ab80fbd 100644 --- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/ReaderTest.java +++ b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/ReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -23,7 +23,6 @@ package jdk.internal.net.http.websocket; -import org.testng.annotations.Test; import jdk.internal.net.http.websocket.Frame.Opcode; import java.nio.ByteBuffer; @@ -35,10 +34,12 @@ import java.util.function.IntUnaryOperator; import static java.util.OptionalInt.empty; import static java.util.OptionalInt.of; -import static org.testng.Assert.assertEquals; import static jdk.internal.net.http.websocket.TestSupport.assertThrows; import static jdk.internal.net.http.websocket.TestSupport.forEachBufferPartition; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class ReaderTest { private long cases, frames; @@ -128,15 +129,15 @@ public class ReaderTest { for (ByteBuffer b : buffers) { r.readFrame(b, c); } - assertEquals(fin, c.fin()); - assertEquals(rsv1, c.rsv1()); - assertEquals(rsv2, c.rsv2()); - assertEquals(rsv3, c.rsv3()); - assertEquals(opcode, c.opcode()); - assertEquals(mask.isPresent(), c.mask()); - assertEquals(payloadLen, c.payloadLen()); - assertEquals(mask, c.maskingKey()); - assertEquals(payloadLen == 0, c.isEndFrame()); + assertEquals(c.fin(), fin); + assertEquals(c.rsv1(), rsv1); + assertEquals(c.rsv2(), rsv2); + assertEquals(c.rsv3(), rsv3); + assertEquals(c.opcode(), opcode); + assertEquals(c.mask(), mask.isPresent()); + assertEquals(c.payloadLen(), payloadLen); + assertEquals(c.maskingKey(), mask); + assertEquals(c.isEndFrame(), payloadLen == 0); }); } diff --git a/test/jdk/java/net/httpclient/websocket/security/WSSanityTest.java b/test/jdk/java/net/httpclient/websocket/security/WSSanityTest.java index 787486dbd84..c8e4faa1ad3 100644 --- a/test/jdk/java/net/httpclient/websocket/security/WSSanityTest.java +++ b/test/jdk/java/net/httpclient/websocket/security/WSSanityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,7 +25,7 @@ * @test * @summary Basic sanity checks for WebSocket URI from the Builder * @compile ../DummyWebSocketServer.java ../../ProxyServer.java - * @run testng/othervm WSSanityTest + * @run junit/othervm WSSanityTest */ import java.io.IOException; @@ -38,20 +38,21 @@ import java.net.URI; import java.util.List; import java.net.http.HttpClient; import java.net.http.WebSocket; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; public class WSSanityTest { - URI wsURI; - DummyWebSocketServer webSocketServer; - InetSocketAddress proxyAddress; + private static URI wsURI; + private static DummyWebSocketServer webSocketServer; + private static InetSocketAddress proxyAddress; - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { ProxyServer proxyServer = new ProxyServer(0, true); proxyAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), proxyServer.getPort()); @@ -63,8 +64,8 @@ public class WSSanityTest { System.out.println("DummyWebSocketServer: " + wsURI); } - @AfterTest - public void teardown() { + @AfterAll + public static void teardown() { webSocketServer.close(); } @@ -75,8 +76,7 @@ public class WSSanityTest { T run() throws Exception; } - @DataProvider(name = "passingScenarios") - public Object[][] passingScenarios() { + public static Object[][] passingScenarios() { HttpClient noProxyClient = HttpClient.newHttpClient(); return new Object[][]{ { (ExceptionAction)() -> { @@ -146,14 +146,15 @@ public class WSSanityTest { HttpClient client = HttpClient.newBuilder().proxy(ps).build(); client.newWebSocketBuilder() .buildAsync(wsURI, noOpListener).get().abort(); - assertEquals(ps.count(), 1); // ps.select only invoked once + assertEquals(1, ps.count()); // ps.select only invoked once return null; }, "7" }, }; } - @Test(dataProvider = "passingScenarios") + @ParameterizedTest + @MethodSource("passingScenarios") public void testScenarios(ExceptionAction action, String dataProviderId) throws Exception { From 0668dab54568a08d49c8c10a7efc4d06ca191083 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Thu, 5 Mar 2026 12:26:37 +0000 Subject: [PATCH 178/636] 8379114: HttpServer path prefix matching incorrectly matches paths that are not slash-prefixed Reviewed-by: dfuchs --- .../sun/net/httpserver/ContextList.java | 51 +++---- .../ContextPathMatcherPathPrefixTest.java | 139 +++++++++++++++--- .../ContextPathMatcherStringPrefixTest.java | 40 ++--- 3 files changed, 162 insertions(+), 68 deletions(-) diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ContextList.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ContextList.java index 6393ca34798..14a07b3a677 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ContextList.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ContextList.java @@ -186,38 +186,31 @@ class ContextList { */ PATH_PREFIX((contextPath, requestPath) -> { - // Fast-path for `/` - if ("/".equals(contextPath)) { + // Does the request path prefix match? + if (!requestPath.startsWith(contextPath)) { + return false; + } + + // Is it an exact match? + int contextPathLength = contextPath.length(); + if (requestPath.length() == contextPathLength) { return true; } - // Does the request path prefix match? - if (requestPath.startsWith(contextPath)) { - - // Is it an exact match? - int contextPathLength = contextPath.length(); - if (requestPath.length() == contextPathLength) { - return true; - } - - // Is it a path-prefix match? - assert contextPathLength > 0; - return - // Case 1: The request path starts with the context - // path, but the context path has an extra path - // separator suffix. For instance, the context path is - // `/foo/` and the request path is `/foo/bar`. - contextPath.charAt(contextPathLength - 1) == '/' || - // Case 2: The request path starts with the - // context path, but the request path has an - // extra path separator suffix. For instance, - // context path is `/foo` and the request path - // is `/foo/` or `/foo/bar`. - requestPath.charAt(contextPathLength) == '/'; - - } - - return false; + // Is it a path-prefix match? + assert contextPathLength > 0; + return + // Case 1: The request path starts with the context + // path, but the context path has an extra path + // separator suffix. For instance, the context path is + // `/foo/` and the request path is `/foo/bar`. + contextPath.charAt(contextPathLength - 1) == '/' || + // Case 2: The request path starts with the + // context path, but the request path has an + // extra path separator suffix. For instance, + // context path is `/foo` and the request path + // is `/foo/` or `/foo/bar`. + requestPath.charAt(contextPathLength) == '/'; }); diff --git a/test/jdk/com/sun/net/httpserver/ContextPathMatcherPathPrefixTest.java b/test/jdk/com/sun/net/httpserver/ContextPathMatcherPathPrefixTest.java index e1cff62a45f..6b260636585 100644 --- a/test/jdk/com/sun/net/httpserver/ContextPathMatcherPathPrefixTest.java +++ b/test/jdk/com/sun/net/httpserver/ContextPathMatcherPathPrefixTest.java @@ -23,21 +23,30 @@ import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; + +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.URI; +import java.net.Socket; import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static java.net.http.HttpClient.Builder.NO_PROXY; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -89,29 +98,88 @@ public class ContextPathMatcherPathPrefixTest { @Test void testContextPathAtRoot() throws Exception { + // Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths try (var infra = new Infra("/")) { - infra.expect(200, "/foo", "/foo/", "/foo/bar", "/foobar"); + // 200 + infra.expect(200, "GET /foo"); + infra.expect(200, "GET /foo/"); + infra.expect(200, "GET /foo/bar"); + infra.expect(200, "GET /foobar"); + infra.expect(200, "DOH /foo"); + infra.expect(200, "DOH /foo/"); + infra.expect(200, "DOH /foo/bar"); + infra.expect(200, "DOH /foobar"); + // 404 + infra.expect(404, "GET foo"); + infra.expect(404, "GET *"); + infra.expect(404, "GET "); + infra.expect(404, "DOH foo"); + infra.expect(404, "DOH *"); + infra.expect(404, "DOH "); + // 400 + infra.expect(400, "GET"); + infra.expect(400, "DOH"); } } @Test void testContextPathAtSubDir() throws Exception { + // Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths try (var infra = new Infra("/foo")) { - infra.expect(200, "/foo", "/foo/", "/foo/bar"); - infra.expect(404, "/foobar"); + // 200 + infra.expect(200, "GET /foo"); + infra.expect(200, "GET /foo/"); + infra.expect(200, "GET /foo/bar"); + infra.expect(200, "DOH /foo"); + infra.expect(200, "DOH /foo/"); + infra.expect(200, "DOH /foo/bar"); + // 404 + infra.expect(404, "GET /foobar"); // Differs from string prefix matching! + infra.expect(404, "GET foo"); + infra.expect(404, "GET *"); + infra.expect(404, "GET "); + infra.expect(404, "DOH /foobar"); // Differs from string prefix matching! + infra.expect(404, "DOH foo"); + infra.expect(404, "DOH *"); + infra.expect(404, "DOH "); + // 400 + infra.expect(400, "GET"); + infra.expect(400, "DOH"); } } @Test void testContextPathAtSubDirWithTrailingSlash() throws Exception { + // Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths try (var infra = new Infra("/foo/")) { - infra.expect(200, "/foo/", "/foo/bar"); - infra.expect(404, "/foo", "/foobar"); + // 200 + infra.expect(200, "GET /foo/"); + infra.expect(200, "GET /foo/bar"); + infra.expect(200, "DOH /foo/"); + infra.expect(200, "DOH /foo/bar"); + // 404 + infra.expect(404, "GET /foo"); + infra.expect(404, "GET /foobar"); + infra.expect(404, "GET foo"); + infra.expect(404, "GET *"); + infra.expect(404, "GET "); + infra.expect(404, "DOH /foo"); + infra.expect(404, "DOH /foobar"); + infra.expect(404, "DOH foo"); + infra.expect(404, "DOH *"); + infra.expect(404, "DOH "); + // 400 + infra.expect(400, "GET"); + infra.expect(400, "DOH"); } } protected static final class Infra implements AutoCloseable { + /** Charset used for network and file I/O. */ + private static final Charset CHARSET = StandardCharsets.US_ASCII; + + /** Socket address the server will bind to. */ private static final InetSocketAddress LO_SA_0 = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); @@ -128,22 +196,51 @@ public class ContextPathMatcherPathPrefixTest { this.contextPath = contextPath; } - protected void expect(int statusCode, String... requestPaths) throws Exception { - for (String requestPath : requestPaths) { - var requestURI = URI.create("http://%s:%s%s".formatted( - server.getAddress().getHostString(), - server.getAddress().getPort(), - requestPath)); - var request = HttpRequest.newBuilder(requestURI).build(); - var response = CLIENT.send(request, HttpResponse.BodyHandlers.discarding()); - assertEquals( - statusCode, response.statusCode(), - "unexpected status code " + Map.of( - "contextPath", contextPath, - "requestPath", requestPath)); + protected void expect(int statusCode, String requestLinePrefix) { + try { + expect0(statusCode, requestLinePrefix); + } catch (Throwable exception) { + var extendedMessage = exception.getMessage() + " " + Map.of( + "contextPath", contextPath, + "requestLinePrefix", requestLinePrefix); + var extendedException = new RuntimeException(extendedMessage); + extendedException.setStackTrace(exception.getStackTrace()); + throw extendedException; } } + private void expect0(int statusCode, String requestLinePrefix) throws IOException { + + // Connect to the server + try (Socket socket = new Socket()) { + socket.connect(server.getAddress()); + + // Obtain the I/O streams + try (OutputStream outputStream = socket.getOutputStream(); + InputStream inputStream = socket.getInputStream(); + BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream, CHARSET))) { + + // Write the request + byte[] requestBytes = String + // `Connection: close` is required for `BufferedReader::readLine` to work. + .format("%s HTTP/1.1\r\nConnection: close\r\n\r\n", requestLinePrefix) + .getBytes(CHARSET); + outputStream.write(requestBytes); + outputStream.flush(); + + // Read the response status code + String statusLine = inputReader.readLine(); + assertNotNull(statusLine, "Unexpected EOF while reading status line"); + Matcher statusLineMatcher = Pattern.compile("^HTTP/1\\.1 (\\d+) .+$").matcher(statusLine); + assertTrue(statusLineMatcher.matches(), "Couldn't match status line: \"" + statusLine + "\""); + assertEquals(statusCode, Integer.parseInt(statusLineMatcher.group(1))); + + } + + } + + } + @Override public void close() { server.stop(0); diff --git a/test/jdk/com/sun/net/httpserver/ContextPathMatcherStringPrefixTest.java b/test/jdk/com/sun/net/httpserver/ContextPathMatcherStringPrefixTest.java index 3f3008a8531..4faa1463e1b 100644 --- a/test/jdk/com/sun/net/httpserver/ContextPathMatcherStringPrefixTest.java +++ b/test/jdk/com/sun/net/httpserver/ContextPathMatcherStringPrefixTest.java @@ -36,28 +36,32 @@ import org.junit.jupiter.api.Test; class ContextPathMatcherStringPrefixTest extends ContextPathMatcherPathPrefixTest { - @Test - @Override - void testContextPathAtRoot() throws Exception { - try (var infra = new Infra("/")) { - infra.expect(200, "/foo", "/foo/", "/foo/bar", "/foobar"); - } - } - @Test @Override void testContextPathAtSubDir() throws Exception { + // Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths try (var infra = new Infra("/foo")) { - infra.expect(200, "/foo", "/foo/", "/foo/bar", "/foobar"); - } - } - - @Test - @Override - void testContextPathAtSubDirWithTrailingSlash() throws Exception { - try (var infra = new Infra("/foo/")) { - infra.expect(200, "/foo/", "/foo/bar"); - infra.expect(404, "/foo", "/foobar"); + // 200 + infra.expect(200, "GET /foo"); + infra.expect(200, "GET /foo/"); + infra.expect(200, "GET /foo/bar"); + infra.expect(200, "GET /foobar"); // Differs from path prefix matching! + infra.expect(200, "DOH /foo"); + infra.expect(200, "DOH /foo/"); + infra.expect(200, "DOH /foo/bar"); + infra.expect(200, "DOH /foobar"); // Differs from path prefix matching! + // 404 + infra.expect(404, "GET /"); + infra.expect(404, "GET foo"); + infra.expect(404, "GET *"); + infra.expect(404, "GET "); + infra.expect(404, "DOH /"); + infra.expect(404, "DOH foo"); + infra.expect(404, "DOH *"); + infra.expect(404, "DOH "); + // 400 + infra.expect(400, "GET"); + infra.expect(400, "DOH"); } } From 4d9d2c352b9daa8bdecea5303f49f2496c127115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Thu, 5 Mar 2026 14:15:08 +0000 Subject: [PATCH 179/636] 8284315: DocTrees.getElement is inconsistent with Elements.getTypeElement Reviewed-by: jlahoda, vromero --- .../classes/com/sun/source/util/DocTrees.java | 25 +-- .../com/sun/tools/javac/api/JavacTrees.java | 142 +++++++++--------- .../doclets/toolkit/util/CommentHelper.java | 7 +- .../jdk/javadoc/internal/doclint/Checker.java | 12 +- .../javadoc/doclet/testSeeTag/TestSeeTag.java | 10 +- .../tools/doclint/ReferenceTest.java | 2 +- .../langtools/tools/doclint/ReferenceTest.out | 14 +- .../tools/javac/doctree/ReferenceTest.java | 78 ++++++++-- 8 files changed, 184 insertions(+), 106 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/DocTrees.java b/src/jdk.compiler/share/classes/com/sun/source/util/DocTrees.java index 45a452bd0dd..44d9bd89917 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/DocTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocTrees.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -195,23 +195,30 @@ public abstract class DocTrees extends Trees { /** * Returns the language model element referred to by the leaf node of the given - * {@link DocTreePath}, or {@code null} if unknown. + * {@link DocTreePath}, or {@code null} if the leaf node of {@code path} does + * not refer to an element. + * * @param path the path for the tree node - * @return the element + * @return the referenced element, or null + * @see #getType(DocTreePath) */ public abstract Element getElement(DocTreePath path); /** * Returns the language model type referred to by the leaf node of the given - * {@link DocTreePath}, or {@code null} if unknown. This method usually - * returns the same value as {@code getElement(path).asType()} for a - * {@code path} argument for which {@link #getElement(DocTreePath)} returns - * a non-null value, but may return a type that includes additional - * information, such as a parameterized generic type instead of a raw type. + * {@link DocTreePath}, or {@code null} if the leaf node of {@code path} does + * not refer to a type. + * + *

If {@link #getElement(DocTreePath)} returns a non-null value for a given {@code path} + * argument, this method usally returns the same value as {@code getElement(path).asType()}. + * However, there are cases where the returned type includes additional information, + * such as a parameterized generic type instead of a raw type. In other cases, such as with + * primitive or array types, the returned type may not have a corresponding element returned + * by {@code getElement(DocTreePath)}.

* * @param path the path for the tree node * @return the referenced type, or null - * + * @see #getElement(DocTreePath) * @since 15 */ public abstract TypeMirror getType(DocTreePath path); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 6bc5d358b6f..ecd7f5b101a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.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 @@ -354,22 +354,28 @@ public class JavacTrees extends DocTrees { DocTree tree = path.getLeaf(); if (tree instanceof DCReference dcReference) { JCTree qexpr = dcReference.qualifierExpression; - if (qexpr != null) { + + // Forward references with explicit module name to getElement + if (qexpr != null && dcReference.moduleName == null) { + + Env env = getAttrContext(path.getTreePath()); Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); + try { - Env env = getAttrContext(path.getTreePath()); - JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); - try { - Type t = attr.attribType(dcReference.qualifierExpression, env); - if (t != null && !t.isErroneous()) { + Type t = attr.attribType(dcReference.qualifierExpression, env); + if (t != null && !t.isErroneous()) { + if (dcReference.memberName != null) { + Symbol sym = resolveMember(t, (Name) dcReference.memberName, dcReference, env); + return sym == null ? null : sym.type; + } else { return t; } - } finally { - log.useSource(prevSource); } } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; } finally { + log.useSource(prevSource); log.popDiagnosticHandler(deferredDiagnosticHandler); } } @@ -426,14 +432,12 @@ public class JavacTrees extends DocTrees { memberName = (Name) ref.memberName; } else { // Check if qualifierExpression is a type or package, using the methods javac provides. - // If no module name is given we check if qualifierExpression identifies a type. - // If that fails or we have a module name, use that to resolve qualifierExpression to - // a package or type. - Type t = ref.moduleName == null ? attr.attribType(ref.qualifierExpression, env) : null; + Type t = attr.attribType(ref.qualifierExpression, env); - if (t == null || t.isErroneous()) { - JCCompilationUnit toplevel = - treeMaker.TopLevel(List.nil()); + if (t == null || t.isErroneous() || + (ref.moduleName != null && !mdlsym.equals(elements.getModuleOf(t.asElement())))) { + + JCCompilationUnit toplevel = treeMaker.TopLevel(List.nil()); toplevel.modle = mdlsym; toplevel.packge = mdlsym.unnamedPackage; Symbol sym = attr.attribIdent(ref.qualifierExpression, toplevel); @@ -447,10 +451,6 @@ public class JavacTrees extends DocTrees { if ((sym.kind == PCK || sym.kind == TYP) && sym.exists()) { tsym = (TypeSymbol) sym; memberName = (Name) ref.memberName; - if (sym.kind == PCK && memberName != null) { - //cannot refer to a package "member" - return null; - } } else { if (modules.modulesInitialized() && ref.moduleName == null && ref.memberName == null) { // package/type does not exist, check if there is a matching module @@ -470,64 +470,22 @@ public class JavacTrees extends DocTrees { } } } else { - Type e = t; - // If this is an array type convert to element type - while (e instanceof ArrayType arrayType) - e = arrayType.elemtype; - tsym = e.tsym; + tsym = switch (t.getKind()) { + case DECLARED, TYPEVAR, PACKAGE, MODULE -> t.tsym; + default -> null; + }; memberName = (Name) ref.memberName; } } if (memberName == null) { return tsym; - } else if (tsym == null || tsym.getKind() == ElementKind.PACKAGE || tsym.getKind() == ElementKind.MODULE) { - return null; // Non-null member name in non-class context - } - - if (tsym.type.isPrimitive()) { + } else if (tsym == null) { return null; } - final List paramTypes; - if (ref.paramTypes == null) - paramTypes = null; - else { - ListBuffer lb = new ListBuffer<>(); - for (List l = (List) ref.paramTypes; l.nonEmpty(); l = l.tail) { - JCTree tree = l.head; - Type t = attr.attribType(tree, env); - lb.add(t); - } - paramTypes = lb.toList(); - } + return resolveMember(tsym.type, memberName, ref, env); - ClassSymbol sym = (ClassSymbol) types.skipTypeVars(tsym.type, false).tsym; - boolean explicitType = ref.qualifierExpression != null; - Symbol msym = (memberName == sym.name) - ? findConstructor(sym, paramTypes, true) - : findMethod(sym, memberName, paramTypes, true, explicitType); - - if (msym == null) { - msym = (memberName == sym.name) - ? findConstructor(sym, paramTypes, false) - : findMethod(sym, memberName, paramTypes, false, explicitType); - } - - if (paramTypes != null) { - // explicit (possibly empty) arg list given, so cannot be a field - return msym; - } - - VarSymbol vsym = (ref.paramTypes != null) ? null : findField(sym, memberName, explicitType); - // prefer a field over a method with no parameters - if (vsym != null && - (msym == null || - types.isSubtypeUnchecked(vsym.enclClass().asType(), msym.enclClass().asType()))) { - return vsym; - } else { - return msym; - } } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; } finally { @@ -536,6 +494,54 @@ public class JavacTrees extends DocTrees { } } + private Symbol resolveMember(Type type, Name memberName, DCReference ref, Env env) { + + if (type.isPrimitive() || type.getKind() == TypeKind.PACKAGE || type.getKind() == TypeKind.MODULE) { + return null; + } + + final List paramTypes; + if (ref.paramTypes == null) + paramTypes = null; + else { + ListBuffer lb = new ListBuffer<>(); + for (List l = (List) ref.paramTypes; l.nonEmpty(); l = l.tail) { + JCTree tree = l.head; + Type t = attr.attribType(tree, env); + lb.add(t); + } + paramTypes = lb.toList(); + } + + // skipTypeVars conversion below is needed if type is itself a type variable + ClassSymbol sym = (ClassSymbol) types.skipTypeVars(type, false).tsym; + boolean explicitType = ref.qualifierExpression != null; + Symbol msym = (memberName == sym.name) + ? findConstructor(sym, paramTypes, true) + : findMethod(sym, memberName, paramTypes, true, explicitType); + + if (msym == null) { + msym = (memberName == sym.name) + ? findConstructor(sym, paramTypes, false) + : findMethod(sym, memberName, paramTypes, false, explicitType); + } + + if (paramTypes != null) { + // explicit (possibly empty) arg list given, so cannot be a field + return msym; + } + + VarSymbol vsym = (ref.paramTypes != null) ? null : findField(sym, memberName, explicitType); + // prefer a field over a method with no parameters + if (vsym != null && + (msym == null || + types.isSubtypeUnchecked(vsym.enclClass().asType(), msym.enclClass().asType()))) { + return vsym; + } else { + return msym; + } + } + private Symbol attributeParamIdentifier(TreePath path, DCParam paramTag) { Symbol javadocSymbol = getElement(path); if (javadocSymbol == null) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java index 30aa86aea71..3d300b77073 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -136,10 +136,7 @@ public class CommentHelper { return null; } DocTrees doctrees = configuration.docEnv.getDocTrees(); - // Workaround for JDK-8284193 - // DocTrees.getElement(DocTreePath) returns javac-internal Symbols - var e = doctrees.getElement(docTreePath); - return e == null || e.getKind() == ElementKind.CLASS && e.asType().getKind() != TypeKind.DECLARED ? null : e; + return doctrees.getElement(docTreePath); } public TypeMirror getType(ReferenceTree rtree) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java index b5399d0fef9..90b371a8a15 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1006,11 +1006,11 @@ public class Checker extends DocTreePathScanner { public Void visitReference(ReferenceTree tree, Void ignore) { // Exclude same-file anchor links from reference checks if (!tree.getSignature().startsWith("##")) { - Element e = env.trees.getElement(getCurrentPath()); - if (e == null) { - reportBadReference(tree); - } else if ((inLink || inSee) - && e.getKind() == ElementKind.CLASS && e.asType().getKind() != TypeKind.DECLARED) { + if (inLink || inSee) { + if (env.trees.getElement(getCurrentPath()) == null) { + reportBadReference(tree); + } + } else if (env.trees.getType(getCurrentPath()) == null) { reportBadReference(tree); } } diff --git a/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java index f3294f6647c..57854396cf4 100644 --- a/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java +++ b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -24,6 +24,7 @@ /* * @test * @bug 8017191 8182765 8200432 8239804 8250766 8262992 8281944 8307377 + * 8284315 * @summary Javadoc is confused by at-link to imported classes outside of the set of generated packages * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -103,7 +104,12 @@ public class TestSeeTag extends JavadocTester {
See Also: